diff options
Diffstat (limited to 'lib/compiler')
205 files changed, 16116 insertions, 11589 deletions
diff --git a/lib/compiler/Makefile b/lib/compiler/Makefile index 1ad883b014..b8b2f562a2 100644 --- a/lib/compiler/Makefile +++ b/lib/compiler/Makefile @@ -1,18 +1,19 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # -# The contents of this file are subject to the Erlang Public License, -# Version 1.1, (the "License"); you may not use this file except in -# compliance with the License. You should have received a copy of the -# Erlang Public License along with this software. If not, it can be -# retrieved online at http://www.erlang.org/. -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -# the License for the specific language governing rights and limitations -# under the License. +# 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% # diff --git a/lib/compiler/doc/src/Makefile b/lib/compiler/doc/src/Makefile index 13a73a5d31..c6864cb835 100644 --- a/lib/compiler/doc/src/Makefile +++ b/lib/compiler/doc/src/Makefile @@ -1,18 +1,19 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2012. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # -# The contents of this file are subject to the Erlang Public License, -# Version 1.1, (the "License"); you may not use this file except in -# compliance with the License. You should have received a copy of the -# Erlang Public License along with this software. If not, it can be -# retrieved online at http://www.erlang.org/. -# -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -# the License for the specific language governing rights and limitations -# under the License. +# 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% # diff --git a/lib/compiler/doc/src/book.xml b/lib/compiler/doc/src/book.xml index 45b49fe46d..af6b4cf47a 100644 --- a/lib/compiler/doc/src/book.xml +++ b/lib/compiler/doc/src/book.xml @@ -4,20 +4,21 @@ <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header titlestyle="normal"> <copyright> - <year>1997</year><year>2013</year> + <year>1997</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. + 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. </legalnotice> diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 5fccdcdcb5..3ce37b98e9 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -4,20 +4,21 @@ <erlref> <header> <copyright> - <year>1996</year><year>2014</year> + <year>1996</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. + 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. </legalnotice> @@ -32,15 +33,28 @@ <modulesummary>Erlang Compiler</modulesummary> <description> <p>This module provides an interface to the standard Erlang - compiler. It can generate either a new file which contains - the object code, or return a binary which can be loaded directly. + compiler. It can generate either a new file, which contains + the object code, or return a binary, which can be loaded directly. </p> </description> <funcs> <func> + <name>env_compiler_options()</name> + <fsummary> + Compiler options defined via the environment variable + <c>ERL_COMPILER_OPTIONS</c> + </fsummary> + <desc> + <p>Return compiler options given via the environment variable + <c>ERL_COMPILER_OPTIONS</c>. If the value is a list, it is + returned as is. If it is not a list, it is put into a list. + </p> + </desc> + </func> + <func> <name>file(File)</name> - <fsummary>Compile a file</fsummary> + <fsummary>Compiles a file.</fsummary> <desc> <p>Is the same as <c>file(File, [verbose,report_errors,report_warnings])</c>. @@ -50,7 +64,7 @@ <func> <name>file(File, Options) -> CompRet</name> - <fsummary>Compile a file</fsummary> + <fsummary>Compiles a file.</fsummary> <type> <v>CompRet = ModRet | BinRet | ErrRet</v> <v>ModRet = {ok,ModuleName} | {ok,ModuleName,Warnings}</v> @@ -64,39 +78,38 @@ <p>Returns <c>{ok,ModuleName}</c> if successful, or <c>error</c> if there are errors. An object code file is created if - the compilation succeeds with no errors. It is considered + the compilation succeeds without errors. It is considered to be an error if the module name in the source code is not the same as the basename of the output file.</p> - <p><marker id="type-option"/>Here follows first all elements of <c>Options</c> that in - some way control the behavior of the compiler.</p> + <p><marker id="type-option"/>Available options:</p> <taglist> <tag><c>basic_validation</c></tag> <item> - <p>This option is fast way to test whether a module will - compile successfully (mainly useful for code generators - that want to verify the code they emit). No code will + <p>This option is a fast way to test whether a module will + compile successfully. This is useful for code generators + that want to verify the code that they emit. No code is generated. If warnings are enabled, warnings generated by the <c>erl_lint</c> module (such as warnings for unused - variables and functions) will be returned too.</p> + variables and functions) are also returned.</p> - <p>Use the <c>strong_validation</c> option to generate all + <p>Use option <c>strong_validation</c> to generate all warnings that the compiler would generate.</p> </item> <tag><c>strong_validation</c></tag> <item> - <p>Similar to the <c>basic_validation</c> option, no code - will be generated, but more compiler passes will be run - to ensure also warnings generated by the optimization - passes are generated (such as clauses that will not match + <p>Similar to option <c>basic_validation</c>. No code + is generated, but more compiler passes are run + to ensure that warnings generated by the optimization + passes are generated (such as clauses that will not match, or expressions that are guaranteed to fail with an - exception at run-time).</p> + exception at runtime).</p> </item> <tag><c>binary</c></tag> <item> - <p>Causes the compiler to return the object code in a + <p>The compiler returns the object code in a binary instead of creating an object file. If successful, the compiler returns <c>{ok,ModuleName,Binary}</c>.</p> </item> @@ -105,7 +118,9 @@ <item> <p>The compiler will emit informational warnings about binary matching optimizations (both successful and unsuccessful). - See the <em>Efficiency Guide</em> for further information.</p> + For more information, see the section about + <seealso marker="doc/efficiency_guide:binaryhandling#bin_opt_info">bin_opt_info</seealso> + in the Efficiency Guide.</p> </item> <tag><c>compressed</c></tag> @@ -117,20 +132,19 @@ <tag><c>debug_info</c></tag> <item> <marker id="debug_info"></marker> - <p>Include debug information in the form of abstract code + <p>Includes debug information in the form of abstract code (see <seealso marker="erts:absform">The Abstract Format</seealso> in ERTS User's Guide) in the compiled beam module. Tools - such as Debugger, Xref and Cover require the debug - information to be included.</p> + such as Debugger, Xref, and Cover require + the debug information to be included.</p> <p><em>Warning</em>: Source code can be reconstructed from the debug information. Use encrypted debug information - (see below) to prevent this.</p> + (<c>encrypt_debug_info</c>) to prevent this.</p> - <p>See - <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso> - for details.</p> + <p>For details, see + <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p> </item> <tag><c>{debug_info_key,KeyString}</c></tag> @@ -138,65 +152,61 @@ <tag><c>{debug_info_key,{Mode,KeyString}}</c></tag> <item> <marker id="debug_info_key"></marker> - <p>Include debug information, but encrypt it, so that it + <p>Includes debug information, but encrypts it so that it cannot be accessed without supplying the key. (To give - the <c>debug_info</c> option as well is allowed, but is + option <c>debug_info</c> as well is allowed, but not necessary.) Using this option is a good way to always have the debug information available during testing, yet - protect the source code.</p> + protecting the source code.</p> <p><c>Mode</c> is the type of crypto algorithm to be used - for encrypting the debug information. The default type -- - and currently the only type -- is <c>des3_cbc</c>.</p> - <p>See - <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso> - for details.</p> + for encrypting the debug information. The default + (and currently the only) type is <c>des3_cbc</c>.</p> + <p>For details, see + <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p> </item> <tag><c>encrypt_debug_info</c></tag> <item> <marker id="encrypt_debug_info"></marker> - <p>Like the <c>debug_info_key</c> option above, except that - the key will be read from an <c>.erlang.crypt</c> file. + <p>Similar to the <c>debug_info_key</c> option, but + the key is read from an <c>.erlang.crypt</c> file. </p> - <p>See - <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso> - for details.</p> + <p>For details, see + <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p> </item> <tag><c>makedep</c></tag> <item> - <p>Produce a Makefile rule to track headers dependencies. + <p>Produces a Makefile rule to track headers dependencies. No object file is produced. </p> <p>By default, this rule is written to - <c><![CDATA[<File>.Pbeam]]></c>. However, if the option + <c><![CDATA[<File>.Pbeam]]></c>. However, if option <c>binary</c> is set, nothing is written and the rule is returned in <c>Binary</c>. </p> - <p>For instance, if one has the following module: + <p>For example, if you have the following module: </p> <code> -module(module). -include_lib("eunit/include/eunit.hrl"). --include("header.hrl"). - </code> - <p>Here is the Makefile rule generated by this option: +-include("header.hrl").</code> + <p>The Makefile rule generated by this option looks as follows: </p> <code> module.beam: module.erl \ /usr/local/lib/erlang/lib/eunit/include/eunit.hrl \ - header.hrl - </code> + header.hrl</code> </item> <tag><c>{makedep_output, Output}</c></tag> <item> - <p>Write generated rule(s) to <c>Output</c> instead of the + <p>Writes generated rules to <c>Output</c> instead of the default <c><![CDATA[<File>.Pbeam]]></c>. <c>Output</c> can be a filename or an <c>io_device()</c>. To write to - stdout, use <c>standard_io</c>. However if <c>binary</c> + stdout, use <c>standard_io</c>. However, if <c>binary</c> is set, nothing is written to <c>Output</c> and the result is returned to the caller with <c>{ok, ModuleName, Binary}</c>. @@ -205,7 +215,7 @@ module.beam: module.erl \ <tag><c>{makedep_target, Target}</c></tag> <item> - <p>Change the name of the rule emitted to <c>Target</c>. + <p>Changes the name of the rule emitted to <c>Target</c>. </p> </item> @@ -217,20 +227,20 @@ module.beam: module.erl \ <tag><c>makedep_add_missing</c></tag> <item> - <p>Consider missing headers as generated files and add them to the + <p>Considers missing headers as generated files and adds them to the dependencies. </p> </item> <tag><c>makedep_phony</c></tag> <item> - <p>Add a phony target for each dependency. + <p>Adds a phony target for each dependency. </p> </item> <tag><c>'P'</c></tag> <item> - <p>Produces a listing of the parsed code after preprocessing + <p>Produces a listing of the parsed code, after preprocessing and parse transforms, in the file <c><![CDATA[<File>.P]]></c>. No object file is produced. </p> @@ -238,7 +248,7 @@ module.beam: module.erl \ <tag><c>'E'</c></tag> <item> - <p>Produces a listing of the code after all source code + <p>Produces a listing of the code, after all source code transformations have been performed, in the file <c><![CDATA[<File>.E]]></c>. No object file is produced. </p> @@ -258,21 +268,21 @@ module.beam: module.erl \ <tag><c>report</c></tag> <item> - <p>This is a short form for both <c>report_errors</c> and + <p>A short form for both <c>report_errors</c> and <c>report_warnings</c>.</p> </item> <tag><c>return_errors</c></tag> <item> - <p>If this flag is set, then + <p>If this flag is set, <c>{error,ErrorList,WarningList}</c> is returned when there are errors.</p> </item> <tag><c>return_warnings</c></tag> <item> - <p>If this flag is set, then an extra field containing - <c>WarningList</c> is added to the tuples returned on + <p>If this flag is set, an extra field, containing + <c>WarningList</c>, is added to the tuples returned on success.</p> </item> @@ -284,13 +294,13 @@ module.beam: module.erl \ <tag><c>return</c></tag> <item> - <p>This is a short form for both <c>return_errors</c> and + <p>A short form for both <c>return_errors</c> and <c>return_warnings</c>.</p> </item> <tag><c>verbose</c></tag> <item> - <p>Causes more verbose information from the compiler + <p>Causes more verbose information from the compiler, describing what it is doing.</p> </item> @@ -314,7 +324,7 @@ module.beam: module.erl \ <tag><c>{i,Dir}</c></tag> <item> - <p>Add <c>Dir</c> to the list of directories to be searched + <p>Adds <c>Dir</c> to the list of directories to be searched when including a file. When encountering an <c>-include</c> or <c>-include_lib</c> directive, the compiler searches for header files in the following @@ -322,14 +332,14 @@ module.beam: module.erl \ <list type="ordered"> <item> <p><c>"."</c>, the current working directory of - the file server;</p> + the file server</p> </item> <item> - <p>the base name of the compiled file;</p> + <p>The base name of the compiled file</p> </item> <item> - <p>the directories specified using the <c>i</c> option. - The directory specified last is searched first.</p> + <p>The directories specified using option <c>i</c>; + the directory specified last is searched first</p> </item> </list> </item> @@ -353,15 +363,15 @@ module.beam: module.erl \ <tag><c>from_asm</c></tag> <item> <p>The input file is expected to be assembler code (default - file suffix ".S"). Note that the format of assembler files - is not documented, and may change between releases.</p> + file suffix ".S"). Notice that the format of assembler files + is not documented, and can change between releases.</p> </item> <tag><c>from_core</c></tag> <item> <p>The input file is expected to be core code (default - file suffix ".core"). Note that the format of core files - is not documented, and may change between releases.</p> + file suffix ".core"). Notice that the format of core files + is not documented, and can change between releases.</p> </item> <tag><c>no_strict_record_tests</c></tag> @@ -369,9 +379,9 @@ module.beam: module.erl \ <p>This option is not recommended.</p> <p>By default, the generated code for - the <c>Record#record_tag.field</c> operation verifies that - the tuple <c>Record</c> is of the correct size for - the record and that the first element is the tag + operation <c>Record#record_tag.field</c> verifies that + the tuple <c>Record</c> has the correct size for + the record, and that the first element is the tag <c>record_tag</c>. Use this option to omit the verification code.</p> </item> @@ -390,79 +400,87 @@ module.beam: module.erl \ <tag><c>{no_auto_import,[{F,A}, ...]}</c></tag> <item> <p>Makes the function <c>F/A</c> no longer being - auto-imported from the module <c>erlang</c>, which resolves - BIF name clashes. This option has to be used to resolve name - clashes with BIFs auto-imported before R14A, if one wants to + auto-imported from the <c>erlang</c> module, which resolves + BIF name clashes. This option must be used to resolve name + clashes with BIFs auto-imported before R14A, if it is needed to call the local function with the same name as an auto-imported BIF without module prefix.</p> <note> - <p>From R14A and forward, the compiler resolves calls + <p>As from R14A and forward, the compiler resolves calls without module prefix to local or imported functions before - trying auto-imported BIFs. If the BIF is to be + trying with auto-imported BIFs. If the BIF is to be called, use the <c>erlang</c> module prefix in the call, not - <c>{ no_auto_import,[{F,A}, ...]}</c></p> + <c>{ no_auto_import,[{F,A}, ...]}</c>.</p> </note> <p>If this option is written in the source code, as a <c>-compile</c> directive, the syntax <c>F/A</c> can be used instead - of <c>{F,A}</c>. Example:</p> + of <c>{F,A}</c>, for example:</p> <code>-compile({no_auto_import,[error/1]}).</code> </item> <tag><c>no_auto_import</c></tag> <item> - <p>Do not auto import any functions from the module <c>erlang</c>.</p> + <p>Do not auto-import any functions from <c>erlang</c> module.</p> </item> <tag><c>no_line_info</c></tag> <item> - <p>Omit line number information in order to produce a slightly + <p>Omits line number information to produce a slightly smaller output file. </p> </item> </taglist> - <p>If warnings are turned on (the <c>report_warnings</c> option - described above), the following options control what type of - warnings that will be generated. + <p>If warnings are turned on (option <c>report_warnings</c> + described earlier), the following options control what type of + warnings that are generated. <marker id="erl_lint_options"></marker> - With the exception of <c>{warn_format,Verbosity}</c> all - options below have two forms; one <c>warn_xxx</c> form to - turn on the warning and one <c>nowarn_xxx</c> form to turn off - the warning. In the description that follows, the form that - is used to change the default value is listed.</p> + Except from <c>{warn_format,Verbosity}</c>, the following options + have two forms:</p> + <list type="bulleted"> + <item>A <c>warn_xxx</c> form, to turn on the warning.</item> + <item>A <c>nowarn_xxx</c> form, to turn off the warning.</item> + </list> + <p>In the descriptions that follow, the form that is used to change + the default value are listed.</p> <taglist> <tag><c>{warn_format, Verbosity}</c></tag> <item> <p>Causes warnings to be emitted for malformed format strings as arguments to <c>io:format</c> and similar - functions. <c>Verbosity</c> selects the amount of - warnings: 0 = no warnings; 1 = warnings for invalid - format strings and incorrect number of arguments; 2 = - warnings also when the validity could not be checked - (for example, when the format string argument is a - variable). The default verbosity is 1. Verbosity 0 can - also be selected by the option <c>nowarn_format</c>.</p> + functions.</p> + <p><c>Verbosity</c> selects the number of warnings:</p> + <list type="bulleted"> + <item><c>0</c> = No warnings</item> + <item><c>1</c> = Warnings for invalid format strings and incorrect + number of arguments</item> + <item><c>2</c> = Warnings also when the validity cannot + be checked, for example, when the format string argument is a + variable.</item> + </list> + <p>The default verbosity is <c>1</c>. Verbosity <c>0</c> can + also be selected by option <c>nowarn_format</c>.</p> </item> <tag><c>nowarn_bif_clash</c></tag> <item> - <p>This option is removed, it will generate a fatal error if used.</p> + <p>This option is removed, it generates a fatal error if used.</p> <warning> - <p>Beginning with R14A, the compiler no longer calls the + <p>As from beginning with R14A, the compiler no longer calls the auto-imported BIF if the name clashes with a local or - explicitly imported function and a call without explicit - module name is issued. Instead the local or imported - function is called. Still accepting <c>nowarn_bif_clash</c> would makes a - module calling functions clashing with autoimported BIFs + explicitly imported function, and a call without explicit + module name is issued. Instead, the local or imported + function is called. Still accepting <c>nowarn_bif_clash</c> would + make a module calling functions clashing with auto-imported BIFs compile with both the old and new compilers, but with - completely different semantics, why the option was removed.</p> + completely different semantics. This is why the option is removed.</p> - <p>The use of this option has always been strongly discouraged. - From OTP R14A and forward it's an error to use it.</p> + <p>The use of this option has always been discouraged. + As from R14A, it is an error to use it.</p> <p>To resolve BIF clashes, use explicit module names or the <c>{no_auto_import,[F/A]}</c> compiler directive.</p> </warning> @@ -470,11 +488,11 @@ module.beam: module.erl \ <tag><c>{nowarn_bif_clash, FAs}</c></tag> <item> - <p>This option is removed, it will generate a fatal error if used.</p> + <p>This option is removed, it generates a fatal error if used.</p> <warning> - <p>The use of this option has always been strongly discouraged. - From OTP R14A and forward it's an error to use it.</p> + <p>The use of this option has always been discouraged. + As from R14A, it is an error to use it.</p> <p>To resolve BIF clashes, use explicit module names or the <c>{no_auto_import,[F/A]}</c> compiler directive.</p> </warning> @@ -482,35 +500,29 @@ module.beam: module.erl \ <tag><c>warn_export_all</c></tag> <item> - <p>Causes a warning to be emitted if the <c>export_all</c> - option has also been given.</p> + <p>Emits a warning if option <c>export_all</c> is also given.</p> </item> <tag><c>warn_export_vars</c></tag> <item> - <p>Causes warnings to be emitted for all implicitly - exported variables referred to after the primitives - where they were first defined. No warnings for exported - variables unless they are referred to in some pattern, - which is the default, can be selected by the option - <c>nowarn_export_vars</c>.</p> + <p>Emits warnings for all implicitly exported variables + referred to after the primitives where they were first defined. + By default, the compiler only emits warnings for exported + variables referred to in a pattern.</p> </item> - <tag><c>warn_shadow_vars</c></tag> + <tag><c>nowarn_shadow_vars</c></tag> <item> - <p>Causes warnings to be emitted for "fresh" variables - in functional objects or list comprehensions with the same - name as some already defined variable. The default is to - warn for such variables. No warnings for shadowed - variables can be selected by the option - <c>nowarn_shadow_vars</c>.</p> + <p>Turns off warnings for "fresh" variables + in functional objects or list comprehensions with the same + name as some already defined variable. Default is to + emit warnings for such variables.</p> </item> <tag><c>nowarn_unused_function</c></tag> <item> - <p>Turns off warnings for unused local functions. - By default (<c>warn_unused_function</c>), warnings are - emitted for all local functions that are not called + <p>Turns off warnings for unused local functions. Default + is to emit warnings for all local functions that are not called directly or indirectly by an exported function. The compiler does not include unused local functions in the generated beam file, but the warning is still useful @@ -519,148 +531,142 @@ module.beam: module.erl \ <tag><c>{nowarn_unused_function, FAs}</c></tag> <item> - <p>Turns off warnings for unused local functions as - <c>nowarn_unused_function</c> but only for the mentioned + <p>Turns off warnings for unused local functions like + <c>nowarn_unused_function</c> does, but only for the mentioned local functions. <c>FAs</c> is a tuple <c>{Name,Arity}</c> or a list of such tuples.</p> </item> <tag><c>nowarn_deprecated_function</c></tag> <item> - <p>Turns off warnings for calls to deprecated functions. By - default (<c>warn_deprecated_function</c>), warnings are - emitted for every call to a function known by the compiler - to be deprecated. Note that the compiler does not know - about the <c>-deprecated()</c> attribute but uses an + <p>Turns off warnings for calls to deprecated functions. Default + is to emit warnings for every call to a function known by the + compiler to be deprecated. Notice that the compiler does not know + about attribute <c>-deprecated()</c>, but uses an assembled list of deprecated functions in Erlang/OTP. To - do a more general check the <c>Xref</c> tool can be used. + do a more general check, the Xref tool can be used. See also <seealso marker="tools:xref#deprecated_function">xref(3)</seealso> and the function - <seealso marker="tools:xref#m/1">xref:m/1</seealso> also - accessible through - the <seealso marker="stdlib:c#xm/1">c:xm/1</seealso> - function.</p> + <seealso marker="tools:xref#m/1">xref:m/1</seealso>, also + accessible through the function + <seealso marker="stdlib:c#xm/1">c:xm/1</seealso>.</p> </item> <tag><c>{nowarn_deprecated_function, MFAs}</c></tag> <item> - <p>Turns off warnings for calls to deprecated functions as - <c>nowarn_deprecated_function</c> but only for + <p>Turns off warnings for calls to deprecated functions like + <c>nowarn_deprecated_function</c> does, but only for the mentioned functions. <c>MFAs</c> is a tuple <c>{Module,Name,Arity}</c> or a list of such tuples.</p> </item> <tag><c>nowarn_deprecated_type</c></tag> <item> - <p>Turns off warnings for uses of deprecated types. By - default (<c>warn_deprecated_type</c>), warnings are - emitted for every use of a type known by the compiler - to be deprecated.</p> + <p>Turns off warnings for use of deprecated types. Default + is to emit warnings for every use of a type known by the compiler + to be deprecated.</p> </item> <tag><c>warn_obsolete_guard</c></tag> <item> - <p>Causes warnings to be emitted for calls to old type - testing BIFs such as <c>pid/1</c> and <c>list/1</c>. See - the - <seealso marker="doc/reference_manual:expressions#guards">Erlang Reference Manual</seealso> + <p>Emits warnings for calls to old type testing BIFs, + such as <c>pid/1</c> and <c>list/1</c>. See the + <seealso marker="doc/reference_manual:expressions#guards">Erlang Reference Manual</seealso> for a complete list of type testing BIFs and their old - equivalents. No warnings for calls to old type testing - BIFs, which is the default, can be selected by the option - <c>nowarn_obsolete_guard</c>.</p> + equivalents. Default is to emit no warnings for calls to + old type testing BIFs.</p> </item> <tag><c>warn_unused_import</c></tag> <item> - <p>Causes warnings to be emitted for unused imported - functions. No warnings for unused imported functions, - which is the default, can be selected by the option - <c>nowarn_unused_import</c>. </p> + <p>Emits warnings for unused imported functions. + Default is to emit no warnings for unused imported functions.</p> </item> <tag><c>nowarn_unused_vars</c></tag> <item> - <p>By default, warnings are emitted for variables which - are not used, with the exception of variables beginning - with an underscore ("Prolog style warnings"). + <p>By default, warnings are emitted for unused variables, + except for variables beginning with an underscore + ("Prolog style warnings"). Use this option to turn off this kind of warnings.</p> </item> <tag><c>nowarn_unused_record</c></tag> <item> - <p>Turns off warnings for unused record types. By - default (<c>warn_unused_records</c>), warnings are - emitted for unused locally defined record types.</p> + <p>Turns off warnings for unused record types. Default is to + emit warnings for unused locally defined record types.</p> </item> </taglist> <p>Another class of warnings is generated by the compiler during optimization and code generation. They warn about patterns that will never match (such as <c>a=b</c>), guards - that will always evaluate to false, and expressions that will + that always evaluate to false, and expressions that always fail (such as <c>atom+42</c>).</p> - - <p>Note that the compiler does not warn for expressions that it - does not attempt to optimize. For instance, the compiler tries - to evaluate <c>1/0</c>, notices that it will cause an - exception and emits a warning. On the other hand, - the compiler is silent about the similar expression - <c>X/0</c>; because of the variable in it, the compiler does - not even try to evaluate and therefore it emits no warnings. - </p> - - <p>Currently, those warnings cannot be disabled (except by + <p>Those warnings cannot be disabled (except by disabling all warnings).</p> + <note> + <p>The compiler does not warn for expressions that it + does not attempt to optimize. For example, the compiler tries + to evaluate <c>1/0</c>, detects that it will cause an + exception, and emits a warning. However, + the compiler is silent about the similar expression, + <c>X/0</c>, because of the variable in it. Thus, the compiler does + not even try to evaluate and therefore it emits no warnings.</p> + </note> + <warning> - <p>Obviously, the absence of warnings does not mean that + <p>The absence of warnings does not mean that there are no remaining errors in the code.</p> </warning> - - <p>Note that all the options except the include path - (<c>{i,Dir}</c>) can also be given in the file with a - <c>-compile([Option,...])</c>. attribute. - The <c>-compile()</c> attribute is allowed after function + + <note> + <p>All options, except the include path + (<c>{i,Dir}</c>), can also be given in the file with attribute + <c>-compile([Option,...])</c>. + Attribute <c>-compile()</c> is allowed after the function definitions.</p> - - <p>Note also that the <c>{nowarn_unused_function, FAs}</c>, + </note> + + <note> + <p>The options <c>{nowarn_unused_function, FAs}</c>, <c>{nowarn_bif_clash, FAs}</c>, and - <c>{nowarn_deprecated_function, MFAs}</c> options are only + <c>{nowarn_deprecated_function, MFAs}</c> are only recognized when given in files. They are not affected by - the <c>warn_unused_function</c>, <c>warn_bif_clash</c>, or - <c>warn_deprecated_function</c> options.</p> + options <c>warn_unused_function</c>, <c>warn_bif_clash</c>, or + <c>warn_deprecated_function</c>.</p> + </note> <p>For debugging of the compiler, or for pure curiosity, the intermediate code generated by each compiler pass can be inspected. - A complete list of the options to produce list files can be - printed by typing <c>compile:options()</c> at the Erlang - shell prompt. - The options will be printed in order that the passes are + To print a complete list of the options to produce list files, + type <c>compile:options()</c> at the Erlang shell prompt. + The options are printed in the order that the passes are executed. If more than one listing option is used, the one representing the earliest pass takes effect.</p> - <p><em>Unrecognized options are ignored.</em></p> + <p>Unrecognized options are ignored.</p> <p>Both <c>WarningList</c> and <c>ErrorList</c> have the following format:</p> <code> -[{FileName,[ErrorInfo]}]. - </code> - - <p><c>ErrorInfo</c> is described below. The file name has been - included here as the compiler uses the Erlang pre-processor - <c>epp</c>, which allows the code to be included in other - files. For this reason, it is important to know to - <em>which</em> file an error or warning line number refers. +[{FileName,[ErrorInfo]}].</code> + + <p><c>ErrorInfo</c> is described later in this section. + The filename is included here, as the compiler uses the + Erlang pre-processor <c>epp</c>, which allows the code to be + included in other files. It is therefore important to know to + <em>which</em> file the line number of an error or a warning refers. </p> </desc> </func> <func> <name>forms(Forms)</name> - <fsummary>Compile a list of forms</fsummary> + <fsummary>Compiles a list of forms.</fsummary> <desc> <p>Is the same as <c>forms(File, [verbose,report_errors,report_warnings])</c>. @@ -670,7 +676,7 @@ module.beam: module.erl \ <func> <name>forms(Forms, Options) -> CompRet</name> - <fsummary>Compile a list of forms</fsummary> + <fsummary>Compiles a list of forms.</fsummary> <type> <v>Forms = [Form]</v> <v>CompRet = BinRet | ErrRet</v> @@ -681,48 +687,49 @@ module.beam: module.erl \ <desc> <p>Analogous to <c>file/1</c>, but takes a list of forms (in the Erlang abstract format representation) as first argument. - The option <c>binary</c> is implicit; i.e., no object code - file is produced. Options that would ordinarily produce a - listing file, such as 'E', will instead cause the internal - format for that compiler pass (an Erlang term; usually not a - binary) to be returned instead of a binary.</p> + Option <c>binary</c> is implicit, that is, no object code + file is produced. For options that normally produce a listing + file, such as 'E', the internal format for that compiler pass + (an Erlang term, usually not a binary) is returned instead of + a binary.</p> </desc> </func> <func> <name>format_error(ErrorDescriptor) -> chars()</name> - <fsummary>Format an error descriptor</fsummary> + <fsummary>Formats an error descriptor.</fsummary> <type> <v>ErrorDescriptor = errordesc()</v> </type> <desc> <p>Uses an <c>ErrorDescriptor</c> and returns a deep list of - characters which describes the error. This function is - usually called implicitly when an <c>ErrorInfo</c> structure - is processed. See below.</p> + characters that describes the error. This function is + usually called implicitly when an <c>ErrorInfo</c> structure + (described in section + <seealso marker="#error_information">Error Information</seealso>) is processed.</p> </desc> </func> <func> <name>output_generated(Options) -> true | false</name> - <fsummary>Determine whether the compile will generate an output file</fsummary> + <fsummary>Determines whether the compiler generates an output file.</fsummary> <type> <v>Options = [term()]</v> </type> <desc> - <p>Determines whether the compiler would generate a <c>beam</c> + <p>Determines whether the compiler generates a <c>beam</c> file with the given options. <c>true</c> means that a <c>beam</c> - file would be generated; <c>false</c> means that the compiler - would generate some listing file, return a binary, or merely - check the syntax of the source code.</p> + file is generated. <c>false</c> means that the compiler + generates some listing file, returns a binary, or merely + checks the syntax of the source code.</p> </desc> </func> <func> <name>noenv_file(File, Options) -> CompRet</name> - <fsummary>Compile a file (ignoring ERL_COMPILER_OPTIONS)</fsummary> + <fsummary>Compiles a file (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary> <desc> - <p>Works exactly like <seealso marker="#file/2">file/2</seealso>, + <p>Works like <seealso marker="#file/2">file/2</seealso>, except that the environment variable <c>ERL_COMPILER_OPTIONS</c> is not consulted.</p> </desc> @@ -730,9 +737,9 @@ module.beam: module.erl \ <func> <name>noenv_forms(Forms, Options) -> CompRet</name> - <fsummary>Compile a list of forms (ignoring ERL_COMPILER_OPTIONS)</fsummary> + <fsummary>Compiles a list of forms (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary> <desc> - <p>Works exactly like <seealso marker="#forms/2">forms/2</seealso>, + <p>Works like <seealso marker="#forms/2">forms/2</seealso>, except that the environment variable <c>ERL_COMPILER_OPTIONS</c> is not consulted.</p> </desc> @@ -740,12 +747,13 @@ module.beam: module.erl \ <func> <name>noenv_output_generated(Options) -> true | false</name> - <fsummary>Determine whether the compile will generate an output file (ignoring ERL_COMPILER_OPTIONS)</fsummary> + <fsummary>Determines whether the compiler generates an output file + (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary> <type> <v>Options = [term()]</v> </type> <desc> - <p>Works exactly like + <p>Works like <seealso marker="#output_generated/1">output_generated/1</seealso>, except that the environment variable <c>ERL_COMPILER_OPTIONS</c> is not consulted.</p> @@ -755,14 +763,14 @@ module.beam: module.erl \ </funcs> <section> - <title>Default compiler options</title> + <title>Default Compiler Options</title> <p>The (host operating system) environment variable <c>ERL_COMPILER_OPTIONS</c> can be used to give default compiler options. Its value must be a valid Erlang term. If the value is a - list, it will be used as is. If it is not a list, it will be put + list, it is used as is. If it is not a list, it is put into a list.</p> - <p>The list will be appended to any options given to + <p>The list is appended to any options given to <seealso marker="#file/2">file/2</seealso>, <seealso marker="#forms/2">forms/2</seealso>, and <seealso marker="#output_generated/1">output_generated/2</seealso>. @@ -770,9 +778,12 @@ module.beam: module.erl \ <seealso marker="#noenv_file/2">noenv_file/2</seealso>, <seealso marker="#noenv_forms/2">noenv_forms/2</seealso>, or <seealso marker="#noenv_output_generated/1">noenv_output_generated/2</seealso> - if you don't want the environment variable to be consulted - (for instance, if you are calling the compiler recursively from - inside a parse transform).</p> + if you do not want the environment variable to be consulted, + for example, if you are calling the compiler recursively from + inside a parse transform.</p> + + <p>The list can be retrieved with + <seealso marker="#env_compiler_options/0">env_compiler_options/0</seealso>.</p> </section> <section> @@ -781,31 +792,31 @@ module.beam: module.erl \ module. Inlining means that a call to a function is replaced with the function body with the arguments replaced with the actual values. The semantics are preserved, except if exceptions are - generated in the inlined code. Exceptions will be reported as + generated in the inlined code. Exceptions are reported as occurring in the function the body was inlined into. Also, - <c>function_clause</c> exceptions will be converted to similar + <c>function_clause</c> exceptions are converted to similar <c>case_clause</c> exceptions.</p> - <p>When a function is inlined, the original function will be + <p>When a function is inlined, the original function is kept if it is exported (either by an explicit export or if the - <c>export_all</c> option was given) or if not all calls to the - function were inlined.</p> + option <c>export_all</c> was given) or if not all calls to the + function are inlined.</p> <p>Inlining does not necessarily improve running time. - For instance, inlining may increase Beam stack usage which will - probably be detrimental to performance for recursive functions. + For example, inlining can increase Beam stack use, which + probably is detrimental to performance for recursive functions. </p> - <p>Inlining is never default; it must be explicitly enabled with a + <p>Inlining is never default. It must be explicitly enabled with a compiler option or a <c>-compile()</c> attribute in the source module.</p> - <p>To enable inlining, either use the <c>inline</c> option to - let the compiler decide which functions to inline or + <p>To enable inlining, either use the option <c>inline</c> to + let the compiler decide which functions to inline, or <c>{inline,[{Name,Arity},...]}</c> to have the compiler inline all calls to the given functions. If the option is given inside a <c>compile</c> directive in an Erlang module, <c>{Name,Arity}</c> - may be written as <c>Name/Arity</c>.</p> + can be written as <c>Name/Arity</c>.</p> <p>Example of explicit inlining:</p> @@ -817,33 +828,30 @@ pi() -> 3.1416. <p>Example of implicit inlining:</p> <pre> --compile(inline). - </pre> +-compile(inline).</pre> - <p>The <c>{inline_size,Size}</c> option controls how large functions - that are allowed to be inlined. Default is <c>24</c>, which will - keep the size of the inlined code roughly the same as - the un-inlined version (only relatively small functions will be + <p>The option <c>{inline_size,Size}</c> controls how large functions + that are allowed to be inlined. Default is <c>24</c>, which + keeps the size of the inlined code roughly the same as + the un-inlined version (only relatively small functions are inlined).</p> <p>Example:</p> <pre> %% Aggressive inlining - will increase code size. -compile(inline). --compile({inline_size,100}). - </pre> +-compile({inline_size,100}).</pre> </section> <section> - <title>Inlining of list functions</title> - <p>The compiler can also inline a variety of list manipulation functions - from the stdlib's lists module.</p> + <title>Inlining of List Functions</title> + <p>The compiler can also inline various list manipulation functions + from the module <c>list</c> in STDLIB.</p> <p>This feature must be explicitly enabled with a compiler option or a <c>-compile()</c> attribute in the source module.</p> - <p>To enable inlining of list functions, use the <c>inline_list_funcs</c> - option.</p> + <p>To enable inlining of list functions, use option <c>inline_list_funcs</c>.</p> <p>The following functions are inlined:</p> <list type="bulleted"> @@ -869,24 +877,23 @@ pi() -> 3.1416. </section> <section> + <marker id="error_information"></marker> <title>Error Information</title> - <p>The <c>ErrorInfo</c> mentioned above is the standard - <c>ErrorInfo</c> structure which is returned from all IO modules. + <p>The <c>ErrorInfo</c> mentioned earlier is the standard + <c>ErrorInfo</c> structure, which is returned from all I/O modules. It has the following format:</p> <code> -{ErrorLine, Module, ErrorDescriptor} - </code> +{ErrorLine, Module, ErrorDescriptor}</code> - <p><c>ErrorLine</c> will be the atom <c>none</c> if the error does - not correspond to a specific line (e.g. if the source file does - not exist).</p> + <p><c>ErrorLine</c> is the atom <c>none</c> if the error does + not correspond to a specific line, for example, if the source file does + not exist.</p> <p>A string describing the error is obtained with the following call:</p> <code> -Module:format_error(ErrorDescriptor) - </code> +Module:format_error(ErrorDescriptor)</code> </section> <section> diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 9b5b44f3e1..dd6b132a92 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -4,20 +4,21 @@ <chapter> <header> <copyright> - <year>2004</year><year>2013</year> + <year>2004</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. + 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. </legalnotice> @@ -31,6 +32,322 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A literal binary matching regression was introduced in + 19.0 where a match could fail to resolve to the right + clause. This has now been fixed.</p> + <p> + Own Id: OTP-13738</p> + </item> + </list> + </section> + +</section> + +<section><title>Compiler 7.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p><c>compile:forms/1,2</c> would crash when used in a + working directory that had been deleted by another + process.</p> + <p> + Own Id: OTP-13430 Aux Id: ERL-113 </p> + </item> + <item> + <p>Dialyzer no longer crashes when there is an invalid + function call such as <c>42(7)</c> in a module being + analyzed. The compiler will now warn for invalid function + calls such as <c>X = 42, x(7)</c>.</p> + <p> + Own Id: OTP-13552 Aux Id: ERL-138 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Optimization of tuple matching has been slightly + improved.</p> + <p> + Own Id: OTP-12951</p> + </item> + <item> + <p>Five deprecated and undocumented functions in the + module <c>core_lib</c> have been removed. The functions + are: <c>get_anno/{1,2}</c>, <c>is_literal/1</c>, + <c>is_literal_list/1</c>, and <c>literal_value</c>. Use + the appropriate functions in the <c>cerl</c> module + instead.</p> + <p> + Own Id: OTP-12979</p> + </item> + <item> + <p>The pre-processor can now expand the ?FUNCTION_NAME + and ?FUNCTION_ARITY macros.</p> + <p> + Own Id: OTP-13059</p> + </item> + <item> + <p>The function mapfold/4 has been added to the + <c>cerl_trees</c> module.</p> + <p> + Own Id: OTP-13280</p> + </item> + <item> + <p>Bitstring comprehensions have been generalized to + allow arbitrary expressions in the construction part.</p> + <p> + Own Id: OTP-13289</p> + </item> + <item> + <p>The compiler will now produce warnings for binary + patterns that will never match (example: + <c><<-1/unsigned>> = Bin</c>). </p> + <p> + Own Id: OTP-13374 Aux Id: ERL-44 </p> + </item> + <item> + <p>The compiler will no longer put the compilation date + and time into BEAM files. That means that two BEAM files + compiled on the same computer from the same source code + and compilation options will be identical.</p> + <p>Note: If you want to find out whether a BEAM file on + disk is different from the loaded code, compared the MD5 + value obtained from <c>Mod:module_info(md5)</c> with the + MD5 value obtained from + <c>beam_lib:md5(BeamFileForMod)</c></p>. + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-13504</p> + </item> + <item> + <p>The function <c>compile:env_compiler_options/0</c> has + been added to allow tools to pick up the same default + compiler options as the compiler itself.</p> + <p> + Own Id: OTP-13654</p> + </item> + </list> + </section> + +</section> + +<section><title>Compiler 6.0.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + An complicated guard expression in a function call could + crash the compiler. (Thanks to Thomas Arts for reporting + this bug.)</p> + <p> + Own Id: OTP-13208</p> + </item> + <item> + <p>Constructing a map in a guard in a catch could crash + the compiler. (Thanks to Thomas Arts for reporting this + bug.)</p> + <p> + Own Id: OTP-13223</p> + </item> + <item> + <p>Updating a fun as if it were a map would cause the + compiler to crash. (Thanks to Thomas Arts for reporting + this bug.)</p> + <p> + Own Id: OTP-13231</p> + </item> + <item> + <p> + Fix pretty printing of Core Maps</p> + <p> + Literal maps could cause Dialyzer to crash when pretty + printing the results.</p> + <p> + Own Id: OTP-13238</p> + </item> + <item> + <p> + A complex combination of bit syntax matching operations + would cause an internal consistency check failure during + compilation. (Thanks to Jose Valim for reporting this + bug.)</p> + <p> + Own Id: OTP-13309</p> + </item> + </list> + </section> + +</section> + +<section><title>Compiler 6.0.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix cerl_trees:label/2 bug with map K/V swap</p> + <p> + Own Id: OTP-13091</p> + </item> + <item> + <p> + Warnings produced when the '<c>bin_opt_info</c>' option + was given could sometimes lack filenames and line + numbers. (Thanks to José Valim for reporting this bug.)</p> + <p> + Own Id: OTP-13113</p> + </item> + </list> + </section> + +</section> + +<section><title>Compiler 6.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix <c>get_map_elements</c> register corruption</p> + <p> + Instruction <c>get_map_elements</c> might destroy target + registers when the fail-label is taken. Only seen for + patterns with two, and only two, target registers. + Specifically if we copy one register and then jump.</p> + <p> + Own Id: OTP-12967</p> + </item> + </list> + </section> + +</section> + +<section><title>Compiler 6.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The compiler optimizes away building of terms that are + never actually used. As a result, the compiler in OTP 18 + may produce more warnings for terms that are built but + not used than the compiler in OTP 17.</p> + <p> + Own Id: OTP-12453</p> + </item> + <item> + <p> + Using a map could incorrectly suppress warnings for + unused variables.</p> + <p> + Own Id: OTP-12515</p> + </item> + <item> + <p> + The compiler now properly reports unknown parse + transforms. That is, <c>undef</c> exceptions coming from + the parse transform itself is reported differently from + the absence of the parse transform.</p> + <p> + Own Id: OTP-12723</p> + </item> + <item> + <p> + Allow for 'creation of sub binary delayed' optimization + if maps instructions are in a clause.</p> + <p> + Own Id: OTP-12758</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The <c>cerl</c> and <c>cerl_trees</c> modules in the + Compiler application are now documented.</p> + <p> + Own Id: OTP-11978</p> + </item> + <item> + <p> + The deprecated '<c>asm</c>' option has been removed.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-12100</p> + </item> + <item> + <p> + Support variables as Map keys in expressions and patterns</p> + <p>Erlang will accept any expression as keys in Map + expressions and it will accept literals or bound + variables as keys in Map patterns.</p> + <p> + Own Id: OTP-12218</p> + </item> + <item> + <p> + Infer Map type information in beam_type compiler + optimization pass.</p> + <p> + Own Id: OTP-12253</p> + </item> + <item> + <p> + Compiler optimizations have been improved.</p> + <p> + Own Id: OTP-12393</p> + </item> + <item> + <p> + Five undocumented functions in the module <c>core_lib</c> + have been deprecated and will be removed in the next + major release. The functions are: <c>get_anno/{1,2}</c>, + <c>is_literal/1</c>, <c>is_literal_list/1</c>, and + <c>literal_value</c>. Use the appropriate functions in + the <c>cerl</c> module instead.</p> + <p> + Own Id: OTP-12497</p> + </item> + <item> + <p> + Change some internal data structures to Maps in order to + speed up compilation time. Measured speed up is around + 10%-15%.</p> + <p> + Own Id: OTP-12774</p> + </item> + <item> + <p> + Fix beam_bool pass for Maps instruction get_map_elements</p> + <p> + Before beam_split the get_map_elements instruction is + still in blocks and the helper function in beam_jump did + not reflect this.</p> + <p> + Own Id: OTP-12844 Aux Id: 17 </p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 5.0.4</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -324,22 +641,28 @@ <p> EEP43: New data type - Maps</p> <p> - With Maps you may for instance: <taglist> <item><c>M0 = - #{ a => 1, b => 2}, % create - associations</c></item> <item><c>M1 = M0#{ a := 10 }, % - update values</c></item> <item><c>M2 = M1#{ "hi" => - "hello"}, % add new associations</c></item> <item><c>#{ - "hi" := V1, a := V2, b := V3} = M2. % match keys with - values</c></item> </taglist></p> + With Maps you may for instance:</p> + <taglist> + <tag/> <item><c>M0 = #{ a => 1, b => 2}, % create + associations</c></item> + <tag/><item><c>M1 = M0#{ a := 10 }, % update values</c></item> + <tag/><item><c>M2 = M1#{ "hi" => + "hello"}, % add new associations</c></item> + <tag/><item><c>#{ "hi" := V1, a := V2, b := V3} = M2. + % match keys with values</c></item> + </taglist> <p> For information on how to use Maps please see Map Expressions in the <seealso marker="doc/reference_manual:expressions#map_expressions"> Reference Manual</seealso>.</p> <p> The current implementation is without the following - features: <taglist> <item>No variable keys</item> - <item>No single value access</item> <item>No map - comprehensions</item> </taglist></p> + features:</p> + <taglist> + <tag/><item>No variable keys</item> + <tag/><item>No single value access</item> + <tag/><item>No map comprehensions</item> + </taglist> <p> Note that Maps is <em>experimental</em> during OTP 17.0.</p> <p> @@ -1659,7 +1982,7 @@ <c>RightExpr</c> or vice versa. The evaluation order is only important if the expressions contains and/or depends on operations with side-effects, such as message passing - or <c>ets</c> operations.</p> + or ETS operations.</p> <p> Own Id: OTP-7206</p> </item> diff --git a/lib/compiler/doc/src/notes_history.xml b/lib/compiler/doc/src/notes_history.xml index 9e8934f416..16bfb3d34b 100644 --- a/lib/compiler/doc/src/notes_history.xml +++ b/lib/compiler/doc/src/notes_history.xml @@ -4,20 +4,21 @@ <chapter> <header> <copyright> - <year>2006</year><year>2013</year> + <year>2006</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. + 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. </legalnotice> diff --git a/lib/compiler/doc/src/part_notes.xml b/lib/compiler/doc/src/part_notes.xml index 0c1fdd567d..c1f0ff3861 100644 --- a/lib/compiler/doc/src/part_notes.xml +++ b/lib/compiler/doc/src/part_notes.xml @@ -4,20 +4,21 @@ <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2004</year><year>2013</year> + <year>2004</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. + 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. </legalnotice> diff --git a/lib/compiler/doc/src/part_notes_history.xml b/lib/compiler/doc/src/part_notes_history.xml index a4909f156e..4019676b83 100644 --- a/lib/compiler/doc/src/part_notes_history.xml +++ b/lib/compiler/doc/src/part_notes_history.xml @@ -5,20 +5,21 @@ <header> <copyright> <year>2006</year> - <year>2013</year> + <year>2016</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. + 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 - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. + 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. The Initial Developer of the Original Code is Ericsson AB. </legalnotice> diff --git a/lib/compiler/doc/src/ref_man.xml b/lib/compiler/doc/src/ref_man.xml index 6478ad4b11..c32c499008 100644 --- a/lib/compiler/doc/src/ref_man.xml +++ b/lib/compiler/doc/src/ref_man.xml @@ -4,20 +4,21 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1996</year><year>2013</year> + <year>1996</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> - The contents of this file are subject to the Erlang Public License, - Version 1.1, (the "License"); you may not use this file except in - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. + 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. </legalnotice> @@ -29,7 +30,7 @@ <file>application.sgml</file> </header> <description> - <p>The <em>Compiler</em> application compiles Erlang + <p>The Compiler application compiles Erlang code to byte-code. The highly compact byte-code is executed by the Erlang emulator.</p> </description> diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index c6d09d85eb..518c89d044 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -1,18 +1,19 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2013. All Rights Reserved. +# Copyright Ericsson AB 1996-2016. All Rights Reserved. # -# The contents of this file are subject to the Erlang Public License, -# Version 1.1, (the "License"); you may not use this file except in -# compliance with the License. You should have received a copy of the -# Erlang Public License along with this software. If not, it can be -# retrieved online at http://www.erlang.org/. +# 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 # -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -# the License for the specific language governing rights and limitations -# under the License. +# 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% # @@ -49,6 +50,7 @@ MODULES = \ beam_asm \ beam_block \ beam_bool \ + beam_bs \ beam_bsm \ beam_clean \ beam_dead \ @@ -61,6 +63,7 @@ MODULES = \ beam_opcodes \ beam_peep \ beam_receive \ + beam_reorder \ beam_split \ beam_trim \ beam_type \ @@ -70,6 +73,7 @@ MODULES = \ cerl \ cerl_clauses \ cerl_inline \ + cerl_sets \ cerl_trees \ compile \ core_lib \ @@ -81,6 +85,7 @@ MODULES = \ rec_env \ sys_core_dsetel \ sys_core_fold \ + sys_core_fold_lists \ sys_core_inline \ sys_pre_attributes \ sys_pre_expand \ @@ -158,6 +163,10 @@ $(EBIN)/beam_asm.beam: $(ESRC)/beam_asm.erl $(EGEN)/beam_opcodes.hrl $(EBIN)/cerl_inline.beam: $(ESRC)/cerl_inline.erl $(V_ERLC) $(ERL_COMPILE_FLAGS) +nowarn_shadow_vars -o$(EBIN) $< +# Inlining core_parse is slow and has no benefit. +$(EBIN)/core_parse.beam: $(EGEN)/core_parse.erl + $(V_ERLC) $(subst +inline,,$(ERL_COMPILE_FLAGS)) -o$(EBIN) $< + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- @@ -187,6 +196,7 @@ $(EBIN)/core_parse.beam: core_parse.hrl $(EGEN)/core_parse.erl $(EBIN)/core_pp.beam: core_parse.hrl $(EBIN)/sys_core_dsetel.beam: core_parse.hrl $(EBIN)/sys_core_fold.beam: core_parse.hrl +$(EBIN)/sys_core_fold_lists.beam: core_parse.hrl $(EBIN)/sys_core_inline.beam: core_parse.hrl $(EBIN)/sys_pre_expand.beam: ../../stdlib/include/erl_bits.hrl $(EBIN)/v3_codegen.beam: v3_life.hrl diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl index fe4f473846..91e6d80da3 100644 --- a/lib/compiler/src/beam_a.erl +++ b/lib/compiler/src/beam_a.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012-2013. All Rights Reserved. +%% Copyright Ericsson AB 2012-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -54,6 +55,9 @@ rename_instrs([{call_only,A,F}|Is]) -> [{call,A,F},return|rename_instrs(Is)]; rename_instrs([{call_ext_only,A,F}|Is]) -> [{call_ext,A,F},return|rename_instrs(Is)]; +rename_instrs([{'%live',_}|Is]) -> + %% When compiling from old .S files. + rename_instrs(Is); rename_instrs([I|Is]) -> [rename_instr(I)|rename_instrs(Is)]; rename_instrs([]) -> []. @@ -88,6 +92,10 @@ rename_instr({bs_private_append=I,F,Sz,U,Src,Flags,Dst}) -> {bs_init,F,{I,U,Flags},none,[Sz,Src],Dst}; rename_instr(bs_init_writable=I) -> {bs_init,{f,0},I,1,[{x,0}],{x,0}}; +rename_instr({test,Op,F,[Ctx,Bits,{string,Str}]}) -> + %% When compiling from a .S file. + <<Bs:Bits/bits,_/bits>> = list_to_binary(Str), + {test,Op,F,[Ctx,Bs]}; rename_instr({put_map_assoc,Fail,S,D,R,L}) -> {put_map,Fail,assoc,S,D,R,L}; rename_instr({put_map_exact,Fail,S,D,R,L}) -> diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl index f8cf178d2e..f6ca7a0afb 100644 --- a/lib/compiler/src/beam_asm.erl +++ b/lib/compiler/src/beam_asm.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -29,11 +30,12 @@ module(Code, Abst, SourceFile, Opts) -> {ok,assemble(Code, Abst, SourceFile, Opts)}. -assemble({Mod,Exp,Attr0,Asm0,NumLabels}, Abst, SourceFile, Opts) -> +assemble({Mod,Exp0,Attr0,Asm0,NumLabels}, Abst, SourceFile, Opts) -> {1,Dict0} = beam_dict:atom(Mod, beam_dict:new()), {0,Dict1} = beam_dict:fname(atom_to_list(Mod) ++ ".erl", Dict0), NumFuncs = length(Asm0), {Asm,Attr} = on_load(Asm0, Attr0), + Exp = cerl_sets:from_list(Exp0), {Code,Dict2} = assemble_1(Asm, Exp, Dict1, []), build_file(Code, Attr, Dict2, NumLabels, NumFuncs, Abst, SourceFile, Opts). @@ -60,7 +62,7 @@ insert_on_load_instruction(Is0, Entry) -> Bef ++ [El,on_load|Is]. assemble_1([{function,Name,Arity,Entry,Asm}|T], Exp, Dict0, Acc) -> - Dict1 = case member({Name,Arity}, Exp) of + Dict1 = case cerl_sets:is_element({Name,Arity}, Exp) of true -> beam_dict:export(Name, Arity, Entry, Dict0); false -> @@ -132,10 +134,10 @@ build_file(Code, Attr, Dict, NumLabels, NumFuncs, Abst, SourceFile, Opts) -> LiteralChunk = case beam_dict:literal_table(Dict) of {0,[]} -> []; {NumLiterals,LitTab0} -> - LitTab1 = iolist_to_binary(LitTab0), - LitTab2 = <<NumLiterals:32,LitTab1/binary>>, - LitTab = iolist_to_binary(zlib:compress(LitTab2)), - chunk(<<"LitT">>, <<(byte_size(LitTab2)):32>>, LitTab) + LitTab1 = [<<NumLiterals:32>>,LitTab0], + LitTab = zlib:compress(LitTab1), + chunk(<<"LitT">>, <<(iolist_size(LitTab1)):32>>, + LitTab) end, %% Create the line chunk. @@ -223,10 +225,12 @@ flatten_imports(Imps) -> list_to_binary(map(fun({M,F,A}) -> <<M:32,F:32,A:32>> end, Imps)). build_attributes(Opts, SourceFile, Attr, MD5) -> + Misc0 = case SourceFile of + [] -> []; + [_|_] -> [{source,SourceFile}] + end, Misc = case member(slim, Opts) of - false -> - {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime(), - [{time,{Y,Mo,D,H,Mi,S}},{source,SourceFile}]; + false -> Misc0; true -> [] end, Compile = [{options,Opts},{version,?COMPILER_VSN}|Misc], @@ -431,45 +435,35 @@ encode_alloc_list_1([], Dict, Acc) -> {iolist_to_binary(Acc),Dict}. encode(Tag, N) when N < 0 -> - encode1(Tag, negative_to_bytes(N, [])); + encode1(Tag, negative_to_bytes(N)); encode(Tag, N) when N < 16 -> (N bsl 4) bor Tag; encode(Tag, N) when N < 16#800 -> [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff]; encode(Tag, N) -> - encode1(Tag, to_bytes(N, [])). + encode1(Tag, to_bytes(N)). encode1(Tag, Bytes) -> - case length(Bytes) of + case iolist_size(Bytes) of Num when 2 =< Num, Num =< 8 -> [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes]; Num when 8 < Num -> [2#11111000 bor Tag, encode(?tag_u, Num-9)| Bytes] end. - -to_bytes(N0, Acc) -> - Bits = 3*128, - case N0 bsr Bits of - 0 -> - to_bytes_1(N0, Acc); - N -> - to_bytes(N, binary_to_list(<<N0:Bits>>) ++ Acc) - end. - -to_bytes_1(0, [B|_]=Done) when B < 128 -> Done; -to_bytes_1(N, Acc) -> to_bytes(N bsr 8, [N band 16#ff|Acc]). - -negative_to_bytes(N0, Acc) -> - Bits = 3*128, - case N0 bsr Bits of - -1 -> - negative_to_bytes_1(N0, Acc); - N -> - negative_to_bytes_1(N, binary_to_list(<<N0:Bits>>) ++ Acc) +to_bytes(N) -> + Bin = binary:encode_unsigned(N), + case Bin of + <<0:1,_/bits>> -> Bin; + <<1:1,_/bits>> -> [0,Bin] end. -negative_to_bytes_1(-1, [B1,_B2|_]=Done) when B1 > 127 -> - Done; -negative_to_bytes_1(N, Acc) -> - negative_to_bytes_1(N bsr 8, [N band 16#ff|Acc]). +negative_to_bytes(N) when N >= -16#8000 -> + <<N:16>>; +negative_to_bytes(N) -> + Bytes = byte_size(binary:encode_unsigned(-N)), + Bin = <<N:Bytes/unit:8>>, + case Bin of + <<0:1,_/bits>> -> [16#ff,Bin]; + <<1:1,_/bits>> -> Bin + end. diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index 7a30c68593..ec41925beb 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -22,14 +23,13 @@ -module(beam_block). -export([module/2]). --import(lists, [mapfoldl/3,reverse/1,reverse/2,foldl/3,member/2]). --define(MAXREG, 1024). +-import(lists, [reverse/1,reverse/2,foldl/3,member/2]). -module({Mod,Exp,Attr,Fs0,Lc0}, _Opt) -> - {Fs,Lc} = mapfoldl(fun function/2, Lc0, Fs0), +module({Mod,Exp,Attr,Fs0,Lc}, _Opt) -> + Fs = [function(F) || F <- Fs0], {ok,{Mod,Exp,Attr,Fs,Lc}}. -function({function,Name,Arity,CLabel,Is0}, Lc0) -> +function({function,Name,Arity,CLabel,Is0}) -> try %% Collect basic blocks and optimize them. Is1 = blockify(Is0), @@ -39,11 +39,8 @@ function({function,Name,Arity,CLabel,Is0}, Lc0) -> Is5 = opt_blocks(Is4), Is6 = beam_utils:delete_live_annos(Is5), - %% Optimize bit syntax. - {Is,Lc} = bsm_opt(Is6, Lc0), - %% Done. - {{function,Name,Arity,CLabel,Is},Lc} + {function,Name,Arity,CLabel,Is6} catch Class:Error -> Stack = erlang:get_stacktrace(), @@ -61,65 +58,22 @@ blockify(Is) -> blockify([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_Lbl},{label,Fail}|Is], Acc) -> %% Useless instruction sequence. blockify(Is, Acc); - -%% New bit syntax matching. -blockify([{bs_save2,R,Point}=I,{bs_restore2,R,Point}|Is], Acc) -> - blockify([I|Is], Acc); -blockify([{bs_save2,R,Point}=I,{test,is_eq_exact,_,_}=Test, - {bs_restore2,R,Point}|Is], Acc) -> - blockify([I,Test|Is], Acc); - -%% Do other peep-hole optimizations. -blockify([{test,is_atom,{f,Fail},[Reg]}=I| - [{select,select_val,Reg,{f,Fail}, - [{atom,false},{f,_}=BrFalse, - {atom,true}=AtomTrue,{f,_}=BrTrue]}|Is]=Is0], - [{block,Bl}|_]=Acc) -> - case is_last_bool(Bl, Reg) of - false -> - blockify(Is0, [I|Acc]); - true -> - %% The last instruction is a boolean operator/guard BIF that can't fail. - %% We can convert the three-way branch to a two-way branch (eliminating - %% the reference to the failure label). - blockify(Is, [{jump,BrTrue}, - {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc]) - end; -blockify([{test,is_atom,{f,Fail},[Reg]}=I| - [{select,select_val,Reg,{f,Fail}, - [{atom,true}=AtomTrue,{f,_}=BrTrue, - {atom,false},{f,_}=BrFalse]}|Is]=Is0], - [{block,Bl}|_]=Acc) -> - case is_last_bool(Bl, Reg) of - false -> - blockify(Is0, [I|Acc]); - true -> - blockify(Is, [{jump,BrTrue}, - {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc]) - end; +blockify([{get_map_elements,F,S,{list,Gets}}|Is0], Acc) -> + %% A get_map_elements instruction is only safe at the beginning of + %% a block because of the failure label. + {Ss,Ds} = beam_utils:split_even(Gets), + I = {set,Ds,[S|Ss],{get_map_elements,F}}, + {Block,Is} = collect_block(Is0, [I]), + blockify(Is, [{block,Block}|Acc]); blockify([I|Is0]=IsAll, Acc) -> - case is_bs_put(I) of - true -> - {BsPuts0,Is} = collect_bs_puts(IsAll), - BsPuts = opt_bs_puts(BsPuts0), - blockify(Is, reverse(BsPuts, Acc)); - false -> - case collect(I) of - error -> blockify(Is0, [I|Acc]); - Instr when is_tuple(Instr) -> - {Block,Is} = collect_block(IsAll), - blockify(Is, [{block,Block}|Acc]) - end + case collect(I) of + error -> blockify(Is0, [I|Acc]); + Instr when is_tuple(Instr) -> + {Block,Is} = collect_block(IsAll), + blockify(Is, [{block,Block}|Acc]) end; blockify([], Acc) -> reverse(Acc). -is_last_bool([{set,[Reg],As,{bif,N,_}}], Reg) -> - Ar = length(As), - erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar) - orelse erl_internal:bool_op(N, Ar); -is_last_bool([_|Is], Reg) -> is_last_bool(Is, Reg); -is_last_bool([], _) -> false. - collect_block(Is) -> collect_block(Is, []). @@ -134,7 +88,9 @@ collect_block([I|Is]=Is0, Acc) -> case collect(I) of error -> {reverse(Acc),Is0}; Instr -> collect_block(Is, [Instr|Acc]) - end. + end; +collect_block([], Acc) -> + {reverse(Acc),[]}. collect({allocate,N,R}) -> {set,[],[],{alloc,R,{nozero,N,0,[]}}}; collect({allocate_zero,N,R}) -> {set,[],[],{alloc,R,{zero,N,0,[]}}}; @@ -154,9 +110,10 @@ collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list}; collect(remove_message) -> {set,[],[],remove_message}; collect({put_map,F,Op,S,D,R,{list,Puts}}) -> {set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}; -collect({get_map_elements,F,S,{list,Gets}}) -> - {set,Gets,[S],{get_map_elements,F}}; -collect({'catch',R,L}) -> {set,[R],[],{'catch',L}}; +collect({'catch'=Op,R,L}) -> + {set,[R],[],{try_catch,Op,L}}; +collect({'try'=Op,R,L}) -> + {set,[R],[],{try_catch,Op,L}}; collect(fclearerror) -> {set,[],[],fclearerror}; collect({fcheckerror,{f,0}}) -> {set,[],[],fcheckerror}; collect({fmove,S,D}) -> {set,[D],[S],fmove}; @@ -183,14 +140,16 @@ embed_lines([], Acc) -> Acc. opt_blocks([{block,Bl0}|Is]) -> %% The live annotation at the beginning is not useful. - [{'%live',_}|Bl] = Bl0, + [{'%live',_,_}|Bl] = Bl0, [{block,opt_block(Bl)}|opt_blocks(Is)]; opt_blocks([I|Is]) -> [I|opt_blocks(Is)]; opt_blocks([]) -> []. opt_block(Is0) -> - Is = find_fixpoint(fun opt/1, Is0), + Is = find_fixpoint(fun(Is) -> + opt_tuple_element(opt(Is)) + end, Is0), opt_alloc(Is). find_fixpoint(OptFun, Is0) -> @@ -200,14 +159,43 @@ find_fixpoint(OptFun, Is0) -> end. %% move_allocates(Is0) -> Is -%% Move allocate instructions upwards in the instruction stream, in the -%% hope of getting more possibilities for optimizing away moves later. +%% Move allocate instructions upwards in the instruction stream +%% (within the same block), in the hope of getting more possibilities +%% for optimizing away moves later. +%% +%% For example, we can transform the following instructions: +%% +%% get_tuple_element x(1) Element => x(2) +%% allocate_zero StackSize 3 %% x(0), x(1), x(2) are live +%% +%% to the following instructions: +%% +%% allocate_zero StackSize 2 %% x(0) and x(1) are live +%% get_tuple_element x(1) Element => x(2) +%% +%% NOTE: Since the beam_reorder pass has been run, it is no longer +%% safe to assume that if x(N) is initialized, then all lower-numbered +%% x registers are also initialized. +%% +%% For example, in general it is not safe to transform the following +%% instructions: %% -%% NOTE: Moving allocation instructions is only safe because it is done -%% immediately after code generation so that we KNOW that if {x,X} is -%% initialized, all x registers with lower numbers are also initialized. -%% That assumption may not be true after other optimizations, such as -%% the beam_utils:live_opt/1 optimization. +%% get_tuple_element x(0) Element => x(1) +%% allocate_zero StackSize 3 %x(0), x(1), x(2) are live +%% +%% to the following instructions: +%% +%% allocate_zero StackSize 3 +%% get_tuple_element x(0) Element => x(1) +%% +%% The transformation is safe if and only if x(1) has been +%% initialized previously. Unfortunately, beam_reorder may have moved +%% a get_tuple_element instruction so that x(1) is not always +%% initialized when this code is reached. To find whether or not x(1) +%% is initialized, we would need to analyze all code preceding these +%% two instructions (across branches). Since we currently don't have +%% any practical mechanism for doing that, we will have to +%% conservatively assume that the transformation is unsafe. move_allocates([{block,Bl0}|Is]) -> Bl = move_allocates_1(reverse(Bl0), []), @@ -216,27 +204,19 @@ move_allocates([I|Is]) -> [I|move_allocates(Is)]; move_allocates([]) -> []. -move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) -> - {Is,Acc} = move_allocates_2(Alloc, Is0, Acc0), - move_allocates_1(Is, Acc); +move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) -> + case {alloc_may_pass(I),alloc_live_regs(I, Live0)} of + {false,_} -> + move_allocates_1(Is, [I|Acc0]); + {true,not_possible} -> + move_allocates_1(Is, [I|Acc0]); + {true,Live} when is_integer(Live) -> + A = {set,[],[],{alloc,Live,Info}}, + move_allocates_1(Is, [A,I|Acc]) + end; move_allocates_1([I|Is], Acc) -> move_allocates_1(Is, [I|Acc]); -move_allocates_1([], Is) -> Is. - -move_allocates_2({alloc,Live,Info}, [{set,[],[],{alloc,Live0,Info0}}|Is], Acc) -> - Live = Live0, % Assertion. - Alloc = {alloc,Live,combine_alloc(Info0, Info)}, - move_allocates_2(Alloc, Is, Acc); -move_allocates_2({alloc,Live,Info}=Alloc0, [I|Is]=Is0, Acc) -> - case alloc_may_pass(I) of - false -> - {Is0,[{set,[],[],Alloc0}|Acc]}; - true -> - Alloc = {alloc,alloc_live_regs(I, Live),Info}, - move_allocates_2(Alloc, Is, [I|Acc]) - end; -move_allocates_2(Alloc, [], Acc) -> - {[],[{set,[],[],Alloc}|Acc]}. +move_allocates_1([], Acc) -> Acc. alloc_may_pass({set,_,_,{alloc,_,_}}) -> false; alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false; @@ -245,19 +225,9 @@ alloc_may_pass({set,_,_,put_list}) -> false; alloc_may_pass({set,_,_,put}) -> false; alloc_may_pass({set,_,_,_}) -> true. -combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) -> - {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}. - %% opt([Instruction]) -> [Instruction] %% Optimize the instruction stream inside a basic block. -opt([{set,[Dst],As,{bif,Bif,Fail}}=I1, - {set,[Dst],[Dst],{bif,'not',Fail}}=I2|Is]) -> - %% Get rid of the 'not' if the operation can be inverted. - case inverse_comp_op(Bif) of - none -> [I1,I2|opt(Is)]; - RevBif -> [{set,[Dst],As,{bif,RevBif,Fail}}|opt(Is)] - end; opt([{set,[X],[X],move}|Is]) -> opt(Is); opt([{set,_,_,{line,_}}=Line1, {set,[D1],[{integer,Idx1},Reg],{bif,element,{f,0}}}=I1, @@ -265,10 +235,12 @@ opt([{set,_,_,{line,_}}=Line1, {set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is]) when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg -> opt([Line2,I2,Line1,I1|Is]); -opt([{set,Ds0,Ss,Op}|Is0]) -> +opt([{set,[_|_],_Ss,{get_map_elements,_F}}=I|Is]) -> + [I|opt(Is)]; +opt([{set,Ds0,Ss,Op}|Is0]) -> {Ds,Is} = opt_moves(Ds0, Is0), [{set,Ds,Ss,Op}|opt(Is)]; -opt([{'%live',_}=I|Is]) -> +opt([{'%live',_,_}=I|Is]) -> [I|opt(Is)]; opt([]) -> []. @@ -291,78 +263,167 @@ opt_moves([X0,Y0], Is0) -> not_possible -> {[X,Y0],Is2}; {X,_} -> {[X,Y0],Is2}; {Y,Is} -> {[X,Y],Is} - end; -opt_moves(Ds, Is) -> - %% multiple destinations -> pass through - {Ds,Is}. - + end. %% opt_move(Dest, [Instruction]) -> {UpdatedDest,[Instruction]} | not_possible %% If there is a {move,Dest,FinalDest} instruction %% in the instruction stream, remove the move instruction %% and let FinalDest be the destination. -%% -%% For this optimization to be safe, we must be sure that -%% Dest will not be referenced in any other by other instructions -%% in the rest of the instruction stream. Not even the indirect -%% reference by an instruction that may allocate (such as -%% test_heap/2 or a GC Bif) is allowed. opt_move(Dest, Is) -> - opt_move_1(Dest, Is, ?MAXREG, []). - -opt_move_1(R, [{set,_,_,{alloc,Live,_}}|_]=Is, SafeRegs, Acc) when Live < SafeRegs -> - %% Downgrade number of safe regs and rescan the instruction, as it most probably - %% is a gc_bif instruction. - opt_move_1(R, Is, Live, Acc); -opt_move_1(R, [{set,[{x,X}=D],[R],move}|Is], SafeRegs, Acc) -> - case X < SafeRegs andalso beam_utils:is_killed_block(R, Is) of - true -> opt_move_2(D, Acc, Is); - false -> not_possible + opt_move_1(Dest, Is, []). + +opt_move_1(R, [{set,[D],[R],move}|Is0], Acc) -> + %% Provided that the source register is killed by instructions + %% that follow, the optimization is safe. + case eliminate_use_of_from_reg(Is0, R, D, []) of + {yes,Is} -> opt_move_rev(D, Acc, Is); + no -> not_possible end; -opt_move_1(R, [{set,[D],[R],move}|Is], _SafeRegs, Acc) -> - case beam_utils:is_killed_block(R, Is) of - true -> opt_move_2(D, Acc, Is); - false -> not_possible +opt_move_1(_R, [{set,_,_,{alloc,_,_}}|_], _) -> + %% The optimization is either not possible or not safe. + %% + %% If R is an X register killed by allocation, the optimization is + %% not safe. On the other hand, if the X register is killed, there + %% will not follow a 'move' instruction with this X register as + %% the source. + %% + %% If R is a Y register, the optimization is still not safe + %% because the new target register is an X register that cannot + %% safely pass the alloc instruction. + not_possible; +opt_move_1(R, [{set,_,_,_}=I|Is], Acc) -> + %% If the source register is either killed or used by this + %% instruction, the optimimization is not possible. + case is_killed_or_used(R, I) of + true -> not_possible; + false -> opt_move_1(R, Is, [I|Acc]) end; -opt_move_1(R, [I|Is], SafeRegs, Acc) -> - case is_transparent(R, I) of - false -> not_possible; - true -> opt_move_1(R, Is, SafeRegs, [I|Acc]) - end. +opt_move_1(_, _, _) -> + not_possible. + +%% opt_tuple_element([Instruction]) -> [Instruction] +%% If possible, move get_tuple_element instructions forward +%% in the instruction stream to a move instruction, eliminating +%% the move instruction. Example: +%% +%% get_tuple_element Tuple Pos Dst1 +%% ... +%% move Dst1 Dst2 +%% +%% This code may be possible to rewrite to: +%% +%% %%(Moved get_tuple_element instruction) +%% ... +%% get_tuple_element Tuple Pos Dst2 +%% -%% Reverse the instructions, while checking that there are no instructions that -%% would interfere with using the new destination register chosen. +opt_tuple_element([{set,[D],[S],{get_tuple_element,_}}=I|Is0]) -> + case opt_tuple_element_1(Is0, I, {S,D}, []) of + no -> + [I|opt_tuple_element(Is0)]; + {yes,Is} -> + opt_tuple_element(Is) + end; +opt_tuple_element([I|Is]) -> + [I|opt_tuple_element(Is)]; +opt_tuple_element([]) -> []. + +opt_tuple_element_1([{set,_,_,{alloc,_,_}}|_], _, _, _) -> + no; +opt_tuple_element_1([{set,_,_,{try_catch,_,_}}|_], _, _, _) -> + no; +opt_tuple_element_1([{set,[D],[S],move}|Is0], I0, {_,S}, Acc) -> + case eliminate_use_of_from_reg(Is0, S, D, []) of + no -> + no; + {yes,Is} -> + {set,[S],Ss,Op} = I0, + I = {set,[D],Ss,Op}, + {yes,reverse(Acc, [I|Is])} + end; +opt_tuple_element_1([{set,Ds,Ss,_}=I|Is], MovedI, {S,D}=Regs, Acc) -> + case member(S, Ds) orelse member(D, Ss) of + true -> + no; + false -> + opt_tuple_element_1(Is, MovedI, Regs, [I|Acc]) + end; +opt_tuple_element_1(_, _, _, _) -> no. -opt_move_2(D, [I|Is], Acc) -> - case is_transparent(D, I) of - false -> not_possible; - true -> opt_move_2(D, Is, [I|Acc]) +%% Reverse the instructions, while checking that there are no +%% instructions that would interfere with using the new destination +%% register (D). + +opt_move_rev(D, [I|Is], Acc) -> + case is_killed_or_used(D, I) of + true -> not_possible; + false -> opt_move_rev(D, Is, [I|Acc]) + end; +opt_move_rev(D, [], Acc) -> {D,Acc}. + +%% is_killed_or_used(Register, {set,_,_,_}) -> bool() +%% Test whether the register is used by the instruction. + +is_killed_or_used(R, {set,Ss,Ds,_}) -> + member(R, Ds) orelse member(R, Ss). + +%% eliminate_use_of_from_reg([Instruction], FromRegister, ToRegister, Acc) -> +%% {yes,Is} | no +%% Eliminate any use of FromRegister in the instruction sequence +%% by replacing uses of FromRegister with ToRegister. If FromRegister +%% is referenced by an allocation instruction, return 'no' to indicate +%% that FromRegister is still used and that the optimization is not +%% possible. + +eliminate_use_of_from_reg([{set,_,_,{alloc,Live,_}}|_]=Is0, {x,X}, _, Acc) -> + if + X < Live -> + no; + true -> + {yes,reverse(Acc, Is0)} end; -opt_move_2(D, [], Acc) -> {D,Acc}. - -%% is_transparent(Register, Instruction) -> true | false -%% Returns true if Instruction does not in any way references Register -%% (even indirectly by an allocation instruction). -%% Returns false if Instruction does reference Register, or we are -%% not sure. - -is_transparent({x,X}, {set,_,_,{alloc,Live,_}}) when X < Live -> - false; -is_transparent(R, {set,Ds,Ss,_Op}) -> - case member(R, Ds) of - true -> false; - false -> not member(R, Ss) +eliminate_use_of_from_reg([{set,Ds,Ss0,Op}=I0|Is], From, To, Acc) -> + I = case member(From, Ss0) of + true -> + Ss = [case S of + From -> To; + _ -> S + end || S <- Ss0], + {set,Ds,Ss,Op}; + false -> + I0 + end, + case member(From, Ds) of + true -> + {yes,reverse(Acc, [I|Is])}; + false -> + eliminate_use_of_from_reg(Is, From, To, [I|Acc]) end; -is_transparent(_, _) -> false. +eliminate_use_of_from_reg([I]=Is, From, _To, Acc) -> + case beam_utils:is_killed_block(From, [I]) of + true -> + {yes,reverse(Acc, Is)}; + false -> + no + end. %% opt_alloc(Instructions) -> Instructions' %% Optimises all allocate instructions. +opt_alloc([{set,[],[],{alloc,Live0,Info0}}, + {set,[],[],{alloc,Live,Info}}|Is]) -> + Live = Live0, %Assertion. + Alloc = combine_alloc(Info0, Info), + I = {set,[],[],{alloc,Live,Alloc}}, + opt_alloc([I|Is]); opt_alloc([{set,[],[],{alloc,R,{_,Ns,Nh,[]}}}|Is]) -> - [{set,[],[],opt_alloc(Is, Ns, Nh, R)}|opt(Is)]; + [{set,[],[],opt_alloc(Is, Ns, Nh, R)}|Is]; opt_alloc([I|Is]) -> [I|opt_alloc(Is)]; opt_alloc([]) -> []. + +combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) -> + {zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}. %% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr] %% Generates the optimal sequence of instructions for @@ -411,13 +472,14 @@ count_ones(Bits, Acc) -> alloc_live_regs({set,Ds,Ss,_}, Regs0) -> Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)), - live_regs(Rset). - -live_regs(Regs) -> - live_regs_1(0, Regs). + live_regs(0, Rset). -live_regs_1(N, 0) -> N; -live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1). +live_regs(N, 0) -> + N; +live_regs(N, Regs) when Regs band 1 =:= 1 -> + live_regs(N+1, Regs bsr 1); +live_regs(_, _) -> + not_possible. x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N))); x_dead([_|Rs], Regs) -> x_dead(Rs, Regs); @@ -426,246 +488,3 @@ x_dead([], Regs) -> Regs. x_live([{x,N}|Rs], Regs) -> x_live(Rs, Regs bor (1 bsl N)); x_live([_|Rs], Regs) -> x_live(Rs, Regs); x_live([], Regs) -> Regs. - -%% inverse_comp_op(Op) -> none|RevOp - -inverse_comp_op('=:=') -> '=/='; -inverse_comp_op('=/=') -> '=:='; -inverse_comp_op('==') -> '/='; -inverse_comp_op('/=') -> '=='; -inverse_comp_op('>') -> '=<'; -inverse_comp_op('<') -> '>='; -inverse_comp_op('>=') -> '<'; -inverse_comp_op('=<') -> '>'; -inverse_comp_op(_) -> none. - -%%% -%%% Evaluation of constant bit fields. -%%% - -is_bs_put({bs_put,_,{bs_put_integer,_,_},_}) -> true; -is_bs_put({bs_put,_,{bs_put_float,_,_},_}) -> true; -is_bs_put(_) -> false. - -collect_bs_puts(Is) -> - collect_bs_puts_1(Is, []). - -collect_bs_puts_1([I|Is]=Is0, Acc) -> - case is_bs_put(I) of - false -> {reverse(Acc),Is0}; - true -> collect_bs_puts_1(Is, [I|Acc]) - end. - -opt_bs_puts(Is) -> - opt_bs_1(Is, []). - -opt_bs_1([{bs_put,Fail, - {bs_put_float,1,Flags0},[{integer,Sz},Src]}=I0|Is], Acc) -> - try eval_put_float(Src, Sz, Flags0) of - <<Int:Sz>> -> - Flags = force_big(Flags0), - I = {bs_put,Fail,{bs_put_integer,1,Flags}, - [{integer,Sz},{integer,Int}]}, - opt_bs_1([I|Is], Acc) - catch - error:_ -> - opt_bs_1(Is, [I0|Acc]) - end; -opt_bs_1([{bs_put,_,{bs_put_integer,1,_},[{integer,8},{integer,_}]}|_]=IsAll, - Acc0) -> - {Is,Acc} = bs_collect_string(IsAll, Acc0), - opt_bs_1(Is, Acc); -opt_bs_1([{bs_put,Fail,{bs_put_integer,1,F},[{integer,Sz},{integer,N}]}=I|Is0], - Acc) when Sz > 8 -> - case field_endian(F) of - big -> - %% We can do this optimization for any field size without risk - %% for code explosion. - case bs_split_int(N, Sz, Fail, Is0) of - no_split -> opt_bs_1(Is0, [I|Acc]); - Is -> opt_bs_1(Is, Acc) - end; - little when Sz < 128 -> - %% We only try to optimize relatively small fields, to avoid - %% an explosion in code size. - <<Int:Sz>> = <<N:Sz/little>>, - Flags = force_big(F), - Is = [{bs_put,Fail,{bs_put_integer,1,Flags}, - [{integer,Sz},{integer,Int}]}|Is0], - opt_bs_1(Is, Acc); - _ -> %native or too wide little field - opt_bs_1(Is0, [I|Acc]) - end; -opt_bs_1([{bs_put,Fail,{Op,U,F},[{integer,Sz},Src]}|Is], Acc) when U > 1 -> - opt_bs_1([{bs_put,Fail,{Op,1,F},[{integer,U*Sz},Src]}|Is], Acc); -opt_bs_1([I|Is], Acc) -> - opt_bs_1(Is, [I|Acc]); -opt_bs_1([], Acc) -> reverse(Acc). - -eval_put_float(Src, Sz, Flags) when Sz =< 256 -> %Only evaluate if Sz is reasonable. - Val = value(Src), - case field_endian(Flags) of - little -> <<Val:Sz/little-float-unit:1>>; - big -> <<Val:Sz/big-float-unit:1>> - %% native intentionally not handled here - we can't optimize it. - end. - -value({integer,I}) -> I; -value({float,F}) -> F. - -bs_collect_string(Is, [{bs_put,_,{bs_put_string,Len,{string,Str}},[]}|Acc]) -> - bs_coll_str_1(Is, Len, reverse(Str), Acc); -bs_collect_string(Is, Acc) -> - bs_coll_str_1(Is, 0, [], Acc). - -bs_coll_str_1([{bs_put,_,{bs_put_integer,U,_},[{integer,Sz},{integer,V}]}|Is], - Len, StrAcc, IsAcc) when U*Sz =:= 8 -> - Byte = V band 16#FF, - bs_coll_str_1(Is, Len+1, [Byte|StrAcc], IsAcc); -bs_coll_str_1(Is, Len, StrAcc, IsAcc) -> - {Is,[{bs_put,{f,0},{bs_put_string,Len,{string,reverse(StrAcc)}},[]}|IsAcc]}. - -field_endian({field_flags,F}) -> field_endian_1(F). - -field_endian_1([big=E|_]) -> E; -field_endian_1([little=E|_]) -> E; -field_endian_1([native=E|_]) -> E; -field_endian_1([_|Fs]) -> field_endian_1(Fs). - -force_big({field_flags,F}) -> - {field_flags,force_big_1(F)}. - -force_big_1([big|_]=Fs) -> Fs; -force_big_1([little|Fs]) -> [big|Fs]; -force_big_1([F|Fs]) -> [F|force_big_1(Fs)]. - -bs_split_int(0, Sz, _, _) when Sz > 64 -> - %% We don't want to split in this case because the - %% string will consist of only zeroes. - no_split; -bs_split_int(-1, Sz, _, _) when Sz > 64 -> - %% We don't want to split in this case because the - %% string will consist of only 255 bytes. - no_split; -bs_split_int(N, Sz, Fail, Acc) -> - FirstByteSz = case Sz rem 8 of - 0 -> 8; - Rem -> Rem - end, - bs_split_int_1(N, FirstByteSz, Sz, Fail, Acc). - -bs_split_int_1(-1, _, Sz, Fail, Acc) when Sz > 64 -> - I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}}, - [{integer,Sz},{integer,-1}]}, - [I|Acc]; -bs_split_int_1(0, _, Sz, Fail, Acc) when Sz > 64 -> - I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}}, - [{integer,Sz},{integer,0}]}, - [I|Acc]; -bs_split_int_1(N, ByteSz, Sz, Fail, Acc) when Sz > 0 -> - Mask = (1 bsl ByteSz) - 1, - I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}}, - [{integer,ByteSz},{integer,N band Mask}]}, - bs_split_int_1(N bsr ByteSz, 8, Sz-ByteSz, Fail, [I|Acc]); -bs_split_int_1(_, _, _, _, Acc) -> Acc. - - -%%% -%%% Optimization of new bit syntax matching: get rid -%%% of redundant bs_restore2/2 instructions across select_val -%%% instructions, as well as a few other simple peep-hole optimizations. -%%% - -bsm_opt(Is0, Lc0) -> - {Is1,D0,Lc} = bsm_scan(Is0, [], Lc0, []), - Is2 = case D0 of - [] -> - Is1; - _ -> - D = gb_trees:from_orddict(orddict:from_list(D0)), - bsm_reroute(Is1, D, none, []) - end, - Is = beam_clean:bs_clean_saves(Is2), - {bsm_opt_2(Is, []),Lc}. - -bsm_scan([{label,L}=Lbl,{bs_restore2,_,Save}=R|Is], D0, Lc, Acc0) -> - D = [{{L,Save},Lc}|D0], - Acc = [{label,Lc},R,Lbl|Acc0], - bsm_scan(Is, D, Lc+1, Acc); -bsm_scan([I|Is], D, Lc, Acc) -> - bsm_scan(Is, D, Lc, [I|Acc]); -bsm_scan([], D, Lc, Acc) -> - {reverse(Acc),D,Lc}. - -bsm_reroute([{bs_save2,Reg,Save}=I|Is], D, _, Acc) -> - bsm_reroute(Is, D, {Reg,Save}, [I|Acc]); -bsm_reroute([{bs_restore2,Reg,Save}=I|Is], D, _, Acc) -> - bsm_reroute(Is, D, {Reg,Save}, [I|Acc]); -bsm_reroute([{label,_}=I|Is], D, S, Acc) -> - bsm_reroute(Is, D, S, [I|Acc]); -bsm_reroute([{select,select_val,Reg,F0,Lbls0}|Is], D, {_,Save}=S, Acc0) -> - [F|Lbls] = bsm_subst_labels([F0|Lbls0], Save, D), - Acc = [{select,select_val,Reg,F,Lbls}|Acc0], - bsm_reroute(Is, D, S, Acc); -bsm_reroute([{test,TestOp,F0,TestArgs}=I|Is], D, {_,Save}=S, Acc0) -> - F = bsm_subst_label(F0, Save, D), - Acc = [{test,TestOp,F,TestArgs}|Acc0], - case bsm_not_bs_test(I) of - true -> - %% The test instruction will not update the bit offset for the - %% binary being matched. Therefore the save position can be kept. - bsm_reroute(Is, D, S, Acc); - false -> - %% The test instruction might update the bit offset. Kill our - %% remembered Save position. - bsm_reroute(Is, D, none, Acc) - end; -bsm_reroute([{test,TestOp,F0,Live,TestArgs,Dst}|Is], D, {_,Save}, Acc0) -> - F = bsm_subst_label(F0, Save, D), - Acc = [{test,TestOp,F,Live,TestArgs,Dst}|Acc0], - %% The test instruction will update the bit offset. Kill our - %% remembered Save position. - bsm_reroute(Is, D, none, Acc); -bsm_reroute([{block,[{set,[],[],{alloc,_,_}}]}=Bl, - {bs_context_to_binary,_}=I|Is], D, S, Acc) -> - %% To help further bit syntax optimizations. - bsm_reroute([I,Bl|Is], D, S, Acc); -bsm_reroute([I|Is], D, _, Acc) -> - bsm_reroute(Is, D, none, [I|Acc]); -bsm_reroute([], _, _, Acc) -> reverse(Acc). - -bsm_opt_2([{test,bs_test_tail2,F,[Ctx,Bits]}|Is], - [{test,bs_skip_bits2,F,[Ctx,{integer,I},Unit,_Flags]}|Acc]) -> - bsm_opt_2(Is, [{test,bs_test_tail2,F,[Ctx,Bits+I*Unit]}|Acc]); -bsm_opt_2([{test,bs_skip_bits2,F,[Ctx,{integer,I1},Unit1,_]}|Is], - [{test,bs_skip_bits2,F,[Ctx,{integer,I2},Unit2,Flags]}|Acc]) -> - bsm_opt_2(Is, [{test,bs_skip_bits2,F, - [Ctx,{integer,I1*Unit1+I2*Unit2},1,Flags]}|Acc]); -bsm_opt_2([I|Is], Acc) -> - bsm_opt_2(Is, [I|Acc]); -bsm_opt_2([], Acc) -> reverse(Acc). - -%% bsm_not_bs_test({test,Name,_,Operands}) -> true|false. -%% Test whether is the test is a "safe", i.e. does not move the -%% bit offset for a binary. -%% -%% 'true' means that the test is safe, 'false' that we don't know or -%% that the test moves the offset (e.g. bs_get_integer2). - -bsm_not_bs_test({test,bs_test_tail2,_,[_,_]}) -> true; -bsm_not_bs_test(Test) -> beam_utils:is_pure_test(Test). - -bsm_subst_labels(Fs, Save, D) -> - bsm_subst_labels_1(Fs, Save, D, []). - -bsm_subst_labels_1([F|Fs], Save, D, Acc) -> - bsm_subst_labels_1(Fs, Save, D, [bsm_subst_label(F, Save, D)|Acc]); -bsm_subst_labels_1([], _, _, Acc) -> - reverse(Acc). - -bsm_subst_label({f,Lbl0}=F, Save, D) -> - case gb_trees:lookup({Lbl0,Save}, D) of - {value,Lbl} -> {f,Lbl}; - none -> F - end; -bsm_subst_label(Other, _, _) -> Other. diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl index a452d30b61..99e4ccb1e9 100644 --- a/lib/compiler/src/beam_bool.erl +++ b/lib/compiler/src/beam_bool.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -24,8 +25,6 @@ -import(lists, [reverse/1,reverse/2,foldl/3,mapfoldl/3,map/2]). --define(MAXREG, 1024). - -record(st, {next, %Next label number. ll %Live regs at labels. @@ -141,11 +140,6 @@ bopt_block(Reg, Fail, OldIs, [{block,Bl0}|Acc0], St0) -> throw:not_boolean_expr -> failed; - %% The block contains a 'move' instruction that could - %% not be handled. - throw:move -> - failed; - %% The optimization is not safe. (A register %% used by the instructions following the %% optimized code is either not assigned a @@ -214,37 +208,14 @@ ensure_opt_safe(Bl, NewCode, OldIs, Fail, PrecedingCode, St) -> false -> throw(all_registers_not_killed); true -> ok end, - Same = assigned_same_value(Bl, NewCode), MustBeUnused = ordsets:subtract(ordsets:union(NotSet, NewDst), - ordsets:union(MustBeKilled, Same)), + MustBeKilled), case none_used(MustBeUnused, OldIs, Fail, St) of false -> throw(registers_used); true -> ok end, ok. -%% assigned_same_value(OldCode, NewCodeReversed) -> [DestinationRegs] -%% Return an ordset with a list of all y registers that are always -%% assigned the same value in the old and new code. Currently, we -%% are very conservative in that we only consider identical move -%% instructions in the same order. -%% -assigned_same_value(Old, New) -> - case reverse(New) of - [{block,Bl}|_] -> - assigned_same_value(Old, Bl, []); - _ -> - ordsets:new() - end. - -assigned_same_value([{set,[{y,_}=D],[S],move}|T1], - [{set,[{y,_}=D],[S],move}|T2], Acc) -> - assigned_same_value(T1, T2, [D|Acc]); -assigned_same_value(_, _, Acc) -> - ordsets:from_list(Acc). - -update_fail_label([{set,_,_,move}=I|Is], Fail, Acc) -> - update_fail_label(Is, Fail, [I|Acc]); update_fail_label([{set,Ds,As,{bif,N,{f,_}}}|Is], Fail, Acc) -> update_fail_label(Is, Fail, [{set,Ds,As,{bif,N,{f,Fail}}}|Acc]); update_fail_label([{set,Ds,As,{alloc,Regs,{gc_bif,N,{f,_}}}}|Is], Fail, Acc) -> @@ -267,9 +238,9 @@ extend_block(BlAcc0, Fail, [{block,Is0}|OldAcc]) -> end; extend_block(BlAcc, _, OldAcc) -> {BlAcc,OldAcc}. -extend_block_1([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) -> +extend_block_1([{set,[{x,_}],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) -> extend_block_1(Is, Fail, [I|Acc]); -extend_block_1([{set,[_],As,{bif,Bif,_}}=I|Is]=Is0, Fail, Acc) -> +extend_block_1([{set,[{x,_}],As,{bif,Bif,_}}=I|Is]=Is0, Fail, Acc) -> case safe_bool_op(Bif, length(As)) of false -> {Acc,reverse(Is0)}; true -> extend_block_1(Is, Fail, [I|Acc]) @@ -313,8 +284,6 @@ split_block_1(Is, Fail, ProhibitFailLabel) -> end end. -split_block_2([{set,_,_,move}=I|Is], Fail, Acc) -> - split_block_2(Is, Fail, [I|Acc]); split_block_2([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) -> split_block_2(Is, Fail, [I|Acc]); split_block_2([{set,[_],_,{alloc,_,{gc_bif,_,{f,Fail}}}}=I|Is], Fail, Acc) -> @@ -342,8 +311,8 @@ dst_regs([{set,[D],_,{bif,_,{f,_}}}|Is], Acc) -> dst_regs(Is, [D|Acc]); dst_regs([{set,[D],_,{alloc,_,{gc_bif,_,{f,_}}}}|Is], Acc) -> dst_regs(Is, [D|Acc]); -dst_regs([{set,[D],_,move}|Is], Acc) -> - dst_regs(Is, [D|Acc]); +dst_regs([{protected,_,Bl,_}|Is], Acc) -> + dst_regs(Bl, dst_regs(Is, Acc)); dst_regs([_|Is], Acc) -> dst_regs(Is, Acc); dst_regs([], Acc) -> ordsets:from_list(Acc). @@ -410,13 +379,6 @@ bopt_tree([{protected,[Dst],Code,_}|Is], Forest0, Pre) -> _Res -> throw(not_boolean_expr) end; -bopt_tree([{set,[Dst],[Src],move}=Move|Is], Forest, Pre) -> - case {Src,Dst} of - {{tmp,_},_} -> throw(move); - {_,{tmp,_}} -> throw(move); - _ -> ok - end, - bopt_tree(Is, Forest, [Move|Pre]); bopt_tree([{set,[Dst],As,{bif,N,_}}=Bif|Is], Forest0, Pre) -> Ar = length(As), case safe_bool_op(N, Ar) of @@ -588,10 +550,6 @@ free_variables(Is) -> E = gb_sets:empty(), free_vars_1(Is, E, E, E). -free_vars_1([{set,Ds,As,move}|Is], F0, N0, A) -> - F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)), - N = gb_sets:union(N0, var_list(Ds)), - free_vars_1(Is, F, N, A); free_vars_1([{set,Ds,As,{bif,_,_}}|Is], F0, N0, A) -> F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)), N = gb_sets:union(N0, var_list(Ds)), @@ -631,8 +589,6 @@ free_vars_regs(X) -> [{x,X-1}|free_vars_regs(X-1)]. rename_regs(Is, Regs) -> rename_regs(Is, Regs, []). -rename_regs([{set,_,_,move}=I|Is], Regs, Acc) -> - rename_regs(Is, Regs, [I|Acc]); rename_regs([{set,[Dst0],Ss0,{alloc,_,Info}}|Is], Regs0, Acc) -> Live = live_regs(Regs0), Ss = rename_sources(Ss0, Regs0), @@ -736,8 +692,7 @@ ssa_assign({x,_}=R, #ssa{sub=Sub0}=Ssa0) -> Sub1 = gb_trees:update(R, NewReg, Sub0), Sub = gb_trees:insert(NewReg, NewReg, Sub1), Ssa#ssa{sub=Sub} - end; -ssa_assign(_, Ssa) -> Ssa. + end. ssa_sub_list(List, Sub) -> [ssa_sub(E, Sub) || E <- List]. @@ -787,6 +742,9 @@ is_not_used(R, Is, Label, #st{ll=Ll}) -> initialized_regs(Is) -> initialized_regs(Is, ordsets:new()). +initialized_regs([{set,Dst,_Src,{alloc,Live,_}}|_], Regs0) -> + Regs = add_init_regs(free_vars_regs(Live), Regs0), + add_init_regs(Dst, Regs); initialized_regs([{set,Dst,Src,_}|Is], Regs) -> initialized_regs(Is, add_init_regs(Dst, add_init_regs(Src, Regs))); initialized_regs([{test,_,_,Src}|Is], Regs) -> diff --git a/lib/compiler/src/beam_bs.erl b/lib/compiler/src/beam_bs.erl new file mode 100644 index 0000000000..2aed98d4e7 --- /dev/null +++ b/lib/compiler/src/beam_bs.erl @@ -0,0 +1,278 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2016. 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% +%% +%% Purpose : Partitions assembly instructions into basic blocks and +%% optimizes them. + +-module(beam_bs). + +-export([module/2]). +-import(lists, [mapfoldl/3,reverse/1]). + +module({Mod,Exp,Attr,Fs0,Lc0}, _Opt) -> + {Fs,Lc} = mapfoldl(fun function/2, Lc0, Fs0), + {ok,{Mod,Exp,Attr,Fs,Lc}}. + +function({function,Name,Arity,CLabel,Is0}, Lc0) -> + try + Is1 = bs_put_opt(Is0), + {Is,Lc} = bsm_opt(Is1, Lc0), + {{function,Name,Arity,CLabel,Is},Lc} + catch + Class:Error -> + Stack = erlang:get_stacktrace(), + io:fwrite("Function: ~w/~w\n", [Name,Arity]), + erlang:raise(Class, Error, Stack) + end. + +%%% +%%% Evaluation of constant bit fields. +%%% + +bs_put_opt([{bs_put,_,_,_}=I|Is0]) -> + {BsPuts0,Is} = collect_bs_puts(Is0, [I]), + BsPuts = opt_bs_puts(BsPuts0), + BsPuts ++ bs_put_opt(Is); +bs_put_opt([I|Is]) -> + [I|bs_put_opt(Is)]; +bs_put_opt([]) -> []. + +collect_bs_puts([{bs_put,_,_,_}=I|Is], Acc) -> + collect_bs_puts(Is, [I|Acc]); +collect_bs_puts([_|_]=Is, Acc) -> + {reverse(Acc),Is}. + +opt_bs_puts(Is) -> + opt_bs_1(Is, []). + +opt_bs_1([{bs_put,Fail, + {bs_put_float,1,Flags0},[{integer,Sz},Src]}=I0|Is], Acc) -> + try eval_put_float(Src, Sz, Flags0) of + <<Int:Sz>> -> + Flags = force_big(Flags0), + I = {bs_put,Fail,{bs_put_integer,1,Flags}, + [{integer,Sz},{integer,Int}]}, + opt_bs_1([I|Is], Acc) + catch + error:_ -> + opt_bs_1(Is, [I0|Acc]) + end; +opt_bs_1([{bs_put,_,{bs_put_integer,1,_},[{integer,8},{integer,_}]}|_]=IsAll, + Acc0) -> + {Is,Acc} = bs_collect_string(IsAll, Acc0), + opt_bs_1(Is, Acc); +opt_bs_1([{bs_put,Fail,{bs_put_integer,1,F},[{integer,Sz},{integer,N}]}=I|Is0], + Acc) when Sz > 8 -> + case field_endian(F) of + big -> + %% We can do this optimization for any field size without + %% risk for code explosion. + case bs_split_int(N, Sz, Fail, Is0) of + no_split -> opt_bs_1(Is0, [I|Acc]); + Is -> opt_bs_1(Is, Acc) + end; + little when Sz < 128 -> + %% We only try to optimize relatively small fields, to + %% avoid an explosion in code size. + <<Int:Sz>> = <<N:Sz/little>>, + Flags = force_big(F), + Is = [{bs_put,Fail,{bs_put_integer,1,Flags}, + [{integer,Sz},{integer,Int}]}|Is0], + opt_bs_1(Is, Acc); + _ -> %native or too wide little field + opt_bs_1(Is0, [I|Acc]) + end; +opt_bs_1([{bs_put,Fail,{Op,U,F},[{integer,Sz},Src]}|Is], Acc) when U > 1 -> + opt_bs_1([{bs_put,Fail,{Op,1,F},[{integer,U*Sz},Src]}|Is], Acc); +opt_bs_1([I|Is], Acc) -> + opt_bs_1(Is, [I|Acc]); +opt_bs_1([], Acc) -> reverse(Acc). + +eval_put_float(Src, Sz, Flags) when Sz =< 256 -> + %%Only evaluate if Sz is reasonable. + Val = value(Src), + case field_endian(Flags) of + little -> <<Val:Sz/little-float-unit:1>>; + big -> <<Val:Sz/big-float-unit:1>> + %% native intentionally not handled here - we can't optimize + %% it. + end. + +value({integer,I}) -> I; +value({float,F}) -> F. + +bs_collect_string(Is, [{bs_put,_,{bs_put_string,Len,{string,Str}},[]}|Acc]) -> + bs_coll_str_1(Is, Len, reverse(Str), Acc); +bs_collect_string(Is, Acc) -> + bs_coll_str_1(Is, 0, [], Acc). + +bs_coll_str_1([{bs_put,_,{bs_put_integer,U,_},[{integer,Sz},{integer,V}]}|Is], + Len, StrAcc, IsAcc) when U*Sz =:= 8 -> + Byte = V band 16#FF, + bs_coll_str_1(Is, Len+1, [Byte|StrAcc], IsAcc); +bs_coll_str_1(Is, Len, StrAcc, IsAcc) -> + {Is,[{bs_put,{f,0},{bs_put_string,Len,{string,reverse(StrAcc)}},[]}|IsAcc]}. + +field_endian({field_flags,F}) -> field_endian_1(F). + +field_endian_1([big=E|_]) -> E; +field_endian_1([little=E|_]) -> E; +field_endian_1([native=E|_]) -> E; +field_endian_1([_|Fs]) -> field_endian_1(Fs). + +force_big({field_flags,F}) -> + {field_flags,force_big_1(F)}. + +force_big_1([big|_]=Fs) -> Fs; +force_big_1([little|Fs]) -> [big|Fs]; +force_big_1([F|Fs]) -> [F|force_big_1(Fs)]. + +bs_split_int(0, Sz, _, _) when Sz > 64 -> + %% We don't want to split in this case because the + %% string will consist of only zeroes. + no_split; +bs_split_int(-1, Sz, _, _) when Sz > 64 -> + %% We don't want to split in this case because the + %% string will consist of only 255 bytes. + no_split; +bs_split_int(N, Sz, Fail, Acc) -> + FirstByteSz = case Sz rem 8 of + 0 -> 8; + Rem -> Rem + end, + bs_split_int_1(N, FirstByteSz, Sz, Fail, Acc). + +bs_split_int_1(-1, _, Sz, Fail, Acc) when Sz > 64 -> + I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}}, + [{integer,Sz},{integer,-1}]}, + [I|Acc]; +bs_split_int_1(0, _, Sz, Fail, Acc) when Sz > 64 -> + I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}}, + [{integer,Sz},{integer,0}]}, + [I|Acc]; +bs_split_int_1(N, ByteSz, Sz, Fail, Acc) when Sz > 0 -> + Mask = (1 bsl ByteSz) - 1, + I = {bs_put,Fail,{bs_put_integer,1,{field_flags,[big]}}, + [{integer,ByteSz},{integer,N band Mask}]}, + bs_split_int_1(N bsr ByteSz, 8, Sz-ByteSz, Fail, [I|Acc]); +bs_split_int_1(_, _, _, _, Acc) -> Acc. + +%%% +%%% Optimization of bit syntax matching: get rid +%%% of redundant bs_restore2/2 instructions across select_val +%%% instructions, as well as a few other simple peep-hole +%%% optimizations. +%%% + +bsm_opt(Is0, Lc0) -> + {Is1,D0,Lc} = bsm_scan(Is0, [], Lc0, []), + Is2 = case D0 of + [] -> + %% No bit syntax matching in this function. + Is1; + [_|_] -> + %% Optimize the bit syntax matching. + D = gb_trees:from_orddict(orddict:from_list(D0)), + bsm_reroute(Is1, D, none, []) + end, + Is = beam_clean:bs_clean_saves(Is2), + {bsm_opt_2(Is, []),Lc}. + +bsm_scan([{label,L}=Lbl,{bs_restore2,_,Save}=R|Is], D0, Lc, Acc0) -> + D = [{{L,Save},Lc}|D0], + Acc = [{label,Lc},R,Lbl|Acc0], + bsm_scan(Is, D, Lc+1, Acc); +bsm_scan([I|Is], D, Lc, Acc) -> + bsm_scan(Is, D, Lc, [I|Acc]); +bsm_scan([], D, Lc, Acc) -> + {reverse(Acc),D,Lc}. + +bsm_reroute([{bs_save2,Reg,Save}=I|Is], D, _, Acc) -> + bsm_reroute(Is, D, {Reg,Save}, [I|Acc]); +bsm_reroute([{bs_restore2,Reg,Save}=I|Is], D, _, Acc) -> + bsm_reroute(Is, D, {Reg,Save}, [I|Acc]); +bsm_reroute([{label,_}=I|Is], D, S, Acc) -> + bsm_reroute(Is, D, S, [I|Acc]); +bsm_reroute([{select,select_val,Reg,F0,Lbls0}|Is], D, {_,Save}=S, Acc0) -> + [F|Lbls] = bsm_subst_labels([F0|Lbls0], Save, D), + Acc = [{select,select_val,Reg,F,Lbls}|Acc0], + bsm_reroute(Is, D, S, Acc); +bsm_reroute([{test,TestOp,F0,TestArgs}=I|Is], D, {_,Save}=S, Acc0) -> + F = bsm_subst_label(F0, Save, D), + Acc = [{test,TestOp,F,TestArgs}|Acc0], + case bsm_not_bs_test(I) of + true -> + %% The test instruction will not update the bit offset for + %% the binary being matched. Therefore the save position + %% can be kept. + bsm_reroute(Is, D, S, Acc); + false -> + %% The test instruction might update the bit offset. Kill + %% our remembered Save position. + bsm_reroute(Is, D, none, Acc) + end; +bsm_reroute([{test,TestOp,F0,Live,TestArgs,Dst}|Is], D, {_,Save}, Acc0) -> + F = bsm_subst_label(F0, Save, D), + Acc = [{test,TestOp,F,Live,TestArgs,Dst}|Acc0], + %% The test instruction will update the bit offset. Kill our + %% remembered Save position. + bsm_reroute(Is, D, none, Acc); +bsm_reroute([{block,[{set,[],[],{alloc,_,_}}]}=Bl, + {bs_context_to_binary,_}=I|Is], D, S, Acc) -> + %% To help further bit syntax optimizations. + bsm_reroute([I,Bl|Is], D, S, Acc); +bsm_reroute([I|Is], D, _, Acc) -> + bsm_reroute(Is, D, none, [I|Acc]); +bsm_reroute([], _, _, Acc) -> reverse(Acc). + +bsm_opt_2([{test,bs_test_tail2,F,[Ctx,Bits]}|Is], + [{test,bs_skip_bits2,F,[Ctx,{integer,I},Unit,_Flags]}|Acc]) -> + bsm_opt_2(Is, [{test,bs_test_tail2,F,[Ctx,Bits+I*Unit]}|Acc]); +bsm_opt_2([{test,bs_skip_bits2,F,[Ctx,{integer,I1},Unit1,_]}|Is], + [{test,bs_skip_bits2,F,[Ctx,{integer,I2},Unit2,Flags]}|Acc]) -> + bsm_opt_2(Is, [{test,bs_skip_bits2,F, + [Ctx,{integer,I1*Unit1+I2*Unit2},1,Flags]}|Acc]); +bsm_opt_2([I|Is], Acc) -> + bsm_opt_2(Is, [I|Acc]); +bsm_opt_2([], Acc) -> reverse(Acc). + +%% bsm_not_bs_test({test,Name,_,Operands}) -> true|false. +%% Test whether is the test is a "safe", i.e. does not move the +%% bit offset for a binary. +%% +%% 'true' means that the test is safe, 'false' that we don't know or +%% that the test moves the offset (e.g. bs_get_integer2). + +bsm_not_bs_test({test,bs_test_tail2,_,[_,_]}) -> true; +bsm_not_bs_test(Test) -> beam_utils:is_pure_test(Test). + +bsm_subst_labels(Fs, Save, D) -> + bsm_subst_labels_1(Fs, Save, D, []). + +bsm_subst_labels_1([F|Fs], Save, D, Acc) -> + bsm_subst_labels_1(Fs, Save, D, [bsm_subst_label(F, Save, D)|Acc]); +bsm_subst_labels_1([], _, _, Acc) -> + reverse(Acc). + +bsm_subst_label({f,Lbl0}=F, Save, D) -> + case gb_trees:lookup({Lbl0,Save}, D) of + {value,Lbl} -> {f,Lbl}; + none -> F + end; +bsm_subst_label(Other, _, _) -> Other. diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl index d54c2a9fde..286307a4be 100644 --- a/lib/compiler/src/beam_bsm.erl +++ b/lib/compiler/src/beam_bsm.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -20,7 +21,7 @@ -module(beam_bsm). -export([module/2,format_error/1]). --import(lists, [member/2,foldl/3,reverse/1,sort/1,all/2,dropwhile/2]). +-import(lists, [member/2,foldl/3,reverse/1,sort/1,all/2]). %%% %%% We optimize bit syntax matching where the tail end of a binary is @@ -242,6 +243,12 @@ btb_reaches_match_2([{bif,_,{f,F},Ss,Dst}=I|Is], Regs0, D0) -> Regs = btb_kill([Dst], Regs0), D = btb_follow_branch(F, Regs, D0), btb_reaches_match_1(Is, Regs, D); +btb_reaches_match_2([{get_map_elements,{f,F},Src,{list,Ls}}=I|Is], Regs0, D0) -> + {Ss,Ds} = beam_utils:split_even(Ls), + btb_ensure_not_used([Src|Ss], I, Regs0), + Regs = btb_kill(Ds, Regs0), + D = btb_follow_branch(F, Regs, D0), + btb_reaches_match_1(Is, Regs, D); btb_reaches_match_2([{test,bs_start_match2,{f,F},Live,[Ctx,_],Ctx}=I|Is], Regs0, D0) -> CtxRegs = btb_context_regs(Regs0), @@ -414,7 +421,8 @@ btb_follow_branches([], _, D) -> D. btb_follow_branch(0, _Regs, D) -> D; btb_follow_branch(Lbl, Regs, #btb{ok_br=Br0,index=Li}=D) -> - case gb_sets:is_member(Lbl, Br0) of + Key = {Lbl,Regs}, + case gb_sets:is_member(Key, Br0) of true -> %% We have already followed this branch and it was OK. D; @@ -425,7 +433,7 @@ btb_follow_branch(Lbl, Regs, #btb{ok_br=Br0,index=Li}=D) -> btb_reaches_match_1(Is, Regs, D), %% Since we got back, this branch is OK. - D#btb{ok_br=gb_sets:insert(Lbl, Br),must_not_save=MustNotSave, + D#btb{ok_br=gb_sets:insert(Key, Br),must_not_save=MustNotSave, must_save=MustSave} end. @@ -542,16 +550,13 @@ btb_context_regs_1(Regs, N, Tag, Acc) -> %% a binary. MustSave is true if the function may pass the match %% context to the bs_context_to_binary instruction (in which case %% the current position in the binary must have saved into the -%% start position using "bs_save_2 Ctx start". +%% start position using "bs_save_2 Ctx start"). btb_index(Fs) -> btb_index_1(Fs, []). btb_index_1([{function,_,_,Entry,Is0}|Fs], Acc0) -> - [{label,Entry}|Is] = - dropwhile(fun({label,L}) when L =:= Entry -> false; - (_) -> true - end, Is0), + Is = drop_to_label(Is0, Entry), Acc = btb_index_2(Is, Entry, false, Acc0), btb_index_1(Fs, Acc); btb_index_1([], Acc) -> gb_trees:from_orddict(sort(Acc)). @@ -566,6 +571,9 @@ btb_index_2(Is0, Entry, _, Acc) -> throw:none -> Acc end. +drop_to_label([{label,L}|Is], L) -> Is; +drop_to_label([_|Is], L) -> drop_to_label(Is, L). + btb_index_find_start_match([{test,_,{f,F},_},{bs_context_to_binary,_}|Is]) -> btb_index_find_label(Is, F); btb_index_find_start_match(_) -> @@ -615,7 +623,7 @@ collect_warnings_instr([_|Is], D, Acc) -> collect_warnings_instr([], _, Acc) -> Acc. add_warning(Term, Anno, Ws) -> - Line = abs(get_line(Anno)), + Line = get_line(Anno), File = get_file(Anno), [{File,[{Line,?MODULE,Term}]}|Ws]. diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl index b653998252..10805a3c36 100644 --- a/lib/compiler/src/beam_clean.erl +++ b/lib/compiler/src/beam_clean.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -140,7 +141,7 @@ renumber_labels([{bif,is_record,{f,_}, renumber_labels(Is, Acc, St); renumber_labels([{test,is_record,{f,_}=Fail, [Term,{atom,Tag}=TagAtom,{integer,Arity}]}|Is0], Acc, St) -> - Tmp = {x,1023}, + Tmp = {x,1022}, Is = case is_record_tuple(Term, Tag, Arity) of yes -> Is0; @@ -184,30 +185,16 @@ function_replace([{function,Name,Arity,Entry,Asm0}|Fs], Dict, Acc) -> function_replace(Fs, Dict, [{function,Name,Arity,Entry,Asm}|Acc]); function_replace([], _, Acc) -> Acc. -replace([{test,bs_match_string=Op,{f,Lbl},[Ctx,Bin0]}|Is], Acc, D) -> - Bits = bit_size(Bin0), - Bin = case Bits rem 8 of - 0 -> Bin0; - Rem -> <<Bin0/bitstring,0:(8-Rem)>> - end, - I = {test,Op,{f,label(Lbl, D)},[Ctx,Bits,{string,binary_to_list(Bin)}]}, - replace(Is, [I|Acc], D); replace([{test,Test,{f,Lbl},Ops}|Is], Acc, D) -> replace(Is, [{test,Test,{f,label(Lbl, D)},Ops}|Acc], D); replace([{test,Test,{f,Lbl},Live,Ops,Dst}|Is], Acc, D) -> replace(Is, [{test,Test,{f,label(Lbl, D)},Live,Ops,Dst}|Acc], D); replace([{select,I,R,{f,Fail0},Vls0}|Is], Acc, D) -> - Vls1 = map(fun ({f,L}) -> {f,label(L, D)}; - (Other) -> Other end, Vls0), + Vls = map(fun ({f,L}) -> {f,label(L, D)}; + (Other) -> Other + end, Vls0), Fail = label(Fail0, D), - case redundant_values(Vls1, Fail, []) of - [] -> - %% Oops, no choices left. The loader will not accept that. - %% Convert to a plain jump. - replace(Is, [{jump,{f,Fail}}|Acc], D); - Vls -> - replace(Is, [{select,I,R,{f,Fail},Vls}|Acc], D) - end; + replace(Is, [{select,I,R,{f,Fail},Vls}|Acc], D); replace([{'try',R,{f,Lbl}}|Is], Acc, D) -> replace(Is, [{'try',R,{f,label(Lbl, D)}}|Acc], D); replace([{'catch',R,{f,Lbl}}|Is], Acc, D) -> @@ -234,31 +221,6 @@ replace([{bs_init,{f,Lbl},Info,Live,Ss,Dst}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{bs_init,{f,label(Lbl, D)},Info,Live,Ss,Dst}|Acc], D); replace([{bs_put,{f,Lbl},Info,Ss}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{bs_put,{f,label(Lbl, D)},Info,Ss}|Acc], D); -replace([{bs_init2,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_init2,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D); -replace([{bs_init_bits,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_init_bits,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D); -replace([{bs_put_integer,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_put_integer,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D); -replace([{bs_put_utf8=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D); -replace([{bs_put_utf16=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D); -replace([{bs_put_utf32=I,{f,Lbl},Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Fl,Val}|Acc], D); -replace([{bs_put_binary,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_put_binary,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D); -replace([{bs_put_float,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_put_float,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D); -replace([{bs_add,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{bs_add,{f,label(Lbl, D)},Src,Dst}|Acc], D); -replace([{bs_append,{f,Lbl},_,_,_,_,_,_,_}=I0|Is], Acc, D) when Lbl =/= 0 -> - I = setelement(2, I0, {f,label(Lbl, D)}), - replace(Is, [I|Acc], D); -replace([{bs_utf8_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D); -replace([{bs_utf16_size=I,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 -> - replace(Is, [{I,{f,label(Lbl, D)},Src,Dst}|Acc], D); replace([{put_map=I,{f,Lbl},Op,Src,Dst,Live,List}|Is], Acc, D) when Lbl =/= 0 -> replace(Is, [{I,{f,label(Lbl, D)},Op,Src,Dst,Live,List}|Acc], D); @@ -273,12 +235,6 @@ label(Old, D) -> {value,Val} -> Val; none -> throw({error,{undefined_label,Old}}) end. - -redundant_values([_,{f,Fail}|Vls], Fail, Acc) -> - redundant_values(Vls, Fail, Acc); -redundant_values([Val,Lbl|Vls], Fail, Acc) -> - redundant_values(Vls, Fail, [Lbl,Val|Acc]); -redundant_values([], _, Acc) -> reverse(Acc). %%% %%% Final fixup of bs_start_match2/5,bs_save2/bs_restore2 instructions for diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl index b15adfa889..6f6d742293 100644 --- a/lib/compiler/src/beam_dead.erl +++ b/lib/compiler/src/beam_dead.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2013. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -21,112 +22,10 @@ -export([module/2]). -%%% The following optimisations are done: -%%% -%%% (1) In this code -%%% -%%% move DeadValue {x,0} -%%% jump L2 -%%% . -%%% . -%%% . -%%% L2: move Anything {x,0} -%%% . -%%% . -%%% . -%%% -%%% the first assignment to {x,0} has no effect (is dead), -%%% so it can be removed. Besides removing a move instruction, -%%% if the move was preceeded by a label, the resulting code -%%% will look this -%%% -%%% L1: jump L2 -%%% . -%%% . -%%% . -%%% L2: move Anything {x,0} -%%% . -%%% . -%%% . -%%% -%%% which can be further optimized by the jump optimizer (beam_jump). -%%% -%%% (2) In this code -%%% -%%% L1: move AtomLiteral {x,0} -%%% jump L2 -%%% . -%%% . -%%% . -%%% L2: test is_atom FailLabel {x,0} -%%% select_val {x,0}, FailLabel [... AtomLiteral => L3...] -%%% . -%%% . -%%% . -%%% L3: ... -%%% -%%% FailLabel: ... -%%% -%%% the first code fragment can be changed to -%%% -%%% L1: move AtomLiteral {x,0} -%%% jump L3 -%%% -%%% If the literal is not included in the table of literals in the -%%% select_val instruction, the first code fragment will instead be -%%% rewritten as: -%%% -%%% L1: move AtomLiteral {x,0} -%%% jump FailLabel -%%% -%%% The move instruction will be removed by optimization (1) above, -%%% if the code following the L3 label overwrites {x,0}. -%%% -%%% The code following the L2 label will be kept, but it will be removed later -%%% by the jump optimizer. -%%% -%%% (3) In this code -%%% -%%% test is_eq_exact ALabel Src Dst -%%% move Src Dst -%%% -%%% the move instruction can be removed. -%%% Same thing for -%%% -%%% test is_nil ALabel Dst -%%% move [] Dst -%%% -%%% -%%% (4) In this code -%%% -%%% select_val {x,Reg}, ALabel [... Literal => L1...] -%%% . -%%% . -%%% . -%%% L1: move Literal {x,Reg} -%%% -%%% we can remove the move instruction. -%%% -%%% (5) In the following code -%%% -%%% bif '=:=' Fail Src1 Src2 {x,0} -%%% jump L1 -%%% . -%%% . -%%% . -%%% L1: select_val {x,0}, ALabel [... true => L2..., ...false => L3...] -%%% . -%%% . -%%% . -%%% L2: .... L3: .... -%%% -%%% the first two instructions can be replaced with -%%% -%%% test is_eq_exact L3 Src1 Src2 -%%% jump L2 -%%% -%%% provided that {x,0} is killed at both L2 and L3. -%%% +%%% Dead code is code that is executed but has no effect. This +%%% optimization pass either removes dead code or jumps around it, +%%% potentially making it unreachable and a target for the +%%% the beam_jump pass. -import(lists, [mapfoldl/3,reverse/1]). @@ -173,12 +72,39 @@ move_move_into_block([I|Is], Acc) -> move_move_into_block([], Acc) -> reverse(Acc). %%% -%%% Scan instructions in execution order and remove dead code. +%%% Scan instructions in execution order and remove redundant 'move' +%%% instructions. 'move' instructions are redundant if we know that +%%% the register already contains the value being assigned, as in the +%%% following code: +%%% +%%% test is_eq_exact SomeLabel Src Dst +%%% move Src Dst +%%% +%%% or in: +%%% +%%% test is_nil SomeLabel Dst +%%% move nil Dst +%%% +%%% or in: +%%% +%%% select_val Register FailLabel [... Literal => L1...] +%%% . +%%% . +%%% . +%%% L1: move Literal Register +%%% +%%% Also add extra labels to help the second backward pass. %%% forward(Is, Lc) -> - forward(Is, gb_trees:empty(), Lc, []). - + forward(Is, #{}, Lc, []). + +forward([{move,_,_}=Move|[{label,L}|_]=Is], D, Lc, Acc) -> + %% move/2 followed by jump/1 is optimized by backward/3. + forward([Move,{jump,{f,L}}|Is], D, Lc, Acc); +forward([{bif,_,_,_,_}=Bif|[{label,L}|_]=Is], D, Lc, Acc) -> + %% bif/4 followed by jump/1 is optimized by backward/3. + forward([Bif,{jump,{f,L}}|Is], D, Lc, Acc); forward([{block,[]}|Is], D, Lc, Acc) -> %% Empty blocks can prevent optimizations. forward(Is, D, Lc, Acc); @@ -190,21 +116,24 @@ forward([{label,Lbl}=LblI,{block,[{set,[Dst],[Lit],move}|BlkIs]}=Blk|Is], D, Lc, %% cannot be reached in any other way than through the select_val/3 %% instruction (i.e. there can be no fallthrough to such label and %% it cannot be referenced by, for example, a jump/1 instruction). - Block = case gb_trees:lookup({Lbl,Dst}, D) of - {value,Lit} -> {block,BlkIs}; %Safe to remove move instruction. - _ -> Blk %Must keep move instruction. - end, + Key = {Lbl,Dst}, + Block = case D of + #{Key := Lit} -> {block,BlkIs}; %Safe to remove move instruction. + _ -> Blk %Must keep move instruction. + end, forward([Block|Is], D, Lc, [LblI|Acc]); forward([{label,Lbl}=LblI|[{move,Lit,Dst}|Is1]=Is0], D, Lc, Acc) -> %% Assumption: The target labels in a select_val/3 instruction %% cannot be reached in any other way than through the select_val/3 %% instruction (i.e. there can be no fallthrough to such label and %% it cannot be referenced by, for example, a jump/1 instruction). - Is = case gb_trees:lookup({Lbl,Dst}, D) of - {value,Lit} -> Is1; %Safe to remove move instruction. - _ -> Is0 %Keep move instruction. + Is = case maps:find({Lbl,Dst}, D) of + {ok,Lit} -> Is1; %Safe to remove move instruction. + _ -> Is0 %Keep move instruction. end, forward(Is, D, Lc, [LblI|Acc]); +forward([{test,is_eq_exact,_,[Same,Same]}|Is], D, Lc, Acc) -> + forward(Is, D, Lc, Acc); forward([{test,is_eq_exact,_,[Dst,Src]}=I, {block,[{set,[Dst],[Src],move}|Bl]}|Is], D, Lc, Acc) -> forward([I,{block,Bl}|Is], D, Lc, Acc); @@ -215,15 +144,13 @@ forward([{test,is_eq_exact,_,[Dst,Src]}=I,{move,Src,Dst}|Is], D, Lc, Acc) -> forward([I|Is], D, Lc, Acc); forward([{test,is_nil,_,[Dst]}=I,{move,nil,Dst}|Is], D, Lc, Acc) -> forward([I|Is], D, Lc, Acc); -forward([{test,is_eq_exact,_,_}=I|Is], D, Lc, Acc) -> - case Is of - [{label,_}|_] -> forward(Is, D, Lc, [I|Acc]); - _ -> forward(Is, D, Lc+1, [{label,Lc},I|Acc]) - end; -forward([{test,is_ne_exact,_,_}=I|Is], D, Lc, Acc) -> - case Is of - [{label,_}|_] -> forward(Is, D, Lc, [I|Acc]); - _ -> forward(Is, D, Lc+1, [{label,Lc},I|Acc]) +forward([{test,_,_,_}=I|Is]=Is0, D, Lc, Acc) -> + %% Help the second, backward pass to by inserting labels after + %% relational operators so that they can be skipped if they are + %% known to be true. + case useful_to_insert_label(Is0) of + false -> forward(Is, D, Lc, [I|Acc]); + true -> forward(Is, D, Lc+1, [{label,Lc},I|Acc]) end; forward([I|Is], D, Lc, Acc) -> forward(Is, D, Lc, [I|Acc]); @@ -231,17 +158,57 @@ forward([], _, Lc, Acc) -> {Acc,Lc}. update_value_dict([Lit,{f,Lbl}|T], Reg, D0) -> Key = {Lbl,Reg}, - D = case gb_trees:lookup(Key, D0) of - none -> gb_trees:insert(Key, Lit, D0); %New. - {value,inconsistent} -> D0; %Inconsistent. - {value,_} -> gb_trees:update(Key, inconsistent, D0) - end, + D = case D0 of + #{Key := inconsistent} -> D0; + #{Key := _} -> D0#{Key := inconsistent}; + _ -> D0#{Key => Lit} + end, update_value_dict(T, Reg, D); update_value_dict([], _, D) -> D. +useful_to_insert_label([_,{label,_}|_]) -> + false; +useful_to_insert_label([{test,Op,_,_}|_]) -> + case Op of + is_lt -> true; + is_ge -> true; + is_eq_exact -> true; + is_ne_exact -> true; + _ -> false + end. + +%%% +%%% Scan instructions in reverse execution order and try to +%%% shortcut branch instructions. +%%% +%%% For example, in this code: +%%% +%%% move Literal Register +%%% jump L1 +%%% . +%%% . +%%% . +%%% L1: test is_{integer,atom} FailLabel Register +%%% select_val {x,0} FailLabel [... Literal => L2...] +%%% . +%%% . +%%% . +%%% L2: ... %%% -%%% Scan instructions in reverse execution order and remove dead code. +%%% the 'selectval' instruction will always transfer control to L2, +%%% so we can just as well jump to L2 directly by rewriting the +%%% first part of the sequence like this: %%% +%%% move Literal Register +%%% jump L2 +%%% +%%% If register Register is killed at label L2, we can remove the +%%% 'move' instruction, leaving just the 'jump' instruction: +%%% +%%% jump L2 +%%% +%%% These transformations may leave parts of the code unreachable. +%%% The beam_jump pass will remove the unreachable code. backward(Is, D) -> backward(Is, D, []). @@ -272,22 +239,30 @@ backward([{test,is_eq_exact,Fail,[Dst,{integer,Arity}]}=I| backward([{label,Lbl}=L|Is], D, Acc) -> backward(Is, beam_utils:index_label(Lbl, Acc, D), [L|Acc]); backward([{select,select_val,Reg,{f,Fail0},List0}|Is], D, Acc) -> - List = shortcut_select_list(List0, Reg, D, []), + List1 = shortcut_select_list(List0, Reg, D, []), Fail1 = shortcut_label(Fail0, D), Fail = shortcut_bs_test(Fail1, Is, D), - Sel = {select,select_val,Reg,{f,Fail},List}, - backward(Is, D, [Sel|Acc]); -backward([{jump,{f,To0}},{move,Src,Reg}=Move0|Is], D, Acc) -> - {To,Move} = case Src of - {atom,Val0} -> - To1 = shortcut_select_label(To0, Reg, Val0, D), - {To2,Val} = shortcut_boolean_label(To1, Reg, Val0, D), - {To2,{move,{atom,Val},Reg}}; - _ -> - {shortcut_label(To0, D),Move0} - end, + List = prune_redundant(List1, Fail), + case List of + [] -> + Jump = {jump,{f,Fail}}, + backward([Jump|Is], D, Acc); + [V,F] -> + Test = {test,is_eq_exact,{f,Fail},[Reg,V]}, + Jump = {jump,F}, + backward([Jump,Test|Is], D, Acc); + [{atom,B1},F,{atom,B2},F] when B1 =:= not B2 -> + Test = {test,is_boolean,{f,Fail},[Reg]}, + Jump = {jump,F}, + backward([Jump,Test|Is], D, Acc); + [_|_] -> + Sel = {select,select_val,Reg,{f,Fail},List}, + backward(Is, D, [Sel|Acc]) + end; +backward([{jump,{f,To0}},{move,Src,Reg}=Move|Is], D, Acc) -> + To = shortcut_select_label(To0, Reg, Src, D), Jump = {jump,{f,To}}, - case beam_utils:is_killed_at(Reg, To, D) of + case is_killed_at(Reg, To, D) of false -> backward([Move|Is], D, [Jump|Acc]); true -> backward([Jump|Is], D, Acc) end; @@ -297,38 +272,66 @@ backward([{jump,{f,To}}=J|[{bif,Op,_,Ops,Reg}|Is]=Is0], D, Acc) -> catch throw:not_possible -> backward(Is0, D, [J|Acc]) end; +backward([{test,bs_start_match2,F,_,[R,_],Ctxt}=I|Is], D, + [{test,bs_match_string,F,[Ctxt,Bs]}, + {test,bs_test_tail2,F,[Ctxt,0]}|Acc0]=Acc) -> + case beam_utils:is_killed(Ctxt, Acc0, D) of + true -> + Eq = {test,is_eq_exact,F,[R,{literal,Bs}]}, + backward(Is, D, [Eq|Acc0]); + false -> + backward(Is, D, [I|Acc]) + end; backward([{test,bs_start_match2,{f,To0},Live,[Src|_]=Info,Dst}|Is], D, Acc) -> To = shortcut_bs_start_match(To0, Src, D), I = {test,bs_start_match2,{f,To},Live,Info,Dst}, backward(Is, D, [I|Acc]); -backward([{test,is_eq_exact,{f,To0},[Reg,{atom,Val}]=Ops}|Is], D, Acc) -> - To1 = shortcut_bs_test(To0, Is, D), - To = shortcut_fail_label(To1, Reg, Val, D), - I = combine_eqs(To, Ops, D, Acc), - backward(Is, D, [I|Acc]); backward([{test,Op,{f,To0},Ops0}|Is], D, Acc) -> To1 = shortcut_bs_test(To0, Is, D), To2 = shortcut_label(To1, D), + To3 = shortcut_rel_op(To2, Op, Ops0, D), + %% Try to shortcut a repeated test: %% %% test Op {f,Fail1} Operands test Op {f,Fail2} Operands %% . . . ==> ... %% Fail1: test Op {f,Fail2} Operands Fail1: test Op {f,Fail2} Operands %% - To = case beam_utils:code_at(To2, D) of - [{test,Op,{f,To3},Ops}|_] -> + To = case beam_utils:code_at(To3, D) of + [{test,Op,{f,To4},Ops}|_] -> case equal_ops(Ops0, Ops) of - true -> To3; - false -> To2 + true -> To4; + false -> To3 end; _Code -> - To2 + To3 end, I = case Op of is_eq_exact -> combine_eqs(To, Ops0, D, Acc); _ -> {test,Op,{f,To},Ops0} end, - backward(Is, D, [I|Acc]); + case {I,Acc} of + {{test,is_atom,Fail,Ops0},[{test,is_boolean,Fail,Ops0}|_]} -> + %% An is_atom test before an is_boolean test (with the + %% same failure label) is redundant. + backward(Is, D, Acc); + {{test,is_atom,Fail,[R]}, + [{test,is_eq_exact,Fail,[R,{atom,_}]}|_]} -> + %% An is_atom test before a comparison with an atom (with + %% the same failure label) is redundant. + backward(Is, D, Acc); + {{test,is_integer,Fail,[R]}, + [{test,is_eq_exact,Fail,[R,{integer,_}]}|_]} -> + %% An is_integer test before a comparison with an integer + %% (with the same failure label) is redundant. + backward(Is, D, Acc); + {{test,_,_,_},_} -> + %% Still a test instruction. Done. + backward(Is, D, [I|Acc]); + {_,_} -> + %% Rewritten to a select_val. Rescan. + backward([I|Is], D, Acc) + end; backward([{test,Op,{f,To0},Live,Ops0,Dst}|Is], D, Acc) -> To1 = shortcut_bs_test(To0, Is, D), To2 = shortcut_label(To1, D), @@ -367,8 +370,8 @@ equal_ops([Op|T0], [Op|T1]) -> equal_ops([], []) -> true; equal_ops(_, _) -> false. -shortcut_select_list([{_,Val}=Lit,{f,To0}|T], Reg, D, Acc) -> - To = shortcut_select_label(To0, Reg, Val, D), +shortcut_select_list([Lit,{f,To0}|T], Reg, D, Acc) -> + To = shortcut_select_label(To0, Reg, Lit, D), shortcut_select_list(T, Reg, D, [{f,To},Lit|Acc]); shortcut_select_list([], _, _, Acc) -> reverse(Acc). @@ -378,58 +381,35 @@ shortcut_label(To0, D) -> _ -> To0 end. -shortcut_select_label(To0, Reg, Val, D) -> - case beam_utils:code_at(To0, D) of - [{jump,{f,To}}|_] -> - shortcut_select_label(To, Reg, Val, D); - [{test,is_atom,_,[Reg]},{select,select_val,Reg,{f,Fail},Map}|_] -> - To = find_select_val(Map, Val, Fail), - shortcut_select_label(To, Reg, Val, D); - [{test,is_eq_exact,{f,_},[Reg,{atom,Val}]},{label,To}|_] when is_atom(Val) -> - shortcut_select_label(To, Reg, Val, D); - [{test,is_eq_exact,{f,_},[Reg,{atom,Val}]},{jump,{f,To}}|_] when is_atom(Val) -> - shortcut_select_label(To, Reg, Val, D); - [{test,is_eq_exact,{f,To},[Reg,{atom,AnotherVal}]}|_] - when is_atom(Val), Val =/= AnotherVal -> - shortcut_select_label(To, Reg, Val, D); - [{test,is_ne_exact,{f,To},[Reg,{atom,Val}]}|_] when is_atom(Val) -> - shortcut_select_label(To, Reg, Val, D); - [{test,is_ne_exact,{f,_},[Reg,{atom,_}]},{label,To}|_] when is_atom(Val) -> - shortcut_select_label(To, Reg, Val, D); - [{test,is_tuple,{f,To},[Reg]}|_] when is_atom(Val) -> - shortcut_select_label(To, Reg, Val, D); - _ -> - To0 - end. +shortcut_select_label(To, Reg, Lit, D) -> + shortcut_rel_op(To, is_ne_exact, [Reg,Lit], D). -shortcut_fail_label(To0, Reg, Val, D) -> - case beam_utils:code_at(To0, D) of - [{jump,{f,To}}|_] -> - shortcut_fail_label(To, Reg, Val, D); - [{test,is_eq_exact,{f,To},[Reg,{atom,Val}]}|_] when is_atom(Val) -> - shortcut_fail_label(To, Reg, Val, D); - _ -> - To0 - end. +prune_redundant([_,{f,Fail}|T], Fail) -> + prune_redundant(T, Fail); +prune_redundant([V,F|T], Fail) -> + [V,F|prune_redundant(T, Fail)]; +prune_redundant([], _) -> []. -shortcut_boolean_label(To0, Reg, Bool0, D) when is_boolean(Bool0) -> - case beam_utils:code_at(To0, D) of - [{line,_},{bif,'not',_,[Reg],Reg},{jump,{f,To}}|_] -> - Bool = not Bool0, - {shortcut_select_label(To, Reg, Bool, D),Bool}; - _ -> - {To0,Bool0} - end; -shortcut_boolean_label(To, _, Bool, _) -> {To,Bool}. - -find_select_val([{_,Val},{f,To}|_], Val, _) -> To; -find_select_val([{_,_}, {f,_}|T], Val, Fail) -> - find_select_val(T, Val, Fail); -find_select_val([], _, Fail) -> Fail. +%% Replace a comparison operator with a test instruction and a jump. +%% For example, if we have this code: +%% +%% bif '=:=' Fail Src1 Src2 {x,0} +%% jump L1 +%% . +%% . +%% . +%% L1: select_val {x,0} FailLabel [... true => L2..., ...false => L3...] +%% +%% the first two instructions can be replaced with +%% +%% test is_eq_exact L3 Src1 Src2 +%% jump L2 +%% +%% provided that {x,0} is killed at both L2 and L3. replace_comp_op(To, Reg, Op, Ops, D) -> - False = comp_op_find_shortcut(To, Reg, false, D), - True = comp_op_find_shortcut(To, Reg, true, D), + False = comp_op_find_shortcut(To, Reg, {atom,false}, D), + True = comp_op_find_shortcut(To, Reg, {atom,true}, D), [bif_to_test(Op, Ops, False),{jump,{f,True}}]. comp_op_find_shortcut(To0, Reg, Val, D) -> @@ -437,7 +417,7 @@ comp_op_find_shortcut(To0, Reg, Val, D) -> To0 -> not_possible(); To -> - case beam_utils:is_killed_at(Reg, To, D) of + case is_killed_at(Reg, To, D) of false -> not_possible(); true -> To end @@ -461,9 +441,9 @@ not_possible() -> throw(not_possible). %% %% is_eq_exact F1 Reg Lit1 select_val Reg F2 [ Lit1 L1 %% L1: . Lit2 L2 ] -%% . -%% . ==> -%% . +%% . +%% . ==> +%% . %% F1: is_eq_exact F2 Reg Lit2 F1: is_eq_exact F2 Reg Lit2 %% L2: .... L2: %% @@ -488,31 +468,26 @@ remove_from_list(Lit, [Val,{f,_}=Fail|T]) -> [Val,Fail|remove_from_list(Lit, T)]; remove_from_list(_, []) -> []. -%% shortcut_bs_test(TargetLabel, [Instruction], D) -> TargetLabel' -%% Try to shortcut the failure label for a bit syntax matching. -%% We know that the binary contains at least Bits bits after -%% the latest save point. +%% shortcut_bs_test(TargetLabel, ReversedInstructions, D) -> TargetLabel' +%% Try to shortcut the failure label for bit syntax matching. shortcut_bs_test(To, Is, D) -> shortcut_bs_test_1(beam_utils:code_at(To, D), Is, To, D). -shortcut_bs_test_1([{bs_restore2,Reg,SavePoint}|Is], PrevIs, To, D) -> - shortcut_bs_test_2(Is, {Reg,SavePoint}, PrevIs, To, D); -shortcut_bs_test_1([_|_], _, To, _) -> To. - -shortcut_bs_test_2([{label,_}|Is], Save, PrevIs, To, D) -> - shortcut_bs_test_2(Is, Save, PrevIs, To, D); -shortcut_bs_test_2([{test,bs_test_tail2,{f,To},[_,TailBits]}|_], - {Reg,_Point} = RP, PrevIs, To0, D) -> - case count_bits_matched(PrevIs, RP, 0) of +shortcut_bs_test_1([{bs_restore2,Reg,SavePoint}, + {label,_}, + {test,bs_test_tail2,{f,To},[_,TailBits]}|_], + PrevIs, To0, D) -> + case count_bits_matched(PrevIs, {Reg,SavePoint}, 0) of Bits when Bits > TailBits -> %% This instruction will fail. We know because a restore has been - %% done from the previous point SavePoint in the binary, and we also know - %% that the binary contains at least Bits bits from SavePoint. + %% done from the previous point SavePoint in the binary, and we + %% also know that the binary contains at least Bits bits from + %% SavePoint. %% %% Since we will skip a bs_restore2 if we shortcut to label To, - %% we must now make sure that code at To does not depend on the position - %% in the context in any way. + %% we must now make sure that code at To does not depend on + %% the position in the context in any way. case shortcut_bs_pos_used(To, Reg, D) of false -> To; true -> To0 @@ -520,15 +495,26 @@ shortcut_bs_test_2([{test,bs_test_tail2,{f,To},[_,TailBits]}|_], _Bits -> To0 end; -shortcut_bs_test_2([_|_], _, _, To, _) -> To. +shortcut_bs_test_1([_|_], _, To, _) -> To. +%% counts_bits_matched(ReversedInstructions, SavePoint, Bits) -> Bits' +%% Given a reversed instruction stream, determine the minimum number +%% of bits that will be matched by bit syntax instructions up to the +%% given save point. + +count_bits_matched([{test,bs_get_utf8,{f,_},_,_,_}|Is], SavePoint, Bits) -> + count_bits_matched(Is, SavePoint, Bits+8); +count_bits_matched([{test,bs_get_utf16,{f,_},_,_,_}|Is], SavePoint, Bits) -> + count_bits_matched(Is, SavePoint, Bits+16); +count_bits_matched([{test,bs_get_utf32,{f,_},_,_,_}|Is], SavePoint, Bits) -> + count_bits_matched(Is, SavePoint, Bits+32); count_bits_matched([{test,_,_,_,[_,Sz,U,{field_flags,_}],_}|Is], SavePoint, Bits) -> case Sz of {integer,N} -> count_bits_matched(Is, SavePoint, Bits+N*U); _ -> count_bits_matched(Is, SavePoint, Bits) end; -count_bits_matched([{test,bs_match_string,_,[_,Bits,_]}|Is], SavePoint, Bits0) -> - count_bits_matched(Is, SavePoint, Bits0+Bits); +count_bits_matched([{test,bs_match_string,_,[_,Bs]}|Is], SavePoint, Bits) -> + count_bits_matched(Is, SavePoint, Bits+bit_size(Bs)); count_bits_matched([{test,_,_,_}|Is], SavePoint, Bits) -> count_bits_matched(Is, SavePoint, Bits); count_bits_matched([{bs_save2,Reg,SavePoint}|_], {Reg,SavePoint}, Bits) -> @@ -545,20 +531,346 @@ shortcut_bs_pos_used_1(Is, Reg, D) -> not beam_utils:is_killed(Reg, Is, D). %% shortcut_bs_start_match(TargetLabel, Reg) -> TargetLabel -%% A failing bs_start_match2 instruction means that the source -%% cannot be a binary, so there is no need to jump bs_context_to_binary/1 -%% or another bs_start_match2 instruction. +%% A failing bs_start_match2 instruction means that the source (Reg) +%% cannot be a binary. That means that it is safe to skip +%% bs_context_to_binary instructions operating on Reg, and +%% bs_start_match2 instructions operating on Reg. shortcut_bs_start_match(To, Reg, D) -> - shortcut_bs_start_match_1(beam_utils:code_at(To, D), Reg, To). + shortcut_bs_start_match_1(beam_utils:code_at(To, D), Reg, To, D). + +shortcut_bs_start_match_1([{bs_context_to_binary,Reg}|Is], Reg, To, D) -> + shortcut_bs_start_match_1(Is, Reg, To, D); +shortcut_bs_start_match_1([{jump,{f,To}}|_], Reg, _, D) -> + Code = beam_utils:code_at(To, D), + shortcut_bs_start_match_1(Code, Reg, To, D); +shortcut_bs_start_match_1([{test,bs_start_match2,{f,To},_,[Reg|_],_}|_], + Reg, _, D) -> + Code = beam_utils:code_at(To, D), + shortcut_bs_start_match_1(Code, Reg, To, D); +shortcut_bs_start_match_1(_, _, To, _) -> + To. + +%% shortcut_rel_op(FailLabel, Operator, [Operand], D) -> FailLabel' +%% Try to shortcut the given test instruction. Example: +%% +%% is_ge L1 {x,0} 48 +%% . +%% . +%% . +%% L1: is_ge L2 {x,0} 65 +%% +%% The first test instruction can be rewritten to "is_ge L2 {x,0} 48" +%% since the instruction at L1 will also fail. +%% +%% If there are instructions between L1 and the other test instruction +%% it may still be possible to do the shortcut. For example: +%% +%% L1: is_eq_exact L3 {x,0} 92 +%% is_ge L2 {x,0} 65 +%% +%% Since the first test instruction failed, we know that {x,0} must +%% be less than 48; therefore, we know that {x,0} cannot be equal to +%% 92 and the jump to L3 cannot happen. + +shortcut_rel_op(To, Op, Ops, D) -> + case normalize_op({test,Op,{f,To},Ops}) of + {{NormOp,A,B},_} -> + Normalized = {negate_op(NormOp),A,B}, + shortcut_rel_op_fp(To, Normalized, D); + {_,_} -> + To; + error -> + To + end. -shortcut_bs_start_match_1([{bs_context_to_binary,Reg}|Is], Reg, To) -> - shortcut_bs_start_match_2(Is, Reg, To); -shortcut_bs_start_match_1(_, _, To) -> To. +shortcut_rel_op_fp(To0, Normalized, D) -> + Code = beam_utils:code_at(To0, D), + case shortcut_any_label(Code, Normalized) of + error -> + To0; + To -> + shortcut_rel_op_fp(To, Normalized, D) + end. -shortcut_bs_start_match_2([{jump,{f,To}}|_], _, _) -> - To; -shortcut_bs_start_match_2([{test,bs_start_match2,{f,To},_,[Reg|_],_}|_], Reg, _) -> - To; -shortcut_bs_start_match_2(_Is, _Reg, To) -> - To. +%% shortcut_any_label([Instruction], PrevCondition) -> FailLabel | error +%% Using PrevCondition (a previous condition known to be true), +%% try to shortcut to another failure label. + +shortcut_any_label([{jump,{f,Lbl}}|_], _Prev) -> + Lbl; +shortcut_any_label([{label,Lbl}|_], _Prev) -> + Lbl; +shortcut_any_label([{select,select_val,R,{f,Fail},L}|_], Prev) -> + shortcut_selectval(L, R, Fail, Prev); +shortcut_any_label([I|Is], Prev) -> + case normalize_op(I) of + error -> + error; + {Normalized,Fail} -> + %% We have a relational operator. + case will_succeed(Prev, Normalized) of + no -> + %% This test instruction will always branch + %% to Fail. + Fail; + yes -> + %% This test instruction will never branch, + %% so we will look at the next instruction. + shortcut_any_label(Is, Prev); + maybe -> + %% May or may not branch. From now on, we can only + %% shortcut to the this specific failure label + %% Fail. + shortcut_specific_label(Is, Fail, Prev) + end + end. + +%% shortcut_specific_label([Instruction], FailLabel, PrevCondition) -> +%% FailLabel | error +%% We have previously encountered a test instruction that may or +%% may not branch to FailLabel. Therefore we are only allowed +%% to do the shortcut to the same fail label (FailLabel). + +shortcut_specific_label([{label,_}|Is], Fail, Prev) -> + shortcut_specific_label(Is, Fail, Prev); +shortcut_specific_label([{select,select_val,R,{f,F},L}|_], Fail, Prev) -> + case shortcut_selectval(L, R, F, Prev) of + Fail -> Fail; + _ -> error + end; +shortcut_specific_label([I|Is], Fail, Prev) -> + case normalize_op(I) of + error -> + error; + {Normalized,Fail} -> + case will_succeed(Prev, Normalized) of + no -> + %% Will branch to FailLabel. + Fail; + yes -> + %% Will definitely never branch. + shortcut_specific_label(Is, Fail, Prev); + maybe -> + %% May branch, but still OK since it will branch + %% to FailLabel. + shortcut_specific_label(Is, Fail, Prev) + end; + {Normalized,_} -> + %% This test instruction will branch to a different + %% fail label, if it branches at all. + case will_succeed(Prev, Normalized) of + yes -> + %% Still OK, since the branch will never be + %% taken. + shortcut_specific_label(Is, Fail, Prev); + no -> + %% Give up. The branch will definitely be taken + %% to a different fail label. + error; + maybe -> + %% Give up. If the branch is taken, it will be + %% to a different fail label. + error + end + end. + + +%% shortcut_selectval(List, Reg, Fail, PrevCond) -> FailLabel | error +%% Try to shortcut a selectval instruction. A selectval instruction +%% is equivalent to the following instruction sequence: +%% +%% is_ne_exact L1 Reg Value1 +%% . +%% . +%% . +%% is_ne_exact LN Reg ValueN +%% jump DefaultFailLabel +%% +shortcut_selectval([Val,{f,Lbl}|T], R, Fail, Prev) -> + case will_succeed(Prev, {'=/=',R,get_literal(Val)}) of + yes -> shortcut_selectval(T, R, Fail, Prev); + no -> Lbl; + maybe -> error + end; +shortcut_selectval([], _, Fail, _) -> Fail. + +%% will_succeed(PrevCondition, Condition) -> yes | no | maybe +%% PrevCondition is a condition known to be true. This function +%% will tell whether Condition will succeed. + +will_succeed({Op1,Reg,A}, {Op2,Reg,B}) -> + will_succeed_1(Op1, A, Op2, B); +will_succeed({'=:=',Reg,{literal,A}}, {TypeTest,Reg}) -> + case erlang:TypeTest(A) of + false -> no; + true -> yes + end; +will_succeed({_,_,_}, maybe) -> + maybe; +will_succeed({_,_,_}, Test) when is_tuple(Test) -> + maybe. + +will_succeed_1('=:=', A, '<', B) -> + if + B =< A -> no; + true -> yes + end; +will_succeed_1('=:=', A, '=<', B) -> + if + B < A -> no; + true -> yes + end; +will_succeed_1('=:=', A, '=:=', B) -> + if + A =:= B -> yes; + true -> no + end; +will_succeed_1('=:=', A, '=/=', B) -> + if + A =:= B -> no; + true -> yes + end; +will_succeed_1('=:=', A, '>=', B) -> + if + B > A -> no; + true -> yes + end; +will_succeed_1('=:=', A, '>', B) -> + if + B >= A -> no; + true -> yes + end; + +will_succeed_1('=/=', A, '=/=', B) when A =:= B -> yes; +will_succeed_1('=/=', A, '=:=', B) when A =:= B -> no; + +will_succeed_1('<', A, '=:=', B) when B >= A -> no; +will_succeed_1('<', A, '=/=', B) when B >= A -> yes; +will_succeed_1('<', A, '<', B) when B >= A -> yes; +will_succeed_1('<', A, '=<', B) when B > A -> yes; +will_succeed_1('<', A, '>=', B) when B > A -> no; +will_succeed_1('<', A, '>', B) when B >= A -> no; + +will_succeed_1('=<', A, '=:=', B) when B > A -> no; +will_succeed_1('=<', A, '=/=', B) when B > A -> yes; +will_succeed_1('=<', A, '<', B) when B > A -> yes; +will_succeed_1('=<', A, '=<', B) when B >= A -> yes; +will_succeed_1('=<', A, '>=', B) when B > A -> no; +will_succeed_1('=<', A, '>', B) when B >= A -> no; + +will_succeed_1('>=', A, '=:=', B) when B < A -> no; +will_succeed_1('>=', A, '=/=', B) when B < A -> yes; +will_succeed_1('>=', A, '<', B) when B =< A -> no; +will_succeed_1('>=', A, '=<', B) when B < A -> no; +will_succeed_1('>=', A, '>=', B) when B =< A -> yes; +will_succeed_1('>=', A, '>', B) when B < A -> yes; + +will_succeed_1('>', A, '=:=', B) when B =< A -> no; +will_succeed_1('>', A, '=/=', B) when B =< A -> yes; +will_succeed_1('>', A, '<', B) when B =< A -> no; +will_succeed_1('>', A, '=<', B) when B < A -> no; +will_succeed_1('>', A, '>=', B) when B =< A -> yes; +will_succeed_1('>', A, '>', B) when B < A -> yes; + +will_succeed_1(_, _, _, _) -> maybe. + +%% normalize_op(Instruction) -> {Normalized,FailLabel} | error +%% Normalized = {Operator,Register,Literal} | +%% {TypeTest,Register} | +%% maybe +%% Operation = '<' | '=<' | '=:=' | '=/=' | '>=' | '>' +%% TypeTest = is_atom | is_integer ... +%% Literal = {literal,Term} +%% +%% Normalize a relational operator to facilitate further +%% comparisons between operators. Always make the register +%% operand the first operand. Thus the following instruction: +%% +%% {test,is_ge,{f,99},{integer,13},{x,0}} +%% +%% will be normalized to: +%% +%% {'=<',{x,0},{literal,13}} +%% +%% NOTE: Bit syntax test instructions are scary. They may change the +%% state of match contexts and update registers, so we don't dare +%% mess with them. + +normalize_op({test,is_ge,{f,Fail},Ops}) -> + normalize_op_1('>=', Ops, Fail); +normalize_op({test,is_lt,{f,Fail},Ops}) -> + normalize_op_1('<', Ops, Fail); +normalize_op({test,is_eq_exact,{f,Fail},Ops}) -> + normalize_op_1('=:=', Ops, Fail); +normalize_op({test,is_ne_exact,{f,Fail},Ops}) -> + normalize_op_1('=/=', Ops, Fail); +normalize_op({test,is_nil,{f,Fail},[R]}) -> + normalize_op_1('=:=', [R,nil], Fail); +normalize_op({test,Op,{f,Fail},[R]}) -> + case erl_internal:new_type_test(Op, 1) of + true -> {{Op,R},Fail}; + false -> {maybe,Fail} + end; +normalize_op({test,_,{f,Fail},_}=I) -> + case beam_utils:is_pure_test(I) of + true -> {maybe,Fail}; + false -> error + end; +normalize_op(_) -> + error. + +normalize_op_1(Op, [Op1,Op2], Fail) -> + case {get_literal(Op1),get_literal(Op2)} of + {error,error} -> + %% Both operands are registers. + {maybe,Fail}; + {error,Lit} -> + {{Op,Op1,Lit},Fail}; + {Lit,error} -> + {{turn_op(Op),Op2,Lit},Fail}; + {_,_} -> + %% Both operands are literals. Can probably only + %% happen if the Core Erlang optimizations passes were + %% turned off, so don't bother trying to do something + %% smart here. + {maybe,Fail} + end. + +turn_op('<') -> '>'; +turn_op('>=') -> '=<'; +turn_op('=:='=Op) -> Op; +turn_op('=/='=Op) -> Op. + +negate_op('>=') -> '<'; +negate_op('<') -> '>='; +negate_op('=<') -> '>'; +negate_op('>') -> '=<'; +negate_op('=:=') -> '=/='; +negate_op('=/=') -> '=:='. + +get_literal({atom,Val}) -> + {literal,Val}; +get_literal({integer,Val}) -> + {literal,Val}; +get_literal({float,Val}) -> + {literal,Val}; +get_literal(nil) -> + {literal,[]}; +get_literal({literal,_}=Lit) -> + Lit; +get_literal({_,_}) -> error. + + +%%% +%%% Removing stores to Y registers is not always safe +%%% if there is an instruction that causes an exception +%%% within a catch. In practice, there are few or no +%%% opportunities for removing stores to Y registers anyway +%%% if sys_core_fold has been run. +%%% + +is_killed_at({x,_}=Reg, Lbl, D) -> + beam_utils:is_killed_at(Reg, Lbl, D); +is_killed_at({y,_}, _, _) -> + false. diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl index ea51673fa3..9565ab74c4 100644 --- a/lib/compiler/src/beam_dict.erl +++ b/lib/compiler/src/beam_dict.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2014. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -31,22 +32,22 @@ -type index() :: non_neg_integer(). --type atom_tab() :: gb_trees:tree(atom(), index()). +-type atom_tab() :: #{atom() => index()}. -type import_tab() :: gb_trees:tree(mfa(), index()). --type fname_tab() :: gb_trees:tree(Name :: term(), index()). --type line_tab() :: gb_trees:tree({Fname :: index(), Line :: term()}, index()). +-type fname_tab() :: #{Name :: term() => index()}. +-type line_tab() :: #{{Fname :: index(), Line :: term()} => index()}. -type literal_tab() :: dict:dict(Literal :: term(), index()). -record(asm, - {atoms = gb_trees:empty() :: atom_tab(), + {atoms = #{} :: atom_tab(), exports = [] :: [{label(), arity(), label()}], locals = [] :: [{label(), arity(), label()}], imports = gb_trees:empty() :: import_tab(), strings = <<>> :: binary(), %String pool - lambdas = [], %[{...}] + lambdas = {0,[]}, %[{...}] literals = dict:new() :: literal_tab(), - fnames = gb_trees:empty() :: fname_tab(), - lines = gb_trees:empty() :: line_tab(), + fnames = #{} :: fname_tab(), + lines = #{} :: line_tab(), num_lines = 0 :: non_neg_integer(), %Number of line instructions next_import = 0 :: non_neg_integer(), string_offset = 0 :: non_neg_integer(), @@ -65,7 +66,7 @@ new() -> %% Remember the highest opcode. -spec opcode(non_neg_integer(), bdict()) -> bdict(). -opcode(Op, Dict) when Dict#asm.highest_opcode > Op -> Dict; +opcode(Op, Dict) when Dict#asm.highest_opcode >= Op -> Dict; opcode(Op, Dict) -> Dict#asm{highest_opcode=Op}. %% Returns the highest opcode encountered. @@ -77,14 +78,12 @@ highest_opcode(#asm{highest_opcode=Op}) -> Op. %% atom(Atom, Dict) -> {Index,Dict'} -spec atom(atom(), bdict()) -> {pos_integer(), bdict()}. -atom(Atom, #asm{atoms=Atoms0}=Dict) when is_atom(Atom) -> - case gb_trees:lookup(Atom, Atoms0) of - {value,Index} -> - {Index,Dict}; - none -> - NextIndex = gb_trees:size(Atoms0) + 1, - Atoms = gb_trees:insert(Atom, NextIndex, Atoms0), - {NextIndex,Dict#asm{atoms=Atoms}} +atom(Atom, #asm{atoms=Atoms}=Dict) when is_atom(Atom) -> + case Atoms of + #{ Atom := Index} -> {Index,Dict}; + _ -> + NextIndex = maps:size(Atoms) + 1, + {NextIndex,Dict#asm{atoms=Atoms#{Atom=>NextIndex}}} end. %% Remembers an exported function. @@ -146,15 +145,14 @@ string(Str, Dict) when is_list(Str) -> -spec lambda(label(), non_neg_integer(), bdict()) -> {non_neg_integer(), bdict()}. -lambda(Lbl, NumFree, #asm{lambdas=Lambdas0}=Dict) -> - OldIndex = length(Lambdas0), +lambda(Lbl, NumFree, #asm{lambdas={OldIndex,Lambdas0}}=Dict) -> %% Set Index the same as OldIndex. Index = OldIndex, %% Initialize OldUniq to 0. It will be set to an unique value %% based on the MD5 checksum of the BEAM code for the module. OldUniq = 0, Lambdas = [{Lbl,{OldIndex,Lbl,Index,NumFree,OldUniq}}|Lambdas0], - {OldIndex,Dict#asm{lambdas=Lambdas}}. + {OldIndex,Dict#asm{lambdas={OldIndex+1,Lambdas}}}. %% Returns the index for a literal (adding it to the literal table if necessary). %% literal(Literal, Dict) -> {Index,Dict'} @@ -177,26 +175,22 @@ line([], #asm{num_lines=N}=Dict) -> %% No location available. Return the special pre-defined %% index 0. {0,Dict#asm{num_lines=N+1}}; -line([{location,Name,Line}], #asm{lines=Lines0,num_lines=N}=Dict0) -> +line([{location,Name,Line}], #asm{lines=Lines,num_lines=N}=Dict0) -> {FnameIndex,Dict1} = fname(Name, Dict0), - case gb_trees:lookup({FnameIndex,Line}, Lines0) of - {value,Index} -> - {Index,Dict1#asm{num_lines=N+1}}; - none -> - Index = gb_trees:size(Lines0) + 1, - Lines = gb_trees:insert({FnameIndex,Line}, Index, Lines0), - Dict = Dict1#asm{lines=Lines,num_lines=N+1}, - {Index,Dict} + Key = {FnameIndex,Line}, + case Lines of + #{Key := Index} -> {Index,Dict1#asm{num_lines=N+1}}; + _ -> + Index = maps:size(Lines) + 1, + {Index, Dict1#asm{lines=Lines#{Key=>Index},num_lines=N+1}} end. -fname(Name, #asm{fnames=Fnames0}=Dict) -> - case gb_trees:lookup(Name, Fnames0) of - {value,Index} -> - {Index,Dict}; - none -> - Index = gb_trees:size(Fnames0), - Fnames = gb_trees:insert(Name, Index, Fnames0), - {Index,Dict#asm{fnames=Fnames}} +fname(Name, #asm{fnames=Fnames}=Dict) -> + case Fnames of + #{Name := Index} -> {Index,Dict}; + _ -> + Index = maps:size(Fnames), + {Index,Dict#asm{fnames=Fnames#{Name=>Index}}} end. %% Returns the atom table. @@ -204,14 +198,12 @@ fname(Name, #asm{fnames=Fnames0}=Dict) -> -spec atom_table(bdict()) -> {non_neg_integer(), [[non_neg_integer(),...]]}. atom_table(#asm{atoms=Atoms}) -> - NumAtoms = gb_trees:size(Atoms), - Sorted = lists:keysort(2, gb_trees:to_list(Atoms)), - Fun = fun({A,_}) -> - L = atom_to_list(A), - [length(L)|L] - end, - AtomTab = lists:map(Fun, Sorted), - {NumAtoms,AtomTab}. + NumAtoms = maps:size(Atoms), + Sorted = lists:keysort(2, maps:to_list(Atoms)), + {NumAtoms,[begin + L = atom_to_list(A), + [length(L)|L] + end || {A,_} <- Sorted]}. %% Returns the table of local functions. %% local_table(Dict) -> {NumLocals, [{Function, Arity, Label}...]} @@ -243,13 +235,13 @@ string_table(#asm{strings=Strings,string_offset=Size}) -> -spec lambda_table(bdict()) -> {non_neg_integer(), [<<_:192>>]}. -lambda_table(#asm{locals=Loc0,lambdas=Lambdas0}) -> +lambda_table(#asm{locals=Loc0,lambdas={NumLambdas,Lambdas0}}) -> Lambdas1 = sofs:relation(Lambdas0), Loc = sofs:relation([{Lbl,{F,A}} || {F,A,Lbl} <- Loc0]), Lambdas2 = sofs:relative_product1(Lambdas1, Loc), Lambdas = [<<F:32,A:32,Lbl:32,Index:32,NumFree:32,OldUniq:32>> || {{_,Lbl,Index,NumFree,OldUniq},{F,A}} <- sofs:to_external(Lambdas2)], - {length(Lambdas),Lambdas}. + {NumLambdas,Lambdas}. %% Returns the literal table. %% literal_table(Dict) -> {NumLiterals, [<<TermSize>>,TermInExternalFormat]} @@ -273,11 +265,11 @@ my_term_to_binary(Term) -> non_neg_integer(),[{non_neg_integer(),non_neg_integer()}]}. line_table(#asm{fnames=Fnames0,lines=Lines0,num_lines=NumLineInstrs}) -> - NumFnames = gb_trees:size(Fnames0), - Fnames1 = lists:keysort(2, gb_trees:to_list(Fnames0)), + NumFnames = maps:size(Fnames0), + Fnames1 = lists:keysort(2, maps:to_list(Fnames0)), Fnames = [Name || {Name,_} <- Fnames1], - NumLines = gb_trees:size(Lines0), - Lines1 = lists:keysort(2, gb_trees:to_list(Lines0)), + NumLines = maps:size(Lines0), + Lines1 = lists:keysort(2, maps:to_list(Lines0)), Lines = [L || {L,_} <- Lines1], {NumLineInstrs,NumFnames,Fnames,NumLines,Lines}. diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index c45596f236..c699672db1 100644 --- a/lib/compiler/src/beam_disasm.erl +++ b/lib/compiler/src/beam_disasm.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2014. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %%======================================================================= @@ -313,10 +314,7 @@ get_funs({LsR0,[{func_info,[{atom,M}=AtomM,{atom,F}=AtomF,ArityArg]}|Code0]}) when is_atom(M), is_atom(F) -> Arity = resolve_arg_unsigned(ArityArg), {LsR,Code,RestCode} = get_fun(Code0, []), - Entry = case Code of - [{label,[{u,E}]}|_] -> E; - _ -> undefined - end, + [{label,[{u,Entry}]}|_] = Code, [#function{name=F, arity=Arity, entry=Entry, diff --git a/lib/compiler/src/beam_disasm.hrl b/lib/compiler/src/beam_disasm.hrl index c2aca1199e..d968cd9587 100644 --- a/lib/compiler/src/beam_disasm.hrl +++ b/lib/compiler/src/beam_disasm.hrl @@ -2,18 +2,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -21,7 +22,9 @@ %% the system (e.g. in the translation from Beam to Icode). %% -%% XXX: THE FOLLOWING TYPE DECLARATION DOES NOT BELONG HERE... +%% XXX: THE FOLLOWING TYPE DECLARATION DOES NOT BELONG HERE. +%% IT SHOULD BE MOVED TO A FILE THAT DEFINES (AND EXPORTS) +%% PROPER TYPES FOR THE SET OF BEAM INSTRUCTIONS. %% -type beam_instr() :: 'bs_init_writable' | 'fclearerror' | 'if_end' | 'remove_message' | 'return' | 'send' | 'timeout' @@ -33,7 +36,7 @@ -record(function, {name :: atom(), arity :: byte(), - entry, %% unused ?? + entry :: beam_lib:label(), %% unnecessary ? code = [] :: [beam_instr()]}). -record(beam_file, {module :: module(), diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl index d261809765..4a181c1923 100644 --- a/lib/compiler/src/beam_except.erl +++ b/lib/compiler/src/beam_except.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2013. All Rights Reserved. +%% Copyright Ericsson AB 2011-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -132,10 +133,12 @@ translate_exception(_, _, _, _) -> no. fix_block(Is, 0) -> reverse(Is); fix_block(Is, Words) -> - fix_block_1(reverse(Is), Words). + reverse(fix_block_1(Is, Words)). -fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is], Words) -> - [{set,[],[],{alloc,Live,{F1,F2,Needed-Words,F3}}}|Is]; +fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed0,F3}}}|Is], Words) -> + Needed = Needed0 - Words, + true = Needed >= 0, %Assertion. + [{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is]; fix_block_1([I|Is], Words) -> [I|fix_block_1(Is, Words)]. diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl index 46835bece1..36369bd0b4 100644 --- a/lib/compiler/src/beam_flatten.erl +++ b/lib/compiler/src/beam_flatten.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -63,8 +64,7 @@ norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I}; norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2}; norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) -> {put_map,F,Op,S,D,R,{list,Puts}}; -norm({set,Gets,[S],{get_map_elements,F}}) -> - {get_map_elements,F,S,{list,Gets}}; +%% get_map_elements is always handled in beam_split (moved out of block) norm({set,[],[],remove_message}) -> remove_message; norm({set,[],[],fclearerror}) -> fclearerror; norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}. diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index b952139f2c..48b5a32814 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -127,7 +128,7 @@ %%% on the program state. %%% --import(lists, [reverse/1,reverse/2,foldl/3,dropwhile/2]). +-import(lists, [reverse/1,reverse/2,foldl/3]). module({Mod,Exp,Attr,Fs0,Lc}, _Opt) -> Fs = [function(F) || F <- Fs0], @@ -152,20 +153,32 @@ function({function,Name,Arity,CLabel,Asm0}) -> share(Is0) -> %% We will get more sharing if we never fall through to a label. Is = eliminate_fallthroughs(Is0, []), - share_1(Is, dict:new(), [], []). + share_1(Is, #{}, [], []). share_1([{label,_}=Lbl|Is], Dict, [], Acc) -> share_1(Is, Dict, [], [Lbl|Acc]); share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) -> - case dict:find(Seq, Dict0) of + case maps:find(Seq, Dict0) of error -> - Dict = dict:store(Seq, L, Dict0), + Dict = maps:put(Seq, L, Dict0), share_1(Is, Dict, [], [Lbl|Seq ++ Acc]); {ok,Label} -> share_1(Is, Dict0, [], [Lbl,{jump,{f,Label}}|Acc]) end; share_1([{func_info,_,_,_}=I|Is], _, [], Acc) -> reverse(Is, [I|Acc]); +share_1([{'catch',_,_}=I|Is], Dict0, Seq, Acc) -> + Dict = clean_non_sharable(Dict0), + share_1(Is, Dict, [I|Seq], Acc); +share_1([{'try',_,_}=I|Is], Dict0, Seq, Acc) -> + Dict = clean_non_sharable(Dict0), + share_1(Is, Dict, [I|Seq], Acc); +share_1([{try_case,_}=I|Is], Dict0, Seq, Acc) -> + Dict = clean_non_sharable(Dict0), + share_1(Is, Dict, [I|Seq], Acc); +share_1([{catch_end,_}=I|Is], Dict0, Seq, Acc) -> + Dict = clean_non_sharable(Dict0), + share_1(Is, Dict, [I|Seq], Acc); share_1([I|Is], Dict, Seq, Acc) -> case is_unreachable_after(I) of false -> @@ -174,6 +187,24 @@ share_1([I|Is], Dict, Seq, Acc) -> share_1(Is, Dict, [I], Acc) end. +clean_non_sharable(Dict) -> + %% We are passing in or out of a 'catch' or 'try' block. Remove + %% sequences that should not be shared over the boundaries of the + %% block. Since the end of the sequence must match, the only + %% possible match between a sequence outside and a sequence inside + %% the 'catch'/'try' block is a sequence that ends with an + %% instruction that causes an exception. Any sequence that causes + %% an exception must contain a line/1 instruction. + maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict). + +sharable_with_try([{line,_}|_]) -> + %% This sequence may cause an exception and may potentially + %% match a sequence on the other side of the 'catch'/'try' block + %% boundary. + false; +sharable_with_try([_|Is]) -> + sharable_with_try(Is); +sharable_with_try([]) -> true. %% Eliminate all fallthroughs. Return the result reversed. @@ -241,17 +272,17 @@ extract_seq_1(_, _) -> no. %%% (3) (4) (5) (6) Jump and unreachable code optimizations. %%% --record(st, {fc, %Label for function class errors. - entry, %Entry label (must not be moved). - mlbl, %Moved labels. - labels %Set of referenced labels. - }). +-record(st, + { + entry, %Entry label (must not be moved). + mlbl, %Moved labels. + labels :: cerl_sets:set() %Set of referenced labels. + }). -opt([{label,Fc}|_]=Is0, CLabel) -> - Lbls = initial_labels(Is0), +opt(Is0, CLabel) -> find_fixpoint(fun(Is) -> - St = #st{fc=Fc,entry=CLabel,mlbl=dict:new(), - labels=Lbls}, + Lbls = initial_labels(Is), + St = #st{entry=CLabel,mlbl=#{},labels=Lbls}, opt(Is, [], St) end, Is0). @@ -295,24 +326,30 @@ opt([{test,_,{f,_}=Lbl,_,_,_}=I|Is], Acc, St) -> opt(Is, [I|Acc], label_used(Lbl, St)); opt([{select,_,_R,Fail,Vls}=I|Is], Acc, St) -> skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St)); -opt([{label,L}=I|Is], Acc, #st{entry=L}=St) -> - %% NEVER move the entry label. - opt(Is, [I|Acc], St); -opt([{label,L1},{jump,{f,L2}}=I|Is], [Prev|Acc], St0) -> - St = St0#st{mlbl=dict:append(L2, L1, St0#st.mlbl)}, - opt([Prev,I|Is], Acc, label_used({f,L2}, St)); opt([{label,Lbl}=I|Is], Acc, #st{mlbl=Mlbl}=St0) -> - case dict:find(Lbl, Mlbl) of + case maps:find(Lbl, Mlbl) of {ok,Lbls} -> %% Essential to remove the list of labels from the dictionary, %% since we will rescan the inserted labels. We MUST rescan. - St = St0#st{mlbl=dict:erase(Lbl, Mlbl)}, + St = St0#st{mlbl=maps:remove(Lbl, Mlbl)}, insert_labels([Lbl|Lbls], Is, Acc, St); - error -> opt(Is, [I|Acc], St0) + error -> + opt(Is, [I|Acc], St0) end; -opt([{jump,{f,Lbl}},{label,Lbl}=I|Is], Acc, St) -> - opt([I|Is], Acc, St); -opt([{jump,Lbl}=I|Is], Acc, St) -> +opt([{jump,{f,_}=X}|[{label,_},{jump,X}|_]=Is], Acc, St) -> + opt(Is, Acc, St); +opt([{jump,{f,Lbl}}|[{label,Lbl}|_]=Is], Acc, St) -> + opt(Is, Acc, St); +opt([{jump,{f,L}=Lbl}=I|Is], Acc0, #st{mlbl=Mlbl0}=St0) -> + %% All labels before this jump instruction should now be + %% moved to the location of the jump's target. + {Lbls,Acc} = collect_labels(Acc0, St0), + St = case Lbls of + [] -> St0; + [_|_] -> + Mlbl = maps_append_list(L, Lbls, Mlbl0), + St0#st{mlbl=Mlbl} + end, skip_unreachable(Is, [I|Acc], label_used(Lbl, St)); %% Optimization: quickly handle some common instructions that don't %% have any failure labels and where is_unreachable_after(I) =:= false. @@ -332,22 +369,36 @@ opt([I|Is], Acc, #st{labels=Used0}=St0) -> true -> skip_unreachable(Is, [I|Acc], St); false -> opt(Is, [I|Acc], St) end; -opt([], Acc, #st{fc=Fc,mlbl=Mlbl}) -> +opt([], Acc, #st{mlbl=Mlbl}) -> Code = reverse(Acc), - case dict:find(Fc, Mlbl) of - {ok,Lbls} -> insert_fc_labels(Lbls, Mlbl, Code); - error -> Code - end. + insert_fc_labels(Code, Mlbl). -insert_fc_labels([L|Ls], Mlbl, Acc0) -> - Acc = [{label,L}|Acc0], - case dict:find(L, Mlbl) of +insert_fc_labels([{label,L}=I|Is0], Mlbl) -> + case maps:find(L, Mlbl) of error -> - insert_fc_labels(Ls, Mlbl, Acc); + [I|insert_fc_labels(Is0, Mlbl)]; {ok,Lbls} -> - insert_fc_labels(Lbls++Ls, Mlbl, Acc) + Is = [{label,Lb} || Lb <- Lbls] ++ Is0, + [I|insert_fc_labels(Is, maps:remove(L, Mlbl))] end; -insert_fc_labels([], _, Acc) -> Acc. +insert_fc_labels([_|_]=Is, _) -> Is. + +maps_append_list(K,Vs,M) -> + case M of + #{K:=Vs0} -> M#{K:=Vs0++Vs}; % same order as dict + _ -> M#{K => Vs} + end. + +collect_labels(Is, #st{entry=Entry}) -> + collect_labels_1(Is, Entry, []). + +collect_labels_1([{label,Entry}|_]=Is, Entry, Acc) -> + %% Never move the entry label. + {Acc,Is}; +collect_labels_1([{label,L}|Is], Entry, Acc) -> + collect_labels_1(Is, Entry, [L|Acc]); +collect_labels_1(Is, _Entry, Acc) -> + {Acc,Is}. %% label_defined(Is, Label) -> true | false. %% Test whether the label Label is defined at the start of the instruction @@ -394,7 +445,7 @@ skip_unreachable([], Acc, St) -> %% Add one or more label to the set of used labels. -label_used({f,L}, St) -> St#st{labels=gb_sets:add(L, St#st.labels)}; +label_used({f,L}, St) -> St#st{labels=cerl_sets:add_element(L,St#st.labels)}; label_used([H|T], St0) -> label_used(T, label_used(H, St0)); label_used([], St) -> St; label_used(_Other, St) -> St. @@ -402,7 +453,7 @@ label_used(_Other, St) -> St. %% Test if label is used. is_label_used(L, St) -> - gb_sets:is_member(L, St#st.labels). + cerl_sets:is_element(L, St#st.labels). %% is_unreachable_after(Instruction) -> boolean() %% Test whether the code after Instruction is unreachable. @@ -432,29 +483,29 @@ is_exit_instruction(_) -> false. %% (including inside blocks). is_label_used_in(Lbl, Is) -> - is_label_used_in_1(Is, Lbl, gb_sets:empty()). + is_label_used_in_1(Is, Lbl, cerl_sets:new()). is_label_used_in_1([{block,Block}|Is], Lbl, Empty) -> - lists:any(fun(I) -> is_label_used_in_2(I, Lbl) end, Block) + lists:any(fun(I) -> is_label_used_in_block(I, Lbl) end, Block) orelse is_label_used_in_1(Is, Lbl, Empty); is_label_used_in_1([I|Is], Lbl, Empty) -> Used = ulbl(I, Empty), - gb_sets:is_member(Lbl, Used) orelse is_label_used_in_1(Is, Lbl, Empty); + cerl_sets:is_element(Lbl, Used) orelse is_label_used_in_1(Is, Lbl, Empty); is_label_used_in_1([], _, _) -> false. -is_label_used_in_2({set,_,_,Info}, Lbl) -> +is_label_used_in_block({set,_,_,Info}, Lbl) -> case Info of - {bif,_,{f,F}} -> F =:= Lbl; - {alloc,_,{gc_bif,_,{f,F}}} -> F =:= Lbl; + {bif,_,{f,F}} -> F =:= Lbl; + {alloc,_,{gc_bif,_,{f,F}}} -> F =:= Lbl; {alloc,_,{put_map,_,{f,F}}} -> F =:= Lbl; - {'catch',{f,F}} -> F =:= Lbl; - {alloc,_,_} -> false; - {put_tuple,_} -> false; - {get_tuple_element,_} -> false; - {set_tuple_element,_} -> false; {get_map_elements,{f,F}} -> F =:= Lbl; - {line,_} -> false; - _ when is_atom(Info) -> false + {try_catch,_,{f,F}} -> F =:= Lbl; + {alloc,_,_} -> false; + {put_tuple,_} -> false; + {get_tuple_element,_} -> false; + {set_tuple_element,_} -> false; + {line,_} -> false; + _ when is_atom(Info) -> false end. %% remove_unused_labels(Instructions0) -> Instructions @@ -467,13 +518,10 @@ remove_unused_labels(Is) -> rem_unused(Is, Used, []). rem_unused([{label,Lbl}=I|Is0], Used, [Prev|_]=Acc) -> - case gb_sets:is_member(Lbl, Used) of + case cerl_sets:is_element(Lbl, Used) of false -> Is = case is_unreachable_after(Prev) of - true -> - dropwhile(fun({label,_}) -> false; - (_) -> true - end, Is0); + true -> drop_upto_label(Is0); false -> Is0 end, rem_unused(Is, Used, Acc); @@ -492,7 +540,11 @@ initial_labels([{line,_}|Is], Acc) -> initial_labels([{label,Lbl}|Is], Acc) -> initial_labels(Is, [Lbl|Acc]); initial_labels([{func_info,_,_,_},{label,Lbl}|_], Acc) -> - gb_sets:from_list([Lbl|Acc]). + cerl_sets:from_list([Lbl|Acc]). + +drop_upto_label([{label,_}|_]=Is) -> Is; +drop_upto_label([_|Is]) -> drop_upto_label(Is); +drop_upto_label([]) -> []. %% ulbl(Instruction, UsedGbSet) -> UsedGbSet' %% Update the gb_set UsedGbSet with any function-local labels @@ -536,10 +588,10 @@ ulbl({get_map_elements,Lbl,_Src,_List}, Used) -> ulbl(_, Used) -> Used. mark_used({f,0}, Used) -> Used; -mark_used({f,L}, Used) -> gb_sets:add(L, Used). +mark_used({f,L}, Used) -> cerl_sets:add_element(L, Used). mark_used_list([{f,L}|T], Used) -> - mark_used_list(T, gb_sets:add(L, Used)); + mark_used_list(T, cerl_sets:add_element(L, Used)); mark_used_list([_|T], Used) -> mark_used_list(T, Used); mark_used_list([], Used) -> Used. diff --git a/lib/compiler/src/beam_listing.erl b/lib/compiler/src/beam_listing.erl index 50d1f3cdb1..ce566373bb 100644 --- a/lib/compiler/src/beam_listing.erl +++ b/lib/compiler/src/beam_listing.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -46,8 +47,8 @@ module(Stream, {Mod,Exp,Attr,Code,NumLabels}) -> fun ({function,Name,Arity,Entry,Asm}) -> io:format(Stream, "\n\n{function, ~w, ~w, ~w}.\n", [Name, Arity, Entry]), - foreach(fun(Op) -> print_op(Stream, Op) end, Asm) end, - Code); + io:put_chars(Stream, format_asm(Asm)) + end, Code); module(Stream, {Mod,Exp,Inter}) -> %% Other kinds of intermediate formats. io:fwrite(Stream, "~w.~n~p.~n", [Mod,Exp]), @@ -56,10 +57,11 @@ module(Stream, [_|_]=Fs) -> %% Form-based abstract format. foreach(fun (F) -> io:format(Stream, "~p.\n", [F]) end, Fs). -print_op(Stream, Label) when element(1, Label) == label -> - io:format(Stream, " ~p.\n", [Label]); -print_op(Stream, Op) -> - io:format(Stream, " ~p.\n", [Op]). +format_asm([{label,L}|Is]) -> + [" {label,",integer_to_list(L),"}.\n"|format_asm(Is)]; +format_asm([I|Is]) -> + [io_lib:format(" ~p", [I]),".\n"|format_asm(Is)]; +format_asm([]) -> []. function(File, {function,Name,Arity,Args,Body,Vdb,_Anno}) -> io:nl(File), diff --git a/lib/compiler/src/beam_peep.erl b/lib/compiler/src/beam_peep.erl index 97a8c7ba70..c8bef31824 100644 --- a/lib/compiler/src/beam_peep.erl +++ b/lib/compiler/src/beam_peep.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -64,18 +65,6 @@ function({function,Name,Arity,CLabel,Is0}) -> %% InEncoding =:= latin1, OutEncoding =:= unicode; %% InEncoding =:= latin1, OutEncoding =:= utf8 -> %% -%% (2) A select_val/4 instruction that only verifies that -%% its argument is either 'true' or 'false' can be -%% be replaced with an is_boolean/2 instruction. That is: -%% -%% select_val Reg Fail [ true Next false Next ] -%% Next: ... -%% -%% can be rewritten to -%% -%% is_boolean Fail Reg -%% Next: ... -%% peep(Is) -> peep(Is, gb_sets:empty(), []). @@ -94,12 +83,19 @@ peep([{gc_bif,_,_,_,_,Dst}=I|Is], SeenTests0, Acc) -> %% Kill all remembered tests that depend on the destination register. SeenTests = kill_seen(Dst, SeenTests0), peep(Is, SeenTests, [I|Acc]); -peep([{test,is_boolean,{f,Fail},Ops}|_]=Is, SeenTests, - [{test,is_atom,{f,Fail},Ops}|Acc]) -> - %% The previous is_atom/2 test (with the same failure label) is redundant. - %% (If is_boolean(Src) is true, is_atom(Src) is also true, so it is - %% OK to still remember that we have seen is_atom/1.) - peep(Is, SeenTests, Acc); +peep([{jump,{f,L}},{label,L}=I|Is], _, Acc) -> + %% Sometimes beam_jump has missed this optimization. + peep(Is, gb_sets:empty(), [I|Acc]); +peep([{select,Op,R,F,Vls0}|Is], _, Acc) -> + case prune_redundant_values(Vls0, F) of + [] -> + %% No values left. Must convert to plain jump. + I = {jump,F}, + peep(Is, gb_sets:empty(), [I|Acc]); + [_|_]=Vls -> + I = {select,Op,R,F,Vls}, + peep(Is, gb_sets:empty(), [I|Acc]) + end; peep([{test,Op,_,Ops}=I|Is], SeenTests0, Acc) -> case beam_utils:is_pure_test(I) of false -> @@ -108,34 +104,33 @@ peep([{test,Op,_,Ops}=I|Is], SeenTests0, Acc) -> %% has succeeded. peep(Is, gb_sets:empty(), [I|Acc]); true -> - Test = {Op,Ops}, - case gb_sets:is_element(Test, SeenTests0) of + case is_test_redundant(Op, Ops, SeenTests0) of true -> - %% This test has already succeeded and + %% This test or a similar test has already succeeded and %% is therefore redundant. peep(Is, SeenTests0, Acc); false -> %% Remember that we have seen this test. + Test = {Op,Ops}, SeenTests = gb_sets:insert(Test, SeenTests0), peep(Is, SeenTests, [I|Acc]) end end; -peep([{select,select_val,Src,Fail, - [{atom,false},{f,L},{atom,true},{f,L}]}| - [{label,L}|_]=Is], SeenTests, Acc) -> - I = {test,is_boolean,Fail,[Src]}, - peep([I|Is], SeenTests, Acc); -peep([{select,select_val,Src,Fail, - [{atom,true},{f,L},{atom,false},{f,L}]}| - [{label,L}|_]=Is], SeenTests, Acc) -> - I = {test,is_boolean,Fail,[Src]}, - peep([I|Is], SeenTests, Acc); peep([I|Is], _, Acc) -> %% An unknown instruction. Throw away all information we %% have collected about test instructions. peep(Is, gb_sets:empty(), [I|Acc]); peep([], _, Acc) -> reverse(Acc). +is_test_redundant(Op, Ops, Seen) -> + gb_sets:is_element({Op,Ops}, Seen) orelse + is_test_redundant_1(Op, Ops, Seen). + +is_test_redundant_1(is_boolean, [R], Seen) -> + gb_sets:is_element({is_eq_exact,[R,{atom,false}]}, Seen) orelse + gb_sets:is_element({is_eq_exact,[R,{atom,true}]}, Seen); +is_test_redundant_1(_, _, _) -> false. + kill_seen(Dst, Seen0) -> gb_sets:from_ordset(kill_seen_1(gb_sets:to_list(Seen0), Dst)). @@ -145,3 +140,9 @@ kill_seen_1([{_,Ops}=Test|T], Dst) -> false -> [Test|kill_seen_1(T, Dst)] end; kill_seen_1([], _) -> []. + +prune_redundant_values([_Val,F|Vls], F) -> + prune_redundant_values(Vls, F); +prune_redundant_values([Val,Lbl|Vls], F) -> + [Val,Lbl|prune_redundant_values(Vls, F)]; +prune_redundant_values([], _) -> []. diff --git a/lib/compiler/src/beam_receive.erl b/lib/compiler/src/beam_receive.erl index 97a9188ee7..89cafe27ce 100644 --- a/lib/compiler/src/beam_receive.erl +++ b/lib/compiler/src/beam_receive.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -176,7 +177,8 @@ opt_recv([I|Is], D, R0, L0, Acc) -> no; false -> opt_recv(Is, D, R, L, [I|Acc]) - end. + end; +opt_recv([], _, _, _, _) -> no. opt_update_regs({block,Bl}, R, L) -> {opt_update_regs_bl(Bl, R),L}; diff --git a/lib/compiler/src/beam_reorder.erl b/lib/compiler/src/beam_reorder.erl new file mode 100644 index 0000000000..6a7c033ec6 --- /dev/null +++ b/lib/compiler/src/beam_reorder.erl @@ -0,0 +1,148 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2016. 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% +%% + +-module(beam_reorder). + +-export([module/2]). +-import(lists, [member/2,reverse/1]). + +module({Mod,Exp,Attr,Fs0,Lc}, _Opt) -> + Fs = [function(F) || F <- Fs0], + {ok,{Mod,Exp,Attr,Fs,Lc}}. + +function({function,Name,Arity,CLabel,Is0}) -> + try + Is = reorder(Is0), + {function,Name,Arity,CLabel,Is} + catch + Class:Error -> + Stack = erlang:get_stacktrace(), + io:fwrite("Function: ~w/~w\n", [Name,Arity]), + erlang:raise(Class, Error, Stack) + end. + +%% reorder(Instructions0) -> Instructions +%% Reorder instructions before the beam_block pass, because reordering +%% will be more cumbersome when the blocks are in place. +%% +%% Execution of get_tuple_element instructions can be delayed until +%% they are actually needed. Consider the sequence: +%% +%% get_tuple_element Tuple Pos Dst +%% test Test Fail Operands +%% +%% If Dst is killed at label Fail (and not referenced in Operands), +%% we can can swap the instructions: +%% +%% test Test Fail Operands +%% get_tuple_element Tuple Pos Dst +%% +%% That can be beneficial in two ways: Firstly, if the branch is taken +%% we have avoided execution of the get_tuple_element instruction. +%% Secondly, even if the branch is not taken, subsequent optimization +%% (opt_blocks/1) may be able to change Dst to the final destination +%% register and eliminate a 'move' instruction. + +reorder(Is) -> + D = beam_utils:index_labels(Is), + reorder_1(Is, D, []). + +reorder_1([{Op,_,_}=TryCatch|[I|Is]=Is0], D, Acc) + when Op =:= 'catch'; Op =:= 'try' -> + %% Don't allow 'try' or 'catch' instructions to split blocks if + %% it can be avoided. + case is_safe(I) of + false -> + reorder_1(Is0, D, [TryCatch|Acc]); + true -> + reorder_1([TryCatch|Is], D, [I|Acc]) + end; +reorder_1([{label,L}=I|_], D, Acc) -> + Is = beam_utils:code_at(L, D), + reorder_1(Is, D, [I|Acc]); +reorder_1([{test,is_nonempty_list,_,_}=I|Is], D, Acc) -> + %% The run-time system may combine the is_nonempty_list test with + %% the following get_list instruction. + reorder_1(Is, D, [I|Acc]); +reorder_1([{test,_,_,_}=I, + {select,_,_,_,_}=S|Is], D, Acc) -> + %% There is nothing to gain by inserting a get_tuple_element + %% instruction between the test instruction and the select + %% instruction. + reorder_1(Is, D, [S,I|Acc]); +reorder_1([{test,_,{f,_},[Src|_]}=I|Is], D, + [{get_tuple_element,Src,_,_}|_]=Acc) -> + %% We want to avoid code that can confuse beam_validator such as: + %% is_tuple Fail Src + %% test_arity Fail Src Arity + %% is_map Fail Src + %% get_tuple_element Src Pos Dst + %% Therefore, don't reorder the instructions in such cases. + reorder_1(Is, D, [I|Acc]); +reorder_1([{test,_,{f,L},Ss}=I|Is0], D0, + [{get_tuple_element,_,_,El}=G|Acc0]=Acc) -> + case member(El, Ss) of + true -> + reorder_1(Is0, D0, [I|Acc]); + false -> + case beam_utils:is_killed_at(El, L, D0) of + true -> + Is = [I,G|Is0], + reorder_1(Is, D0, Acc0); + false -> + case beam_utils:is_killed(El, Is0, D0) of + true -> + Code0 = beam_utils:code_at(L, D0), + Code = [G|Code0], + D = beam_utils:index_label(L, Code, D0), + Is = [I|Is0], + reorder_1(Is, D, Acc0); + false -> + reorder_1(Is0, D0, [I|Acc]) + end + end + end; +reorder_1([{allocate_zero,N,Live}=I0|Is], D, + [{get_tuple_element,{x,Tup},_,{x,Dst}}=G|Acc]=Acc0) -> + case Tup < Dst andalso Dst+1 =:= Live of + true -> + %% Move allocation instruction upwards past + %% get_tuple_element instructions to create more + %% opportunities for moving get_tuple_element + %% instructions. + I = {allocate_zero,N,Dst}, + reorder_1([I,G|Is], D, Acc); + false -> + reorder_1(Is, D, [I0|Acc0]) + end; +reorder_1([I|Is], D, Acc) -> + reorder_1(Is, D, [I|Acc]); +reorder_1([], _, Acc) -> reverse(Acc). + +%% is_safe(Instruction) -> true|false +%% Test whether an instruction is safe (cannot cause an exception). + +is_safe({kill,_}) -> true; +is_safe({move,_,_}) -> true; +is_safe({put,_}) -> true; +is_safe({put_list,_,_,_}) -> true; +is_safe({put_tuple,_,_}) -> true; +is_safe({test_heap,_,_}) -> true; +is_safe(_) -> false. diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl index 688bba9a94..c83c686953 100644 --- a/lib/compiler/src/beam_split.erl +++ b/lib/compiler/src/beam_split.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -46,6 +47,8 @@ split_block([{set,[R],[_,_,_]=As,{bif,is_record,{f,Lbl}}}|Is], Bl, Acc) -> split_block(Is, [], [{bif,is_record,{f,Lbl},As,R}|make_block(Bl, Acc)]); split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block(Is, [], [{bif,N,Fail,As,R}|make_block(Bl, Acc)]); +split_block([{set,[R],As,{bif,raise,{f,_}=Fail}}|Is], Bl, Acc) -> + split_block(Is, [], [{bif,raise,Fail,As,R}|make_block(Bl, Acc)]); split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]); @@ -53,11 +56,11 @@ split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block(Is, [], [{put_map,Fail,Op,S,D,R,{list,Puts}}| make_block(Bl, Acc)]); -split_block([{set,Gets,[S],{get_map_elements,{f,Lbl}=Fail}}|Is], Bl, Acc) - when Lbl =/= 0 -> +split_block([{set,Ds,[S|Ss],{get_map_elements,Fail}}|Is], Bl, Acc) -> + Gets = beam_utils:join_even(Ss,Ds), split_block(Is, [], [{get_map_elements,Fail,S,{list,Gets}}|make_block(Bl, Acc)]); -split_block([{set,[R],[],{'catch',L}}|Is], Bl, Acc) -> - split_block(Is, [], [{'catch',R,L}|make_block(Bl, Acc)]); +split_block([{set,[R],[],{try_catch,Op,L}}|Is], Bl, Acc) -> + split_block(Is, [], [{Op,R,L}|make_block(Bl, Acc)]); split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) -> split_block(Is, [], [Line|make_block(Bl, Acc)]); split_block([I|Is], Bl, Acc) -> diff --git a/lib/compiler/src/beam_trim.erl b/lib/compiler/src/beam_trim.erl index fad9c42584..a8dc6805bc 100644 --- a/lib/compiler/src/beam_trim.erl +++ b/lib/compiler/src/beam_trim.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -172,6 +173,10 @@ remap([{bif,Name,Fail,Ss,D}|Is], Map, Acc) -> remap([{gc_bif,Name,Fail,Live,Ss,D}|Is], Map, Acc) -> I = {gc_bif,Name,Fail,Live,[Map(S) || S <- Ss],Map(D)}, remap(Is, Map, [I|Acc]); +remap([{get_map_elements,Fail,M,{list,L0}}|Is], Map, Acc) -> + L = [Map(E) || E <- L0], + I = {get_map_elements,Fail,Map(M),{list,L}}, + remap(Is, Map, [I|Acc]); remap([{bs_init,Fail,Info,Live,Ss0,Dst0}|Is], Map, Acc) -> Ss = [Map(Src) || Src <- Ss0], Dst = Map(Dst0), @@ -275,6 +280,8 @@ frame_size([{kill,_}|Is], Safe) -> frame_size(Is, Safe); frame_size([{make_fun2,_,_,_,_}|Is], Safe) -> frame_size(Is, Safe); +frame_size([{get_map_elements,{f,L},_,_}|Is], Safe) -> + frame_size_branch(L, Is, Safe); frame_size([{deallocate,N}|_], _) -> N; frame_size([{line,_}|Is], Safe) -> frame_size(Is, Safe); diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl index 58c0f765ae..acaf3ede66 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -22,7 +23,8 @@ -export([module/2]). --import(lists, [foldl/3,reverse/1,filter/2]). +-import(lists, [filter/2,foldl/3,keyfind/3,member/2, + reverse/1,reverse/2,sort/1]). module({Mod,Exp,Attr,Fs0,Lc}, _Opts) -> Fs = [function(F) || F <- Fs0], @@ -91,8 +93,19 @@ simplify_basic_1([{set,[D],[TupleReg],{get_tuple_element,0}}=I|Is0], Ts0, Acc) - Ts = update(I, Ts0), simplify_basic_1(Is0, Ts, [I|Acc]) end; -simplify_basic_1([{set,_,_,{'catch',_}}=I|Is], _Ts, Acc) -> +simplify_basic_1([{set,_,_,{try_catch,_,_}}=I|Is], _Ts, Acc) -> simplify_basic_1(Is, tdb_new(), [I|Acc]); +simplify_basic_1([{test,is_atom,_,[R]}=I|Is], Ts, Acc) -> + case tdb_find(R, Ts) of + boolean -> simplify_basic_1(Is, Ts, Acc); + _ -> simplify_basic_1(Is, Ts, [I|Acc]) + end; +simplify_basic_1([{test,is_integer,_,[R]}=I|Is], Ts, Acc) -> + case tdb_find(R, Ts) of + integer -> simplify_basic_1(Is, Ts, Acc); + {integer,_} -> simplify_basic_1(Is, Ts, Acc); + _ -> simplify_basic_1(Is, Ts, [I|Acc]) + end; simplify_basic_1([{test,is_tuple,_,[R]}=I|Is], Ts, Acc) -> case tdb_find(R, Ts) of {tuple,_,_} -> simplify_basic_1(Is, Ts, Acc); @@ -106,6 +119,20 @@ simplify_basic_1([{test,test_arity,_,[R,Arity]}=I|Is], Ts0, Acc) -> Ts = update(I, Ts0), simplify_basic_1(Is, Ts, [I|Acc]) end; +simplify_basic_1([{test,is_map,_,[R]}=I|Is], Ts0, Acc) -> + case tdb_find(R, Ts0) of + map -> simplify_basic_1(Is, Ts0, Acc); + _Other -> + Ts = update(I, Ts0), + simplify_basic_1(Is, Ts, [I|Acc]) + end; +simplify_basic_1([{test,is_nonempty_list,_,[R]}=I|Is], Ts0, Acc) -> + case tdb_find(R, Ts0) of + nonempty_list -> simplify_basic_1(Is, Ts0, Acc); + _Other -> + Ts = update(I, Ts0), + simplify_basic_1(Is, Ts, [I|Acc]) + end; simplify_basic_1([{test,is_eq_exact,Fail,[R,{atom,_}=Atom]}=I|Is0], Ts0, Acc0) -> Acc = case tdb_find(R, Ts0) of {atom,_}=Atom -> Acc0; @@ -122,6 +149,16 @@ simplify_basic_1([{test,is_record,_,[R,{atom,_}=Tag,{integer,Arity}]}=I|Is], Ts0 Ts = update(I, Ts0), simplify_basic_1(Is, Ts, [I|Acc]) end; +simplify_basic_1([{select,select_val,Reg,_,_}=I0|Is], Ts, Acc) -> + I = case tdb_find(Reg, Ts) of + {integer,Range} -> + simplify_select_val_int(I0, Range); + boolean -> + simplify_select_val_bool(I0); + _ -> + I0 + end, + simplify_basic_1(Is, tdb_new(), [I|Acc]); simplify_basic_1([I|Is], Ts0, Acc) -> Ts = update(I, Ts0), simplify_basic_1(Is, Ts, [I|Acc]); @@ -129,15 +166,42 @@ simplify_basic_1([], Ts, Acc) -> Is = reverse(Acc), {Is,Ts}. +simplify_select_val_int({select,select_val,R,_,L0}=I, {Min,Max}) -> + Vs = sort([V || {integer,V} <- L0]), + case eq_ranges(Vs, Min, Max) of + false -> I; + true -> simplify_select_val_1(L0, {integer,Max}, R, []) + end. + +simplify_select_val_bool({select,select_val,R,_,L}=I) -> + Vs = sort([V || {atom,V} <- L]), + case Vs of + [false,true] -> + simplify_select_val_1(L, {atom,false}, R, []); + _ -> + I + end. + +simplify_select_val_1([Val,F|T], Val, R, Acc) -> + L = reverse(Acc, T), + {select,select_val,R,F,L}; +simplify_select_val_1([V,F|T], Val, R, Acc) -> + simplify_select_val_1(T, Val, R, [F,V|Acc]). + +eq_ranges([H], H, H) -> true; +eq_ranges([H|T], H, Max) -> eq_ranges(T, H+1, Max); +eq_ranges(_, _, _) -> false. + %% simplify_float([Instruction], TypeDatabase) -> %% {[Instruction],TypeDatabase'} | not_possible %% Simplify floating point operations in blocks. %% simplify_float(Is0, Ts0) -> {Is1,Ts} = simplify_float_1(Is0, Ts0, [], []), - Is2 = flt_need_heap(Is1), + Is2 = opt_fmoves(Is1, []), + Is3 = flt_need_heap(Is2), try - {flt_liveness(Is2),Ts} + {flt_liveness(Is3),Ts} catch throw:not_possible -> not_possible end. @@ -183,19 +247,20 @@ simplify_float_1([{set,[D0],[A0,B0],{alloc,_,{gc_bif,Op0,{f,0}}}}=I|Is]=Is0, Ts = tdb_update([{D0,float}], Ts0), simplify_float_1(Is, Ts, Rs, Acc) end; -simplify_float_1([{set,_,_,{'catch',_}}=I|Is]=Is0, _Ts, Rs0, Acc0) -> +simplify_float_1([{set,_,_,{try_catch,_,_}}=I|Is]=Is0, _Ts, Rs0, Acc0) -> Acc = flush_all(Rs0, Is0, Acc0), simplify_float_1(Is, tdb_new(), Rs0, [I|Acc]); simplify_float_1([{set,_,_,{line,_}}=I|Is], Ts, Rs, Acc) -> simplify_float_1(Is, Ts, Rs, [I|Acc]); +simplify_float_1([I|Is], Ts0, [], Acc) -> + Ts = update(I, Ts0), + simplify_float_1(Is, Ts, [], [I|Acc]); simplify_float_1([I|Is]=Is0, Ts0, Rs0, Acc0) -> Ts = update(I, Ts0), {Rs,Acc} = flush(Rs0, Is0, Acc0), simplify_float_1(Is, Ts, Rs, [I|checkerror(Acc)]); -simplify_float_1([], Ts, Rs, Acc0) -> - Acc = checkerror(Acc0), - Is0 = reverse(flush_all(Rs, [], Acc)), - Is = opt_fmoves(Is0, []), +simplify_float_1([], Ts, [], Acc) -> + Is = reverse(Acc), {Is,Ts}. coerce_to_float({integer,I}=Int) -> @@ -230,7 +295,7 @@ clearerror([], OrigIs) -> [{set,[],[],fclearerror}|OrigIs]. %% Combine two blocks and eliminate any move instructions that assign %% to registers that are killed later in the block. %% -merge_blocks(B1, [{'%live',_}|B2]) -> +merge_blocks(B1, [{'%live',_,_}|B2]) -> merge_blocks_1(B1++[{set,[],[],stop_here}|B2]). merge_blocks_1([{set,[],_,stop_here}|Is]) -> Is; @@ -294,7 +359,7 @@ flt_need_heap_2({set,_,_,{get_tuple_element,_}}, H, Fl) -> {[],H,Fl}; flt_need_heap_2({set,_,_,get_list}, H, Fl) -> {[],H,Fl}; -flt_need_heap_2({set,_,_,{'catch',_}}, H, Fl) -> +flt_need_heap_2({set,_,_,{try_catch,_,_}}, H, Fl) -> {[],H,Fl}; %% All other instructions should cause the insertion of an allocation %% instruction if needed. @@ -315,27 +380,27 @@ build_alloc(Words, Floats) -> {alloc,[{words,Words},{floats,Floats}]}. %% flt_liveness([Instruction]) -> [Instruction] %% (Re)calculate the number of live registers for each heap allocation -%% function. We base liveness of the number of live registers at -%% entry to the instruction sequence. +%% function. We base liveness of the number of register map at the +%% beginning of the instruction sequence. %% %% A 'not_possible' term will be thrown if the set of live registers %% is not continous at an allocation function (e.g. if {x,0} and {x,2} %% are live, but not {x,1}). -flt_liveness([{'%live',Live}=LiveInstr|Is]) -> - flt_liveness_1(Is, init_regs(Live), [LiveInstr]). +flt_liveness([{'%live',_Live,Regs}=LiveInstr|Is]) -> + flt_liveness_1(Is, Regs, [LiveInstr]). -flt_liveness_1([{set,Ds,Ss,{alloc,_,Alloc}}|Is], Regs0, Acc) -> - Live = live_regs(Regs0), +flt_liveness_1([{set,Ds,Ss,{alloc,Live0,Alloc}}|Is], Regs0, Acc) -> + Live = min(Live0, live_regs(Regs0)), I = {set,Ds,Ss,{alloc,Live,Alloc}}, - Regs = foldl(fun(R, A) -> set_live(R, A) end, Regs0, Ds), + Regs1 = init_regs(Live), + Regs = x_live(Ds, Regs1), flt_liveness_1(Is, Regs, [I|Acc]); flt_liveness_1([{set,Ds,_,_}=I|Is], Regs0, Acc) -> - Regs = foldl(fun(R, A) -> set_live(R, A) end, Regs0, Ds), - flt_liveness_1(Is, Regs, [I|Acc]); -flt_liveness_1([{'%live',_}=I|Is], Regs, Acc) -> + Regs = x_live(Ds, Regs0), flt_liveness_1(Is, Regs, [I|Acc]); -flt_liveness_1([], _Regs, Acc) -> reverse(Acc). +flt_liveness_1([{'%live',_,_}], _Regs, Acc) -> + reverse(Acc). init_regs(Live) -> (1 bsl Live) - 1. @@ -350,20 +415,32 @@ live_regs_1(R, N) -> 1 -> live_regs_1(R bsr 1, N+1) end. -set_live({x,X}, Regs) -> Regs bor (1 bsl X); -set_live(_, Regs) -> Regs. +x_live([{x,N}|Rs], Regs) -> x_live(Rs, Regs bor (1 bsl N)); +x_live([_|Rs], Regs) -> x_live(Rs, Regs); +x_live([], Regs) -> Regs. %% update(Instruction, TypeDb) -> NewTypeDb %% Update the type database to account for executing an instruction. %% %% First the cases for instructions inside basic blocks. -update({'%live',_}, Ts) -> Ts; +update({'%live',_,_}, Ts) -> Ts; update({set,[D],[S],move}, Ts) -> tdb_copy(S, D, Ts); update({set,[D],[{integer,I},Reg],{bif,element,_}}, Ts0) -> tdb_update([{Reg,{tuple,I,[]}},{D,kill}], Ts0); update({set,[D],[_Index,Reg],{bif,element,_}}, Ts0) -> tdb_update([{Reg,{tuple,0,[]}},{D,kill}], Ts0); +update({set,[D],Args,{bif,N,_}}, Ts0) -> + Ar = length(Args), + BoolOp = erl_internal:new_type_test(N, Ar) orelse + erl_internal:comp_op(N, Ar) orelse + erl_internal:bool_op(N, Ar), + case BoolOp of + true -> + tdb_update([{D,boolean}], Ts0); + false -> + tdb_update([{D,kill}], Ts0) + end; update({set,[D],[S],{get_tuple_element,0}}, Ts) -> tdb_update([{D,{tuple_element,S,0}}], Ts); update({set,[D],[S],{alloc,_,{gc_bif,float,{f,0}}}}, Ts0) -> @@ -372,6 +449,13 @@ update({set,[D],[S],{alloc,_,{gc_bif,float,{f,0}}}}, Ts0) -> true -> tdb_update([{D,float}], Ts0); false -> Ts0 end; +update({set,[D],[S1,S2],{alloc,_,{gc_bif,'band',{f,0}}}}, Ts) -> + case keyfind(integer, 1, [S1,S2]) of + {integer,N} -> + update_band(N, D, Ts); + false -> + tdb_update([{D,integer}], Ts) + end; update({set,[D],[S1,S2],{alloc,_,{gc_bif,'/',{f,0}}}}, Ts0) -> %% Make sure we reject non-numeric literals. case possibly_numeric(S1) andalso possibly_numeric(S2) of @@ -379,15 +463,17 @@ update({set,[D],[S1,S2],{alloc,_,{gc_bif,'/',{f,0}}}}, Ts0) -> false -> Ts0 end; update({set,[D],[S1,S2],{alloc,_,{gc_bif,Op,{f,0}}}}, Ts0) -> - case arith_op(Op) of - no -> - tdb_update([{D,kill}], Ts0); - {yes,_} -> + case op_type(Op) of + integer -> + tdb_update([{D,integer}], Ts0); + {float,_} -> case {tdb_find(S1, Ts0),tdb_find(S2, Ts0)} of {float,_} -> tdb_update([{D,float}], Ts0); {_,float} -> tdb_update([{D,float}], Ts0); {_,_} -> tdb_update([{D,kill}], Ts0) - end + end; + unknown -> + tdb_update([{D,kill}], Ts0) end; update({set,[],_Src,_Op}, Ts0) -> Ts0; update({set,[D],_Src,_Op}, Ts0) -> @@ -402,6 +488,10 @@ update({test,is_float,_Fail,[Src]}, Ts0) -> tdb_update([{Src,float}], Ts0); update({test,test_arity,_Fail,[Src,Arity]}, Ts0) -> tdb_update([{Src,{tuple,Arity,[]}}], Ts0); +update({test,is_map,_Fail,[Src]}, Ts0) -> + tdb_update([{Src,map}], Ts0); +update({test,is_nonempty_list,_Fail,[Src]}, Ts0) -> + tdb_update([{Src,nonempty_list}], Ts0); update({test,is_eq_exact,_,[Reg,{atom,_}=Atom]}, Ts) -> case tdb_find(Reg, Ts) of error -> @@ -415,26 +505,72 @@ update({test,is_record,_Fail,[Src,Tag,{integer,Arity}]}, Ts) -> tdb_update([{Src,{tuple,Arity,[Tag]}}], Ts); update({test,_Test,_Fail,_Other}, Ts) -> Ts; +update({test,bs_get_integer2,_,_,Args,Dst}, Ts) -> + tdb_update([{Dst,get_bs_integer_type(Args)}], Ts); update({call_ext,Ar,{extfunc,math,Math,Ar}}, Ts) -> case is_math_bif(Math, Ar) of true -> tdb_update([{{x,0},float}], Ts); false -> tdb_kill_xregs(Ts) end; update({call_ext,3,{extfunc,erlang,setelement,3}}, Ts0) -> - Op = case tdb_find({x,1}, Ts0) of - error -> kill; - Info -> Info - end, - Ts1 = tdb_kill_xregs(Ts0), - tdb_update([{{x,0},Op}], Ts1); + Ts = tdb_kill_xregs(Ts0), + case tdb_find({x,1}, Ts0) of + {tuple,Sz,_}=T0 -> + T = case tdb_find({x,0}, Ts0) of + {integer,{I,I}} when I > 1 -> + %% First element is not changed. The result + %% will have the same type. + T0; + _ -> + %% Position is 1 or unknown. May change the + %% first element of the tuple. + {tuple,Sz,[]} + end, + tdb_update([{{x,0},T}], Ts); + _ -> + Ts + end; update({call,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts); update({call_ext,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts); update({make_fun2,_,_,_,_}, Ts) -> tdb_kill_xregs(Ts); update({line,_}, Ts) -> Ts; +update({bs_save2,_,_}, Ts) -> Ts; +update({bs_restore2,_,_}, Ts) -> Ts; %% The instruction is unknown. Kill all information. update(_I, _Ts) -> tdb_new(). +update_band(N, Reg, Ts) -> + Type = update_band_1(N, 0), + tdb_update([{Reg,Type}], Ts). + +update_band_1(N, Bits) when Bits < 64 -> + case 1 bsl Bits of + P when P =:= N + 1 -> + {integer,{0,N}}; + P when P > N + 1 -> + integer; + _ -> + update_band_1(N, Bits+1) + end; +update_band_1(_, _) -> + %% Negative or large positive number. Give up. + integer. + +get_bs_integer_type([_,{integer,N},U,{field_flags,Fl}]) + when N*U < 64 -> + NumBits = N*U, + case member(unsigned, Fl) of + true -> + {integer,{0,(1 bsl NumBits)-1}}; + false -> + %% Signed integer. Don't bother. + integer + end; +get_bs_integer_type(_) -> + %% Avoid creating ranges with a huge upper limit. + integer. + is_math_bif(cos, 1) -> true; is_math_bif(cosh, 1) -> true; is_math_bif(sin, 1) -> true; @@ -451,6 +587,7 @@ is_math_bif(erf, 1) -> true; is_math_bif(erfc, 1) -> true; is_math_bif(exp, 1) -> true; is_math_bif(log, 1) -> true; +is_math_bif(log2, 1) -> true; is_math_bif(log10, 1) -> true; is_math_bif(sqrt, 1) -> true; is_math_bif(atan2, 2) -> true; @@ -522,20 +659,31 @@ load_reg(V, Ts, Rs0, Is0) -> {Rs,Is} end. -arith_op('+') -> {yes,fadd}; -arith_op('-') -> {yes,fsub}; -arith_op('*') -> {yes,fmul}; -arith_op('/') -> {yes,fdiv}; -arith_op(_) -> no. +arith_op(Op) -> + case op_type(Op) of + {float,Instr} -> {yes,Instr}; + _ -> no + end. + +op_type('+') -> {float,fadd}; +op_type('-') -> {float,fsub}; +op_type('*') -> {float,fmul}; +%% '/' and 'band' are specially handled. +op_type('bor') -> integer; +op_type('bxor') -> integer; +op_type('bsl') -> integer; +op_type('bsr') -> integer; +op_type('div') -> integer; +op_type(_) -> unknown. flush(Rs, [{set,[_],[],{put_tuple,_}}|_]=Is0, Acc0) -> Acc = flush_all(Rs, Is0, Acc0), {[],Acc}; flush(Rs0, [{set,Ds,Ss,_Op}|_], Acc0) -> - Save = gb_sets:from_list(Ss), + Save = cerl_sets:from_list(Ss), Acc = save_regs(Rs0, Save, Acc0), Rs1 = foldl(fun(S, A) -> mark(S, A, clean) end, Rs0, Ss), - Kill = gb_sets:from_list(Ds), + Kill = cerl_sets:from_list(Ds), Rs = kill_regs(Rs1, Kill), {Rs,Acc}; flush(Rs0, Is, Acc0) -> @@ -558,7 +706,7 @@ save_regs(Rs, Save, Acc) -> foldl(fun(R, A) -> save_reg(R, Save, A) end, Acc, Rs). save_reg({I,V,dirty}, Save, Acc) -> - case gb_sets:is_member(V, Save) of + case cerl_sets:is_element(V, Save) of true -> [{set,[V],[{fr,I}],fmove}|checkerror(Acc)]; false -> Acc end; @@ -568,7 +716,7 @@ kill_regs(Rs, Kill) -> [kill_reg(R, Kill) || R <- Rs]. kill_reg({_,V,_}=R, Kill) -> - case gb_sets:is_member(V, Kill) of + case cerl_sets:is_element(V, Kill) of true -> free; false -> R end; @@ -595,7 +743,6 @@ checkerror(Is) -> checkerror_1(Is, Is). checkerror_1([{set,[],[],fcheckerror}|_], OrigIs) -> OrigIs; -checkerror_1([{set,[],[],fclearerror}|_], OrigIs) -> OrigIs; checkerror_1([{set,_,_,{bif,fadd,_}}|_], OrigIs) -> checkerror_2(OrigIs); checkerror_1([{set,_,_,{bif,fsub,_}}|_], OrigIs) -> checkerror_2(OrigIs); checkerror_1([{set,_,_,{bif,fmul,_}}|_], OrigIs) -> checkerror_2(OrigIs); @@ -612,11 +759,14 @@ checkerror_2(OrigIs) -> [{set,[],[],fcheckerror}|OrigIs]. %%% %%% {tuple,Size,First} means that the corresponding register contains a %%% tuple with *at least* Size elements. An tuple with unknown -%%% size is represented as {tuple,0}. First is either [] (meaning that +%%% size is represented as {tuple,0,[]}. First is either [] (meaning that %%% the tuple's first element is unknown) or [FirstElement] (the contents %%% of the first element). %%% %%% 'float' means that the register contains a float. +%%% +%%% 'integer' or {integer,{Min,Max}} that the register contains an +%%% integer. %% tdb_new() -> EmptyDataBase %% Creates a new, empty type database. @@ -646,21 +796,45 @@ tdb_copy({Tag,_}=S, D, Ts) when Tag =:= x; Tag =:= y -> error -> orddict:erase(D, Ts); Type -> orddict:store(D, Type, Ts) end; -tdb_copy(Literal, D, Ts) -> orddict:store(D, Literal, Ts). +tdb_copy(Literal, D, Ts) -> + Type = case Literal of + {atom,_} -> Literal; + {float,_} -> float; + {integer,Int} -> {integer,{Int,Int}}; + {literal,[_|_]} -> nonempty_list; + {literal,#{}} -> map; + {literal,Tuple} when tuple_size(Tuple) >= 1 -> + Lit = tag_literal(element(1, Tuple)), + {tuple,tuple_size(Tuple),[Lit]}; + _ -> term + end, + if + Type =:= term -> + orddict:erase(D, Ts); + true -> + verify_type(Type), + orddict:store(D, Type, Ts) + end. + +tag_literal(A) when is_atom(A) -> {atom,A}; +tag_literal(F) when is_float(F) -> {float,F}; +tag_literal(I) when is_integer(I) -> {integer,I}; +tag_literal([]) -> nil; +tag_literal(Lit) -> {literal,Lit}. %% tdb_update([UpdateOp], Db) -> NewDb %% UpdateOp = {Register,kill}|{Register,NewInfo} %% Updates a type database. If a 'kill' operation is given, the type %% information for that register will be removed from the database. %% A kill operation takes precedence over other operations for the same -%% register (i.e. [{{x,0},kill},{{x,0},{tuple,5}}] means that the +%% register (i.e. [{{x,0},kill},{{x,0},{tuple,5,[]}}] means that the %% the existing type information, if any, will be discarded, and the -%% the '{tuple,5}' information ignored. +%% the '{tuple,5,[]}' information ignored. %% %% If NewInfo information is given and there exists information about %% the register, the old and new type information will be merged. -%% For instance, {tuple,5} and {tuple,10} will be merged to produce -%% {tuple,10}. +%% For instance, {tuple,5,_} and {tuple,10,_} will be merged to produce +%% {tuple,10,_}. tdb_update(Uis0, Ts0) -> Uis1 = filter(fun ({{x,_},_Op}) -> true; @@ -671,7 +845,8 @@ tdb_update(Uis0, Ts0) -> tdb_update1([{Key,kill}|Ops], [{K,_Old}|_]=Db) when Key < K -> tdb_update1(remove_key(Key, Ops), Db); -tdb_update1([{Key,_New}=New|Ops], [{K,_Old}|_]=Db) when Key < K -> +tdb_update1([{Key,Type}=New|Ops], [{K,_Old}|_]=Db) when Key < K -> + verify_type(Type), [New|tdb_update1(Ops, Db)]; tdb_update1([{Key,kill}|Ops], [{Key,_}|Db]) -> tdb_update1(remove_key(Key, Ops), Db); @@ -681,7 +856,8 @@ tdb_update1([{_,_}|_]=Ops, [Old|Db]) -> [Old|tdb_update1(Ops, Db)]; tdb_update1([{Key,kill}|Ops], []) -> tdb_update1(remove_key(Key, Ops), []); -tdb_update1([{_,_}=New|Ops], []) -> +tdb_update1([{_,Type}=New|Ops], []) -> + verify_type(Type), [New|tdb_update1(Ops, [])]; tdb_update1([], Db) -> Db. @@ -706,10 +882,23 @@ merge_type_info({tuple,Sz1,[]}, {tuple,_Sz2,First}=Tuple2) -> merge_type_info({tuple,Sz1,First}, Tuple2); merge_type_info({tuple,_Sz1,First}=Tuple1, {tuple,Sz2,_}) -> merge_type_info(Tuple1, {tuple,Sz2,First}); +merge_type_info(integer, {integer,_}=Int) -> + Int; +merge_type_info({integer,_}=Int, integer) -> + Int; +merge_type_info({integer,{Min1,Max1}}, {integer,{Min2,Max2}}) -> + {integer,{max(Min1, Min2),min(Max1, Max2)}}; merge_type_info(NewType, _) -> verify_type(NewType), NewType. +verify_type({atom,_}) -> ok; +verify_type(boolean) -> ok; +verify_type(integer) -> ok; +verify_type({integer,{Min,Max}}) + when is_integer(Min), is_integer(Max) -> ok; +verify_type(map) -> ok; +verify_type(nonempty_list) -> ok; verify_type({tuple,Sz,[]}) when is_integer(Sz) -> ok; verify_type({tuple,Sz,[_]}) when is_integer(Sz) -> ok; verify_type({tuple_element,_,_}) -> ok; diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 8ca368c167..a15ecf633e 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -24,7 +25,8 @@ is_not_used/3,is_not_used_at/3, empty_label_index/0,index_label/3,index_labels/1, code_at/2,bif_to_test/3,is_pure_test/1, - live_opt/1,delete_live_annos/1,combine_heap_needs/2]). + live_opt/1,delete_live_annos/1,combine_heap_needs/2, + join_even/2,split_even/1]). -import(lists, [member/2,sort/1,reverse/1,splitwith/2]). @@ -64,8 +66,7 @@ is_killed(R, Is, D) -> St = #live{bl=check_killed_block_fun(),lbl=D,res=gb_trees:empty()}, case check_liveness(R, Is, St) of {killed,_} -> true; - {used,_} -> false; - {unknown,_} -> false + {used,_} -> false end. %% is_killed_at(Reg, Lbl, State) -> true|false @@ -75,8 +76,7 @@ is_killed_at(R, Lbl, D) when is_integer(Lbl) -> St0 = #live{bl=check_killed_block_fun(),lbl=D,res=gb_trees:empty()}, case check_liveness_at(R, Lbl, St0) of {killed,_} -> true; - {used,_} -> false; - {unknown,_} -> false + {used,_} -> false end. %% is_not_used(Register, [Instruction], State) -> true|false @@ -90,8 +90,7 @@ is_not_used(R, Is, D) -> St = #live{bl=fun check_used_block/3,lbl=D,res=gb_trees:empty()}, case check_liveness(R, Is, St) of {killed,_} -> true; - {used,_} -> false; - {unknown,_} -> false + {used,_} -> false end. %% is_not_used(Register, [Instruction], State) -> true|false @@ -105,8 +104,7 @@ is_not_used_at(R, Lbl, D) -> St = #live{bl=fun check_used_block/3,lbl=D,res=gb_trees:empty()}, case check_liveness_at(R, Lbl, St) of {killed,_} -> true; - {used,_} -> false; - {unknown,_} -> false + {used,_} -> false end. %% index_labels(FunctionIs) -> State @@ -126,8 +124,7 @@ empty_label_index() -> %% Add an index for a label. index_label(Lbl, Is0, Acc) -> - Is = lists:dropwhile(fun({label,_}) -> true; - (_) -> false end, Is0), + Is = drop_labels(Is0), gb_trees:enter(Lbl, Is, Acc). @@ -135,10 +132,7 @@ index_label(Lbl, Is0, Acc) -> %% Retrieve the code at the given label. code_at(L, Ll) -> - case gb_trees:lookup(L, Ll) of - {value,Code} -> Code; - none -> none - end. + gb_trees:get(L, Ll). %% bif_to_test(Bif, [Op], Fail) -> {test,Test,Fail,[Op]} %% Convert a BIF to a test. Fail if not possible. @@ -162,10 +156,10 @@ bif_to_test('=<', [A,B], Fail) -> {test,is_ge,Fail,[B,A]}; bif_to_test('>', [A,B], Fail) -> {test,is_lt,Fail,[B,A]}; bif_to_test('<', [_,_]=Ops, Fail) -> {test,is_lt,Fail,Ops}; bif_to_test('>=', [_,_]=Ops, Fail) -> {test,is_ge,Fail,Ops}; -bif_to_test('==', [A,[]], Fail) -> {test,is_nil,Fail,[A]}; +bif_to_test('==', [A,nil], Fail) -> {test,is_nil,Fail,[A]}; bif_to_test('==', [_,_]=Ops, Fail) -> {test,is_eq,Fail,Ops}; bif_to_test('/=', [_,_]=Ops, Fail) -> {test,is_ne,Fail,Ops}; -bif_to_test('=:=', [A,[]], Fail) -> {test,is_nil,Fail,[A]}; +bif_to_test('=:=', [A,nil], Fail) -> {test,is_nil,Fail,[A]}; bif_to_test('=:=', [_,_]=Ops, Fail) -> {test,is_eq_exact,Fail,Ops}; bif_to_test('=/=', [_,_]=Ops, Fail) -> {test,is_ne_exact,Fail,Ops}; bif_to_test(is_record, [_,_,_]=Ops, Fail) -> {test,is_record,Fail,Ops}. @@ -173,8 +167,7 @@ bif_to_test(is_record, [_,_,_]=Ops, Fail) -> {test,is_record,Fail,Ops}. %% is_pure_test({test,Op,Fail,Ops}) -> true|false. %% Return 'true' if the test instruction does not modify any -%% registers and/or bit syntax matching state, nor modifies -%% any bit syntax matching state. +%% registers and/or bit syntax matching state. %% is_pure_test({test,is_eq,_,[_,_]}) -> true; is_pure_test({test,is_ne,_,[_,_]}) -> true; @@ -185,7 +178,9 @@ is_pure_test({test,is_lt,_,[_,_]}) -> true; is_pure_test({test,is_nil,_,[_]}) -> true; is_pure_test({test,is_nonempty_list,_,[_]}) -> true; is_pure_test({test,test_arity,_,[_,_]}) -> true; -is_pure_test({test,has_map_fields,_,[_,{list,_}]}) -> true; +is_pure_test({test,has_map_fields,_,[_|_]}) -> true; +is_pure_test({test,is_bitstr,_,[_]}) -> true; +is_pure_test({test,is_function2,_,[_,_]}) -> true; is_pure_test({test,Op,_,Ops}) -> erl_internal:new_type_test(Op, length(Ops)). @@ -194,7 +189,7 @@ is_pure_test({test,Op,_,Ops}) -> %% Go through the instruction sequence in reverse execution %% order, keep track of liveness and remove 'move' instructions %% whose destination is a register that will not be used. -%% Also insert {'%live',Live} annotations at the beginning +%% Also insert {'%live',Live,Regs} annotations at the beginning %% and end of each block. %% live_opt(Is0) -> @@ -215,7 +210,7 @@ delete_live_annos([{block,Bl0}|Is]) -> [] -> delete_live_annos(Is); [_|_]=Bl -> [{block,Bl}|delete_live_annos(Is)] end; -delete_live_annos([{'%live',_}|Is]) -> +delete_live_annos([{'%live',_,_}|Is]) -> delete_live_annos(Is); delete_live_annos([I|Is]) -> [I|delete_live_annos(Is)]; @@ -233,21 +228,28 @@ combine_heap_needs(Words, {alloc,Alloc}) when is_integer(Words) -> combine_heap_needs(H1, H2) when is_integer(H1), is_integer(H2) -> H1+H2. +%% split_even/1 +%% [1,2,3,4,5,6] -> {[1,3,5],[2,4,6]} + +split_even(Rs) -> split_even(Rs, [], []). + +%% join_even/1 +%% {[1,3,5],[2,4,6]} -> [1,2,3,4,5,6] + +join_even([], []) -> []; +join_even([S|Ss], [D|Ds]) -> [S,D|join_even(Ss, Ds)]. + %%% %%% Local functions. %%% -%% check_liveness(Reg, [Instruction], {State,BlockCheckFun}) -> -%% {killed | used | unknown,UpdateState} -%% Finds out how Reg is used in the instruction sequence. Returns one of: -%% killed - Reg is assigned a new value or killed by an allocation instruction -%% used - Reg is used (or possibly referenced by an allocation instruction) -%% unknown - not possible to determine (perhaps because of an instruction -%% that we don't recognize) +%% check_liveness(Reg, [Instruction], #live{}) -> +%% {killed | used, #live{}} +%% Find out whether Reg is used or killed in instruction sequence. +%% 'killed' means that Reg is assigned a new value or killed by an +%% allocation instruction. 'used' means that Reg is used in some way. -check_liveness(R, [{set,_,_,_}=I|_], St) -> - erlang:error(only_allowed_in_blocks, [R,I,St]); check_liveness(R, [{block,Blk}|Is], #live{bl=BlockCheck}=St0) -> case BlockCheck(R, Blk, St0) of {transparent,St} -> check_liveness(R, Is, St); @@ -323,8 +325,11 @@ check_liveness(R, [{deallocate,_}|Is], St) -> {y,_} -> {killed,St}; _ -> check_liveness(R, Is, St) end; -check_liveness(R, [return|_], St) -> - check_liveness_live_ret(R, 1, St); +check_liveness({x,_}=R, [return|_], St) -> + case R of + {x,0} -> {used,St}; + {x,_} -> {killed,St} + end; check_liveness(R, [{call,Live,_}|Is], St) -> case R of {x,X} when X < Live -> {used,St}; @@ -342,14 +347,10 @@ check_liveness(R, [{call_ext,Live,_}=I|Is], St) -> false -> check_liveness(R, Is, St); true -> - %% We must make sure we don't check beyond this instruction - %% or we will fall through into random unrelated code and - %% get stuck in a loop. - %% - %% We don't want to overwrite a 'catch', so consider this - %% register in use. - %% - {used,St} + %% We must make sure we don't check beyond this + %% instruction or we will fall through into random + %% unrelated code and get stuck in a loop. + {killed,St} end end; check_liveness(R, [{call_fun,Live}|Is], St) -> @@ -364,11 +365,6 @@ check_liveness(R, [{apply,Args}|Is], St) -> {x,_} -> {killed,St}; {y,_} -> check_liveness(R, Is, St) end; -check_liveness({x,R}, [{'%live',Live}|Is], St) -> - if - R < Live -> check_liveness(R, Is, St); - true -> {killed,St} - end; check_liveness(R, [{bif,Op,{f,Fail},Ss,D}|Is], St0) -> case check_liveness_fail(R, Op, Ss, Fail, St0) of {killed,St} = Killed -> @@ -468,20 +464,54 @@ check_liveness(R, [{loop_rec,{f,_},{x,0}}|_], St) -> {x,_} -> {killed,St}; _ -> - %% y register. Rarely happens. Be very conversative. - {unknown,St} + %% y register. Rarely happens. Be very conversative and + %% assume it's used. + {used,St} end; check_liveness(R, [{loop_rec_end,{f,Fail}}|_], St) -> check_liveness_at(R, Fail, St); check_liveness(R, [{line,_}|Is], St) -> check_liveness(R, Is, St); +check_liveness(R, [{get_map_elements,{f,Fail},S,{list,L}}|Is], St0) -> + {Ss,Ds} = split_even(L), + case member(R, [S|Ss]) of + true -> + {used,St0}; + false -> + case check_liveness_at(R, Fail, St0) of + {killed,St}=Killed -> + case member(R, Ds) of + true -> Killed; + false -> check_liveness(R, Is, St) + end; + Other -> + Other + end + end; +check_liveness(R, [{put_map,{f,_},_,Src,_D,Live,{list,_}}|_], St0) -> + case R of + Src -> + {used,St0}; + {x,X} when X < Live -> + {used,St0}; + {x,_} -> + {killed,St0}; + {y,_} -> + %% Conservatively mark it as used. + {used,St0} + end; +check_liveness(R, [{test_heap,N,Live}|Is], St) -> + I = {block,[{set,[],[],{alloc,Live,{nozero,nostack,N,[]}}}]}, + check_liveness(R, [I|Is], St); +check_liveness(R, [{allocate_zero,N,Live}|Is], St) -> + I = {block,[{set,[],[],{alloc,Live,{zero,N,0,[]}}}]}, + check_liveness(R, [I|Is], St); +check_liveness(R, [{get_list,S,D1,D2}|Is], St) -> + I = {block,[{set,[D1,D2],[S],get_list}]}, + check_liveness(R, [I|Is], St); check_liveness(_R, Is, St) when is_list(Is) -> -%% case Is of -%% [I|_] -> -%% io:format("~p ~p\n", [_R,I]); -%% _ -> ok -%% end, - {unknown,St}. + %% Not implemented. Conservatively assume that the register is used. + {used,St}. check_liveness_everywhere(R, [{f,Lbl}|T], St0) -> case check_liveness_at(R, Lbl, St0) of @@ -500,7 +530,7 @@ check_liveness_at(R, Lbl, #live{lbl=Ll,res=ResMemorized}=St0) -> none -> {Res,St} = case gb_trees:lookup(Lbl, Ll) of {value,Is} -> check_liveness(R, Is, St0); - none -> {unknown,St0} + none -> {used,St0} end, {Res,St#live{res=gb_trees:insert(Lbl, Res, St#live.res)}} end. @@ -508,14 +538,6 @@ check_liveness_at(R, Lbl, #live{lbl=Ll,res=ResMemorized}=St0) -> check_liveness_ret(R, R, St) -> {used,St}; check_liveness_ret(_, _, St) -> {killed,St}. -check_liveness_live_ret({x,R}, Live, St) -> - if - R < Live -> {used,St}; - true -> {killed,St} - end; -check_liveness_live_ret({y,_}, _, St) -> - {killed,St}. - check_liveness_fail(_, _, _, 0, St) -> {killed,St}; check_liveness_fail(R, Op, Args, Fail, St) -> @@ -552,9 +574,9 @@ check_killed_block(R, [{set,Ds,Ss,_Op}|Is]) -> false -> check_killed_block(R, Is) end end; -check_killed_block(R, [{'%live',Live}|Is]) -> +check_killed_block(R, [{'%live',_,Regs}|Is]) -> case R of - {x,X} when X >= Live -> killed; + {x,X} when (Regs bsr X) band 1 =:= 0 -> killed; _ -> check_killed_block(R, Is) end; check_killed_block(_, []) -> transparent. @@ -565,8 +587,10 @@ check_killed_block(_, []) -> transparent. %% killed - Reg is assigned a new value or killed by an allocation instruction %% transparent - Reg is neither used nor killed %% used - Reg is explicitly used by an instruction -%% -%% (Unknown instructions will cause an exception.) +%% +%% '%live' annotations are not allowed. +%% +%% (Unknown instructions will cause an exception.) check_used_block({x,X}=R, [{set,Ds,Ss,{alloc,Live,Op}}|Is], St) -> if @@ -575,11 +599,6 @@ check_used_block({x,X}=R, [{set,Ds,Ss,{alloc,Live,Op}}|Is], St) -> end; check_used_block(R, [{set,Ds,Ss,Op}|Is], St) -> check_used_block_1(R, Ss, Ds, Op, Is, St); -check_used_block(R, [{'%live',Live}|Is], St) -> - case R of - {x,X} when X >= Live -> {killed,St}; - _ -> check_used_block(R, Is, St) - end; check_used_block(_, [], St) -> {transparent,St}. check_used_block_1(R, Ss, Ds, Op, Is, St0) -> @@ -610,18 +629,19 @@ is_reg_used_at_1(_, 0, St) -> is_reg_used_at_1(R, Lbl, St0) -> case check_liveness_at(R, Lbl, St0) of {killed,St} -> {false,St}; - {used,St} -> {true,St}; - {unknown,St} -> {true,St} + {used,St} -> {true,St} end. index_labels_1([{label,Lbl}|Is0], Acc) -> - Is = lists:dropwhile(fun({label,_}) -> true; - (_) -> false end, Is0), + Is = drop_labels(Is0), index_labels_1(Is0, [{Lbl,Is}|Acc]); index_labels_1([_|Is], Acc) -> index_labels_1(Is, Acc); index_labels_1([], Acc) -> gb_trees:from_orddict(sort(Acc)). +drop_labels([{label,_}|Is]) -> drop_labels(Is); +drop_labels(Is) -> Is. + %% Help functions for combine_heap_needs. combine_alloc_lists(Al1, Al2) -> @@ -676,9 +696,9 @@ live_opt([{test,bs_start_match2,Fail,Live,[Src,_],_}=I|Is], _, D, Acc) -> %% Other instructions. live_opt([{block,Bl0}|Is], Regs0, D, Acc) -> - Live0 = {'%live',live_regs(Regs0)}, + Live0 = {'%live',live_regs(Regs0),Regs0}, {Bl,Regs} = live_opt_block(reverse(Bl0), Regs0, D, [Live0]), - Live = {'%live',live_regs(Regs)}, + Live = {'%live',live_regs(Regs),Regs}, live_opt(Is, Regs, D, [{block,[Live|Bl]}|Acc]); live_opt([{label,L}=I|Is], Regs, D0, Acc) -> D = gb_trees:insert(L, Regs, D0), @@ -725,11 +745,6 @@ live_opt([{select,_,Src,Fail,List}=I|Is], Regs0, D, Acc) -> Regs1 = x_live([Src], Regs0), Regs = live_join_labels([Fail|List], D, Regs1), live_opt(Is, Regs, D, [I|Acc]); -live_opt([{'try',_,_}=I|Is], Regs, D, Acc) -> - %% If an exeption happens, all x registers will be killed. - %% Therefore, we should only base liveness of the code inside - %% the try. - live_opt(Is, Regs, D, [I|Acc]); live_opt([{try_case,_}=I|Is], _, D, Acc) -> live_opt(Is, live_call(1), D, [I|Acc]); live_opt([{loop_rec,_Fail,_Dst}=I|Is], _, D, Acc) -> @@ -756,13 +771,9 @@ live_opt([{line,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); %% The following instructions can occur if the "compilation" has been -%% started from a .S file using the 'asm' option. +%% started from a .S file using the 'from_asm' option. live_opt([{trim,_,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); -live_opt([{allocate,_,Live}=I|Is], _, D, Acc) -> - live_opt(Is, live_call(Live), D, [I|Acc]); -live_opt([{allocate_heap,_,_,Live}=I|Is], _, D, Acc) -> - live_opt(Is, live_call(Live), D, [I|Acc]); live_opt([{'%',_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); live_opt([{recv_set,_}=I|Is], Regs, D, Acc) -> @@ -832,3 +843,8 @@ x_live([_|Rs], Regs) -> x_live(Rs, Regs); x_live([], Regs) -> Regs. is_live(X, Regs) -> ((Regs bsr X) band 1) =:= 1. + +split_even([], Ss, Ds) -> + {reverse(Ss),reverse(Ds)}; +split_even([S,D|Rs], Ss, Ds) -> + split_even(Rs, [S|Ss], [D|Ds]). diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 9d5563d13b..4c0cb6780a 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2014. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% @@ -22,56 +23,20 @@ %% Avoid warning for local function error/1 clashing with autoimported BIF. -compile({no_auto_import,[error/1]}). --export([file/1, files/1]). %% Interface for compiler. -export([module/2, format_error/1]). -include("beam_disasm.hrl"). --import(lists, [reverse/1,foldl/3,foreach/2,member/2,dropwhile/2]). - --define(MAXREG, 1024). - -%%-define(DEBUG, 1). --ifdef(DEBUG). --define(DBG_FORMAT(F, D), (io:format((F), (D)))). --else. --define(DBG_FORMAT(F, D), ok). --endif. - -%%% -%%% API functions. -%%% - --spec file(file:filename()) -> 'ok' | {'error', term()}. - -file(Name) when is_list(Name) -> - case case filename:extension(Name) of - ".S" -> s_file(Name); - ".beam" -> beam_file(Name) - end of - [] -> ok; - Es -> {error,Es} - end. - --spec files([file:filename()]) -> 'ok'. - -files([F|Fs]) -> - ?DBG_FORMAT("# Verifying: ~p~n", [F]), - case file(F) of - ok -> ok; - {error,Es} -> - io:format("~tp:~n~ts~n", [F,format_error(Es)]) - end, - files(Fs); -files([]) -> ok. +-import(lists, [reverse/1,foldl/3,foreach/2,dropwhile/2]). %% To be called by the compiler. module({Mod,Exp,Attr,Fs,Lc}=Code, _Opts) when is_atom(Mod), is_list(Exp), is_list(Attr), is_integer(Lc) -> case validate(Mod, Fs) of - [] -> {ok,Code}; + [] -> + {ok,Code}; Es0 -> Es = [{?MODULE,E} || E <- Es0], {error,[{atom_to_list(Mod),Es}]} @@ -79,12 +44,6 @@ module({Mod,Exp,Attr,Fs,Lc}=Code, _Opts) -spec format_error(term()) -> iolist(). -format_error([]) -> []; -format_error([{{M,F,A},{I,Off,Desc}}|Es]) -> - [io_lib:format(" ~p:~p/~p+~p:~n ~p - ~p~n", - [M,F,A,Off,I,Desc])|format_error(Es)]; -format_error([Error|Es]) -> - [format_error(Error)|format_error(Es)]; format_error({{_M,F,A},{I,Off,limit}}) -> io_lib:format( "function ~p/~p+~p:~n" @@ -103,8 +62,6 @@ format_error({{_M,F,A},{I,Off,Desc}}) -> " Internal consistency check failed - please report this bug.~n" " Instruction: ~p~n" " Error: ~p:~n", [F,A,Off,I,Desc]); -format_error({Module,Error}) -> - [Module:format_error(Error)]; format_error(Error) -> io_lib:format("~p~n", [Error]). @@ -112,36 +69,6 @@ format_error(Error) -> %%% Local functions follow. %%% -s_file(Name) -> - {ok,Is} = file:consult(Name), - {module,Module} = lists:keyfind(module, 1, Is), - Fs = find_functions(Is), - validate(Module, Fs). - -find_functions(Fs) -> - find_functions_1(Fs, none, [], []). - -find_functions_1([{function,Name,Arity,Entry}|Is], Func, FuncAcc, Acc0) -> - Acc = add_func(Func, FuncAcc, Acc0), - find_functions_1(Is, {Name,Arity,Entry}, [], Acc); -find_functions_1([I|Is], Func, FuncAcc, Acc) -> - find_functions_1(Is, Func, [I|FuncAcc], Acc); -find_functions_1([], Func, FuncAcc, Acc) -> - reverse(add_func(Func, FuncAcc, Acc)). - -add_func(none, _, Acc) -> Acc; -add_func({Name,Arity,Entry}, Is, Acc) -> - [{function,Name,Arity,Entry,reverse(Is)}|Acc]. - -beam_file(Name) -> - try beam_disasm:file(Name) of - {error,beam_lib,Reason} -> [{beam_lib,Reason}]; - #beam_file{module=Module, code=Code0} -> - Code = normalize_disassembled_code(Code0), - validate(Module, Code) - catch _:_ -> [disassembly_failed] - end. - %%% %%% The validator follows. %%% @@ -196,23 +123,16 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) -> try validate_1(Code, Name, Ar, Entry, Ft) of _ -> validate_0(Module, Fs, Ft) catch - Error -> + throw:Error -> + %% Controlled error. [Error|validate_0(Module, Fs, Ft)]; - error:Error -> - [validate_error(Error, Module, Name, Ar)|validate_0(Module, Fs, Ft)] + Class:Error -> + %% Crash. + Stack = erlang:get_stacktrace(), + io:fwrite("Function: ~w/~w\n", [Name,Ar]), + erlang:raise(Class, Error, Stack) end. --ifdef(DEBUG). -validate_error(Error, Module, Name, Ar) -> - exit(validate_error_1(Error, Module, Name, Ar)). --else. -validate_error(Error, Module, Name, Ar) -> - validate_error_1(Error, Module, Name, Ar). --endif. -validate_error_1(Error, Module, Name, Ar) -> - {{Module,Name,Ar}, - {internal_error,'_',{Error,erlang:get_stacktrace()}}}. - -type index() :: non_neg_integer(). -type reg_tab() :: gb_trees:tree(index(), 'none' | {'value', _}). @@ -225,8 +145,6 @@ validate_error_1(Error, Module, Name, Ar) -> hf=0, %Available heap size for floats. fls=undefined, %Floating point state. ct=[], %List of hot catch/try labels - bsm=undefined, %Bit syntax matching state. - bits=undefined, %Number of bits in bit syntax binary. setelem=false %Previous instruction was setelement/3. }). @@ -243,29 +161,25 @@ validate_error_1(Error, Module, Name, Ar) -> % in the module (those that start with bs_start_match2). }). --ifdef(DEBUG). -print_st(#st{x=Xs,y=Ys,numy=NumY,h=H,ct=Ct}) -> - io:format(" #st{x=~p~n" - " y=~p~n" - " numy=~p,h=~p,ct=~w~n", - [gb_trees:to_list(Xs),gb_trees:to_list(Ys),NumY,H,Ct]). --endif. +%% Match context type. +-record(ms, + {id=make_ref() :: reference(), %Unique ID. + valid=0 :: non_neg_integer(), %Valid slots + slots=0 :: non_neg_integer() %Number of slots + }). validate_1(Is, Name, Arity, Entry, Ft) -> validate_2(labels(Is), Name, Arity, Entry, Ft). validate_2({Ls1,[{func_info,{atom,Mod},{atom,Name},Arity}=_F|Is]}, Name, Arity, Entry, Ft) -> - lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [{label,_L}]) end, Ls1), - ?DBG_FORMAT(" ~p.~n", [_F]), validate_3(labels(Is), Name, Arity, Entry, Mod, Ls1, Ft); validate_2({Ls1,Is}, Name, Arity, _Entry, _Ft) -> error({{'_',Name,Arity},{first(Is),length(Ls1),illegal_instruction}}). validate_3({Ls2,Is}, Name, Arity, Entry, Mod, Ls1, Ft) -> - lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [{label,_L}]) end, Ls2), Offset = 1 + length(Ls1) + 1 + length(Ls2), - EntryOK = (Entry =:= undefined) orelse lists:member(Entry, Ls2), + EntryOK = lists:member(Entry, Ls2), if EntryOK -> St = init_state(Arity), @@ -308,7 +222,7 @@ labels_1([{label,L}|Is], R) -> labels_1([{line,_}|Is], R) -> labels_1(Is, R); labels_1(Is, R) -> - {lists:reverse(R),Is}. + {reverse(R),Is}. init_state(Arity) -> Xs = init_regs(Arity, term), @@ -333,7 +247,6 @@ valfun([], MFA, _Offset, #vst{branched=Targets0,labels=Labels0}=Vst) -> error({MFA,Error}) end; valfun([I|Is], MFA, Offset, Vst0) -> - ?DBG_FORMAT(" ~p.\n", [I]), valfun(Is, MFA, Offset+1, try Vst = val_dsetel(I, Vst0), @@ -351,7 +264,6 @@ valfun_1({label,Lbl}, #vst{current=St0,branched=B,labels=Lbls}=Vst) -> valfun_1(_I, #vst{current=none}=Vst) -> %% Ignore instructions after erlang:error/1,2, which %% the original R10B compiler thought would return. - ?DBG_FORMAT("Ignoring ~p\n", [_I]), Vst; valfun_1({badmatch,Src}, Vst) -> assert_term(Src, Vst), @@ -369,7 +281,7 @@ valfun_1({bs_context_to_binary,Ctx}, #vst{current=#st{x=Xs}}=Vst) -> case Ctx of {Tag,X} when Tag =:= x; Tag =:= y -> Type = case gb_trees:lookup(X, Xs) of - {value,{match_context,_,_}} -> term; + {value,#ms{}} -> term; _ -> get_term_type(Ctx, Vst) end, set_type_reg(Type, Ctx, Vst); @@ -403,10 +315,6 @@ valfun_1({init,{y,_}=Reg}, Vst) -> set_type_y(initialized, Reg, Vst); valfun_1({test_heap,Heap,Live}, Vst) -> test_heap(Heap, Live, Vst); -valfun_1({bif,_Op,nofail,Src,Dst}, Vst) -> - %% The 'nofail' atom only occurs in disassembled code. - validate_src(Src, Vst), - set_type_reg(term, Dst, Vst); valfun_1({bif,Op,{f,_},Src,Dst}=I, Vst) -> case is_bif_safe(Op, length(Src)) of false -> @@ -432,18 +340,12 @@ valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) -> valfun_1({put,Src}, Vst) -> assert_term(Src, Vst), eat_heap(1, Vst); -valfun_1({put_string,Sz,_,Dst}, Vst0) when is_integer(Sz) -> - Vst = eat_heap(2*Sz, Vst0), - set_type_reg(cons, Dst, Vst); %% Instructions for optimization of selective receives. valfun_1({recv_mark,{f,Fail}}, Vst) when is_integer(Fail) -> Vst; valfun_1({recv_set,{f,Fail}}, Vst) when is_integer(Fail) -> Vst; %% Misc. -valfun_1({'%live',Live}, Vst) -> - verify_live(Live, Vst), - Vst; valfun_1(remove_message, Vst) -> Vst; valfun_1({'%',_}, Vst) -> @@ -494,37 +396,33 @@ valfun_1({'try',Dst,{f,Fail}}, Vst0) -> Vst = #vst{current=#st{ct=Fails}=St} = set_type_y({trytag,[Fail]}, Dst, Vst0), Vst#vst{current=St#st{ct=[[Fail]|Fails]}}; -valfun_1({catch_end,Reg}, #vst{current=#st{ct=[Fail|Fails]}=St0}=Vst0) -> +valfun_1({catch_end,Reg}, #vst{current=#st{ct=[Fail|Fails]}}=Vst0) -> case get_special_y_type(Reg, Vst0) of {catchtag,Fail} -> - Vst = #vst{current=St} = - set_type_y(initialized_ct, Reg, - Vst0#vst{current=St0#st{ct=Fails}}), + Vst = #vst{current=St} = set_catch_end(Reg, Vst0), Xs = gb_trees_from_list([{0,term}]), - Vst#vst{current=St#st{x=Xs,fls=undefined}}; + Vst#vst{current=St#st{x=Xs,ct=Fails,fls=undefined}}; Type -> error({bad_type,Type}) end; -valfun_1({try_end,Reg}, #vst{current=#st{ct=[Fail|Fails]}=St}=Vst0) -> +valfun_1({try_end,Reg}, #vst{current=#st{ct=[Fail|Fails]}=St0}=Vst0) -> case get_special_y_type(Reg, Vst0) of {trytag,Fail} -> Vst = case Fail of [FailLabel] -> branch_state(FailLabel, Vst0); _ -> Vst0 end, - set_type_reg(initialized_ct, Reg, - Vst#vst{current=St#st{ct=Fails,fls=undefined}}); + St = St0#st{ct=Fails,fls=undefined}, + set_catch_end(Reg, Vst#vst{current=St}); Type -> error({bad_type,Type}) end; -valfun_1({try_case,Reg}, #vst{current=#st{ct=[Fail|Fails]}=St0}=Vst0) -> +valfun_1({try_case,Reg}, #vst{current=#st{ct=[Fail|Fails]}}=Vst0) -> case get_special_y_type(Reg, Vst0) of {trytag,Fail} -> - Vst = #vst{current=St} = - set_type_y(initialized_ct, Reg, - Vst0#vst{current=St0#st{ct=Fails}}), - Xs = gb_trees_from_list([{0,{atom,[]}},{1,term},{2,term}]), %XXX - Vst#vst{current=St#st{x=Xs,fls=undefined}}; + Vst = #vst{current=St} = set_catch_end(Reg, Vst0), + Xs = gb_trees_from_list([{0,{atom,[]}},{1,term},{2,term}]), + Vst#vst{current=St#st{x=Xs,ct=Fails,fls=undefined}}; Type -> error({bad_type,Type}) end; @@ -602,8 +500,6 @@ valfun_4({call_ext_last,Live,Func,StkSize}, tail_call(Func, Live, Vst); valfun_4({call_ext_last,_,_,_}, #vst{current=#st{numy=NumY}}) -> error({allocated,NumY}); -valfun_4({make_fun,_,_,Live}, Vst) -> - call('fun', Live, Vst); valfun_4({make_fun2,_,_,_,Live}, Vst) -> call(make_fun, Live, Vst); %% Other BIFs @@ -620,8 +516,9 @@ valfun_4({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) -> TupleType = upgrade_tuple_type({tuple,[get_tuple_size(PosType)]}, TupleType0), Vst = set_type(TupleType, Tuple, Vst1), set_type_reg(term, Dst, Vst); -valfun_4({raise,{f,_}=Fail,Src,Dst}, Vst) -> - valfun_4({bif,raise,Fail,Src,Dst}, Vst); +valfun_4({bif,raise,{f,0},Src,_Dst}, Vst) -> + validate_src(Src, Vst), + kill_state(Vst); valfun_4({bif,Op,{f,Fail},Src,Dst}, Vst0) -> validate_src(Src, Vst0), Vst = branch_state(Fail, Vst0), @@ -685,7 +582,7 @@ valfun_4({test,bs_start_match2,{f,Fail},Live,[Ctx,NeedSlots],Ctx}, Vst0) -> verify_live(Live, Vst0), Vst1 = prune_x_regs(Live, Vst0), BranchVst = case CtxType of - {match_context,_,_} -> + #ms{} -> %% The failure branch will never be taken when Ctx %% is a match context. Therefore, the type for Ctx %% at the failure label must not be match_context @@ -738,32 +635,6 @@ valfun_4({bs_save2,Ctx,SavePoint}, Vst) -> valfun_4({bs_restore2,Ctx,SavePoint}, Vst) -> bsm_restore(Ctx, SavePoint, Vst); -%% Bit syntax instructions. -valfun_4({bs_start_match,{f,_Fail}=F,Src}, Vst) -> - valfun_4({test,bs_start_match,F,[Src]}, Vst); -valfun_4({test,bs_start_match,{f,Fail},[Src]}, Vst) -> - assert_term(Src, Vst), - bs_start_match(branch_state(Fail, Vst)); - -valfun_4({bs_save,SavePoint}, Vst) -> - bs_assert_state(Vst), - bs_save(SavePoint, Vst); -valfun_4({bs_restore,SavePoint}, Vst) -> - bs_assert_state(Vst), - bs_assert_savepoint(SavePoint, Vst), - Vst; -valfun_4({test,bs_skip_bits,{f,Fail},[Src,_,_]}, Vst) -> - bs_assert_state(Vst), - assert_term(Src, Vst), - branch_state(Fail, Vst); -valfun_4({test,bs_test_tail,{f,Fail},_}, Vst) -> - bs_assert_state(Vst), - branch_state(Fail, Vst); -valfun_4({test,_,{f,Fail},[_,_,_,Dst]}, Vst0) -> - bs_assert_state(Vst0), - Vst = branch_state(Fail, Vst0), - set_type_reg({integer,[]}, Dst, Vst); - %% Other test instructions. valfun_4({test,is_float,{f,Lbl},[Float]}, Vst) -> assert_term(Float, Vst), @@ -779,9 +650,19 @@ valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) -> assert_type(tuple, Tuple, Vst), set_type_reg({tuple,Sz}, Tuple, branch_state(Lbl, Vst)); valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) -> - validate_src([Src], Vst), - assert_strict_literal_termorder(List), + assert_type(map, Src, Vst), + assert_unique_map_keys(List), branch_state(Lbl, Vst); +valfun_4({test,is_map,{f,Lbl},[Src]}, Vst0) -> + Vst = branch_state(Lbl, Vst0), + case Src of + {Tag,_} when Tag =:= x; Tag =:= y -> + set_type_reg(map, Src, Vst); + {literal,Map} when is_map(Map) -> + Vst; + _ -> + kill_state(Vst) + end; valfun_4({test,_Op,{f,Lbl},Src}, Vst) -> validate_src(Src, Vst), branch_state(Lbl, Vst); @@ -795,9 +676,6 @@ valfun_4({bs_utf8_size,{f,Fail},A,Dst}, Vst) -> valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) -> assert_term(A, Vst), set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst)); -valfun_4({bs_bits_to_bytes,{f,Fail},Src,Dst}, Vst) -> - assert_term(Src, Vst), - set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst)); valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) -> verify_live(Live, Vst0), if @@ -808,8 +686,7 @@ valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) -> end, Vst1 = heap_alloc(Heap, Vst0), Vst2 = branch_state(Fail, Vst1), - Vst3 = prune_x_regs(Live, Vst2), - Vst = bs_zero_bits(Vst3), + Vst = prune_x_regs(Live, Vst2), set_type_reg(binary, Dst, Vst); valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) -> verify_live(Live, Vst0), @@ -821,8 +698,7 @@ valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) -> end, Vst1 = heap_alloc(Heap, Vst0), Vst2 = branch_state(Fail, Vst1), - Vst3 = prune_x_regs(Live, Vst2), - Vst = bs_zero_bits(Vst3), + Vst = prune_x_regs(Live, Vst2), set_type_reg(binary, Dst, Vst); valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) -> verify_live(Live, Vst0), @@ -830,54 +706,36 @@ valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) -> assert_term(Bin, Vst0), Vst1 = heap_alloc(Heap, Vst0), Vst2 = branch_state(Fail, Vst1), - Vst3 = prune_x_regs(Live, Vst2), - Vst = bs_zero_bits(Vst3), + Vst = prune_x_regs(Live, Vst2), set_type_reg(binary, Dst, Vst); valfun_4({bs_private_append,{f,Fail},Bits,_Unit,Bin,_Flags,Dst}, Vst0) -> assert_term(Bits, Vst0), assert_term(Bin, Vst0), - Vst1 = branch_state(Fail, Vst0), - Vst = bs_zero_bits(Vst1), + Vst = branch_state(Fail, Vst0), set_type_reg(binary, Dst, Vst); valfun_4({bs_put_string,Sz,_}, Vst) when is_integer(Sz) -> Vst; -valfun_4({bs_put_binary,{f,Fail},Sz,_,_,Src}=I, Vst0) -> - assert_term(Sz, Vst0), - assert_term(Src, Vst0), - Vst = bs_align_check(I, Vst0), +valfun_4({bs_put_binary,{f,Fail},Sz,_,_,Src}, Vst) -> + assert_term(Sz, Vst), + assert_term(Src, Vst), branch_state(Fail, Vst); -valfun_4({bs_put_float,{f,Fail},Sz,_,_,Src}=I, Vst0) -> - assert_term(Sz, Vst0), - assert_term(Src, Vst0), - Vst = bs_align_check(I, Vst0), +valfun_4({bs_put_float,{f,Fail},Sz,_,_,Src}, Vst) -> + assert_term(Sz, Vst), + assert_term(Src, Vst), branch_state(Fail, Vst); -valfun_4({bs_put_integer,{f,Fail},Sz,_,_,Src}=I, Vst0) -> - assert_term(Sz, Vst0), - assert_term(Src, Vst0), - Vst = bs_align_check(I, Vst0), +valfun_4({bs_put_integer,{f,Fail},Sz,_,_,Src}, Vst) -> + assert_term(Sz, Vst), + assert_term(Src, Vst), branch_state(Fail, Vst); -valfun_4({bs_put_utf8,{f,Fail},_,Src}=I, Vst0) -> - assert_term(Src, Vst0), - Vst = bs_align_check(I, Vst0), +valfun_4({bs_put_utf8,{f,Fail},_,Src}, Vst) -> + assert_term(Src, Vst), branch_state(Fail, Vst); -valfun_4({bs_put_utf16,{f,Fail},_,Src}=I, Vst0) -> - assert_term(Src, Vst0), - Vst = bs_align_check(I, Vst0), +valfun_4({bs_put_utf16,{f,Fail},_,Src}, Vst) -> + assert_term(Src, Vst), branch_state(Fail, Vst); -valfun_4({bs_put_utf32,{f,Fail},_,Src}=I, Vst0) -> - assert_term(Src, Vst0), - Vst = bs_align_check(I, Vst0), +valfun_4({bs_put_utf32,{f,Fail},_,Src}, Vst) -> + assert_term(Src, Vst), branch_state(Fail, Vst); -%% Old bit syntax construction (before R10B). -valfun_4({bs_init,_,_}, Vst) -> - bs_zero_bits(Vst); -valfun_4({bs_need_buf,_}, Vst) -> Vst; -valfun_4({bs_final,{f,Fail},Dst}, Vst0) -> - Vst = branch_state(Fail, Vst0), - set_type_reg(binary, Dst, Vst); -valfun_4({bs_final2,Src,Dst}, Vst0) -> - assert_term(Src, Vst0), - set_type_reg(binary, Dst, Vst0); %% Map instructions. valfun_4({put_map_assoc,{f,Fail},Src,Dst,Live,{list,List}}, Vst) -> verify_put_map(Fail, Src, Dst, Live, List, Vst); @@ -889,11 +747,25 @@ valfun_4(_, _) -> error(unknown_instruction). verify_get_map(Fail, Src, List, Vst0) -> - assert_term(Src, Vst0), - Vst1 = branch_state(Fail, Vst0), - Lits = mmap(fun(L,_R) -> [L] end, List), - assert_strict_literal_termorder(Lits), - verify_get_map_pair(List,Vst0,Vst1). + assert_type(map, Src, Vst0), + Vst1 = foldl(fun(D, Vsti) -> + case is_reg_defined(D,Vsti) of + true -> set_type_reg(term,D,Vsti); + false -> Vsti + end + end, Vst0, extract_map_vals(List)), + Vst2 = branch_state(Fail, Vst1), + Keys = extract_map_keys(List), + assert_unique_map_keys(Keys), + verify_get_map_pair(List,Vst0,Vst2). + +extract_map_vals([_Key,Val|T]) -> + [Val|extract_map_vals(T)]; +extract_map_vals([]) -> []. + +extract_map_keys([Key,_Val|T]) -> + [Key|extract_map_keys(T)]; +extract_map_keys([]) -> []. verify_get_map_pair([],_,Vst) -> Vst; verify_get_map_pair([Src,Dst|Vs],Vst0,Vsti) -> @@ -901,14 +773,16 @@ verify_get_map_pair([Src,Dst|Vs],Vst0,Vsti) -> verify_get_map_pair(Vs,Vst0,set_type_reg(term,Dst,Vsti)). verify_put_map(Fail, Src, Dst, Live, List, Vst0) -> + assert_type(map, Src, Vst0), verify_live(Live, Vst0), verify_y_init(Vst0), foreach(fun (Term) -> assert_term(Term, Vst0) end, List), - assert_term(Src, Vst0), Vst1 = heap_alloc(0, Vst0), Vst2 = branch_state(Fail, Vst1), Vst = prune_x_regs(Live, Vst2), - set_type_reg(term, Dst, Vst). + Keys = extract_map_keys(List), + assert_unique_map_keys(Keys), + set_type_reg(map, Dst, Vst). %% %% Common code for validating bs_get* instructions. @@ -936,9 +810,6 @@ validate_bs_skip_utf(Fail, Ctx, Live, Vst0) -> %% val_dsetel({move,_,_}, Vst) -> Vst; -val_dsetel({put_string,0,{string,""},_}, Vst) -> - %% An empty string is OK since it doesn't build anything. - Vst; val_dsetel({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=St}=Vst) -> Vst#vst{current=St#st{setelem=true}}; val_dsetel({set_tuple_element,_,_,_}, #vst{current=#st{setelem=false}}) -> @@ -966,56 +837,86 @@ kill_state_1(Vst) -> %% The stackframe must be initialized. %% The instruction will return to the instruction following the call. call(Name, Live, #vst{current=St}=Vst) -> - verify_live(Live, Vst), + verify_call_args(Name, Live, Vst), verify_y_init(Vst), case return_type(Name, Vst) of Type when Type =/= exception -> %% Type is never 'exception' because it has been handled earlier. Xs = gb_trees_from_list([{0,Type}]), - Vst#vst{current=St#st{x=Xs,f=init_fregs(),bsm=undefined}} + Vst#vst{current=St#st{x=Xs,f=init_fregs()}} end. %% Tail call. %% The stackframe must have a known size and be initialized. %% Does not return to the instruction following the call. -tail_call(Name, Live, Vst) -> +tail_call(Name, Live, Vst0) -> + verify_y_init(Vst0), + Vst = deallocate(Vst0), verify_call_args(Name, Live, Vst), - verify_y_init(Vst), verify_no_ct(Vst), kill_state(Vst). verify_call_args(_, 0, #vst{}) -> ok; verify_call_args({f,Lbl}, Live, Vst) when is_integer(Live)-> - Verify = fun(R) -> - case get_move_term_type(R, Vst) of - {match_context,_,_} -> - verify_call_match_context(Lbl, Vst); - _ -> - ok - end - end, - verify_call_args_1(Live, Verify, Vst); + verify_local_call(Lbl, Live, Vst); verify_call_args(_, Live, Vst) when is_integer(Live)-> - Verify = fun(R) -> get_term_type(R, Vst) end, - verify_call_args_1(Live, Verify, Vst); + verify_call_args_1(Live, Vst); verify_call_args(_, Live, _) -> error({bad_number_of_live_regs,Live}). -verify_call_args_1(0, _, _) -> ok; -verify_call_args_1(N, Verify, Vst) -> +verify_call_args_1(0, _) -> ok; +verify_call_args_1(N, Vst) -> X = N - 1, - Verify({x,X}), - verify_call_args_1(X, Verify, Vst). + get_term_type({x,X}, Vst), + verify_call_args_1(X, Vst). + +verify_local_call(Lbl, Live, Vst) -> + case all_ms_in_x_regs(Live, Vst) of + [{R,Ctx}] -> + %% Verify that there is a suitable bs_start_match2 instruction. + verify_call_match_context(Lbl, R, Vst), + + %% Since the callee has consumed the match context, + %% there must be no additional copies in Y registers. + #ms{id=Id} = Ctx, + case ms_in_y_regs(Id, Vst) of + [] -> + ok; + [_|_]=Ys -> + error({multiple_match_contexts,[R|Ys]}) + end; + [_,_|_]=Xs0 -> + Xs = [R || {R,_} <- Xs0], + error({multiple_match_contexts,Xs}); + [] -> + ok + end. + +all_ms_in_x_regs(0, _Vst) -> + []; +all_ms_in_x_regs(Live0, Vst) -> + Live = Live0 - 1, + R = {x,Live}, + case get_move_term_type(R, Vst) of + #ms{}=M -> + [{R,M}|all_ms_in_x_regs(Live, Vst)]; + _ -> + all_ms_in_x_regs(Live, Vst) + end. -verify_call_match_context(Lbl, #vst{ft=Ft}) -> +ms_in_y_regs(Id, #vst{current=#st{y=Ys0}}) -> + Ys = gb_trees:to_list(Ys0), + [Y || {Y,#ms{id=OtherId}} <- Ys, OtherId =:= Id]. + +verify_call_match_context(Lbl, Ctx, #vst{ft=Ft}) -> case gb_trees:lookup(Lbl, Ft) of none -> error(no_bs_start_match2); {value,[{test,bs_start_match2,_,_,[Ctx,_],Ctx}|_]} -> ok; - {value,[{test,bs_start_match2,_,_,[Bin,_,_],Ctx}|_]} -> - error({binary_and_context_regs_different,Bin,Ctx}) + {value,[{test,bs_start_match2,_,_,_,_}=I|_]} -> + error({unsuitable_bs_start_match2,I}) end. allocate(Zero, Stk, Heap, Live, #vst{current=#st{numy=none}=St}=Vst0) -> @@ -1030,7 +931,7 @@ allocate(_, _, _, _, #vst{current=#st{numy=Numy}}) -> error({existing_stack_frame,{size,Numy}}). deallocate(#vst{current=St}=Vst) -> - Vst#vst{current=St#st{y=init_regs(0, initialized),numy=none,bsm=undefined}}. + Vst#vst{current=St#st{y=init_regs(0, initialized),numy=none}}. test_heap(Heap, Live, Vst0) -> verify_live(Live, Vst0), @@ -1038,7 +939,7 @@ test_heap(Heap, Live, Vst0) -> heap_alloc(Heap, Vst). heap_alloc(Heap, #vst{current=St0}=Vst) -> - St1 = kill_heap_allocation(St0#st{bsm=undefined}), + St1 = kill_heap_allocation(St0), St = heap_alloc_1(Heap, St1), Vst#vst{current=St}. @@ -1099,9 +1000,9 @@ get_fls(#vst{current=#st{fls=Fls}}) when is_atom(Fls) -> Fls. init_fregs() -> 0. -set_freg({fr,Fr}, #vst{current=#st{f=Fregs0}=St}=Vst) +set_freg({fr,Fr}=Freg, #vst{current=#st{f=Fregs0}=St}=Vst) when is_integer(Fr), 0 =< Fr -> - limit_check(Fr), + check_limit(Freg), Bit = 1 bsl Fr, if Fregs0 band Bit =:= 0 -> @@ -1114,87 +1015,40 @@ set_freg(Fr, _) -> error({bad_target,Fr}). assert_freg_set({fr,Fr}=Freg, #vst{current=#st{f=Fregs}}) when is_integer(Fr), 0 =< Fr -> if - Fregs band (1 bsl Fr) =/= 0 -> - limit_check(Fr); - true -> error({uninitialized_reg,Freg}) + (Fregs bsr Fr) band 1 =:= 0 -> + error({uninitialized_reg,Freg}); + true -> + ok end; assert_freg_set(Fr, _) -> error({bad_source,Fr}). %%% Maps -%% ensure that a list of literals has a strict -%% ascending term order (also meaning unique literals) -assert_strict_literal_termorder(Ls) -> - Vs = lists:map(fun (L) -> get_literal(L) end, Ls), - case check_strict_value_termorder(Vs) of - true -> ok; - false -> error({not_strict_order, Ls}) - end. - -%% usage: -%% mmap(fun(A,B) -> [{A,B}] end, [1,2,3,4]), -%% [{1,2},{3,4}] - -mmap(F,List) -> - {arity,Ar} = erlang:fun_info(F,arity), - mmap(F,Ar,List). -mmap(_F,_,[]) -> []; -mmap(F,Ar,List) -> - {Hd,Tl} = lists:split(Ar,List), - apply(F,Hd) ++ mmap(F,Ar,Tl). - -check_strict_value_termorder([]) -> true; -check_strict_value_termorder([_]) -> true; -check_strict_value_termorder([V1,V2]) -> - erts_internal:cmp_term(V1,V2) < 0; -check_strict_value_termorder([V1,V2|Vs]) -> - case erts_internal:cmp_term(V1,V2) < 0 of - true -> check_strict_value_termorder([V2|Vs]); - false -> false - end. +%% A single item list may be either a list or a register. +%% +%% A list with more than item must contain unique literals. +%% +%% An empty list is not allowed. -%%% -%%% Binary matching. -%%% -%%% Possible values for the bsm field (=bit syntax matching state). -%%% -%%% undefined - Undefined (initial state). No matching instructions allowed. -%%% -%%% (gb set) - The gb set contains the defined save points. -%%% -%%% The bsm field is reset to 'undefined' by instructions that may cause a -%%% a garbage collection (might move the binary) and/or context switch -%%% (may invalidate the save points). - -bs_start_match(#vst{current=#st{bsm=undefined}=St}=Vst) -> - Vst#vst{current=St#st{bsm=gb_sets:empty()}}; -bs_start_match(Vst) -> - %% Must retain save points here - it is possible to restore back - %% to a previous binary. - Vst. - -bs_save(Reg, #vst{current=#st{bsm=Saved}=St}=Vst) - when is_integer(Reg), Reg < ?MAXREG -> - Vst#vst{current=St#st{bsm=gb_sets:add(Reg, Saved)}}; -bs_save(_, _) -> error(limit). - -bs_assert_savepoint(Reg, #vst{current=#st{bsm=Saved}}) -> - case gb_sets:is_member(Reg, Saved) of - false -> error({no_save_point,Reg}); - true -> ok +assert_unique_map_keys([]) -> + %% There is no reason to use the get_map_elements and + %% has_map_fields instructions with empty lists. + error(empty_field_list); +assert_unique_map_keys([_]) -> + ok; +assert_unique_map_keys([_,_|_]=Ls) -> + Vs = [get_literal(L) || L <- Ls], + case length(Vs) =:= sets:size(sets:from_list(Vs)) of + true -> ok; + false -> error(keys_not_unique) end. -bs_assert_state(#vst{current=#st{bsm=undefined}}) -> - error(no_bs_match_state); -bs_assert_state(_) -> ok. - - %%% %%% New binary matching instructions. %%% bsm_match_state(Slots) -> - {match_context,0,Slots}. + #ms{slots=Slots}. bsm_validate_context(Reg, Vst) -> _ = bsm_get_context(Reg, Vst), @@ -1202,7 +1056,7 @@ bsm_validate_context(Reg, Vst) -> bsm_get_context({x,X}=Reg, #vst{current=#st{x=Xs}}=_Vst) when is_integer(X) -> case gb_trees:lookup(X, Xs) of - {value,{match_context,_,_}=Ctx} -> Ctx; + {value,#ms{}=Ctx} -> Ctx; _ -> error({no_bsm_context,Reg}) end; bsm_get_context(Reg, _) -> error({bad_source,Reg}). @@ -1214,8 +1068,8 @@ bsm_save(Reg, {atom,start}, Vst) -> Vst; bsm_save(Reg, SavePoint, Vst) -> case bsm_get_context(Reg, Vst) of - {match_context,Bits,Slots} when SavePoint < Slots -> - Ctx = {match_context,Bits bor (1 bsl SavePoint),Slots}, + #ms{valid=Bits,slots=Slots}=Ctxt0 when SavePoint < Slots -> + Ctx = Ctxt0#ms{valid=Bits bor (1 bsl SavePoint),slots=Slots}, set_type_reg(Ctx, Reg, Vst); _ -> error({illegal_save,SavePoint}) end. @@ -1227,62 +1081,14 @@ bsm_restore(Reg, {atom,start}, Vst) -> Vst; bsm_restore(Reg, SavePoint, Vst) -> case bsm_get_context(Reg, Vst) of - {match_context,Bits,Slots} when SavePoint < Slots -> + #ms{valid=Bits,slots=Slots} when SavePoint < Slots -> case Bits band (1 bsl SavePoint) of 0 -> error({illegal_restore,SavePoint,not_set}); _ -> Vst end; _ -> error({illegal_restore,SavePoint,range}) end. - - -%%% -%%% Validation of alignment in the bit syntax. (Currently, construction only.) -%%% -%%% We make sure that the aligned flag is only set when we can be sure of the -%%% aligment. -%%% - -bs_zero_bits(#vst{current=St}=Vst) -> - Vst#vst{current=St#st{bits=0}}. - -bs_align_check({bs_put_utf8,_,Flags,_}, #vst{current=#st{}=St}=Vst) -> - bs_verify_flags(Flags, St), - Vst; -bs_align_check({bs_put_utf16,_,Flags,_}, #vst{current=#st{}=St}=Vst) -> - bs_verify_flags(Flags, St), - Vst; -bs_align_check({bs_put_utf32,_,Flags,_}, #vst{current=#st{}=St}=Vst) -> - bs_verify_flags(Flags, St), - Vst; -bs_align_check({_,_,Sz,U,Flags,_}, #vst{current=#st{bits=Bits}=St}=Vst) -> - bs_verify_flags(Flags, St), - bs_update_bits(Bits, Sz, U, St, Vst). - -bs_update_bits(undefined, _, _, _, Vst) -> Vst; -bs_update_bits(Bits0, {integer,Sz}, U, St, Vst) -> - Bits = Bits0 + U*Sz, - Vst#vst{current=St#st{bits=Bits}}; -bs_update_bits(_, {atom,all}, _, _, Vst) -> - %% A binary will not change the alignment. - Vst; -bs_update_bits(_, _, U, _, Vst) when U rem 8 =:= 0 -> - %% Units of 8, 16, and so on will not change the aligment. - Vst; -bs_update_bits(_, _, _, St, Vst) -> - %% We can no longer be sure about aligment. - Vst#vst{current=St#st{bits=undefined}}. - -bs_verify_flags({field_flags,Fl}, #st{bits=Bits}) -> - case bs_is_aligned(Fl) of - false -> ok; - true when is_integer(Bits), Bits rem 8 =:= 0 -> ok; - true -> error({aligned_flag_set,{bits,Bits}}) - end. -bs_is_aligned(Fl) when is_integer(Fl) -> Fl band 1 =:= 1; -bs_is_aligned(Fl) when is_list(Fl) -> member(aligned, Fl). - %%% %%% Keeping track of types. %%% @@ -1291,42 +1097,44 @@ set_type(Type, {x,_}=Reg, Vst) -> set_type_reg(Type, Reg, Vst); set_type(Type, {y,_}=Reg, Vst) -> set_type_y(Type, Reg, Vst); set_type(_, _, #vst{}=Vst) -> Vst. -set_type_reg(Type, {x,X}, #vst{current=#st{x=Xs}=St}=Vst) +set_type_reg(Type, {x,X}=Reg, #vst{current=#st{x=Xs}=St}=Vst) when is_integer(X), 0 =< X -> - limit_check(X), + check_limit(Reg), Vst#vst{current=St#st{x=gb_trees:enter(X, Type, Xs)}}; set_type_reg(Type, Reg, Vst) -> set_type_y(Type, Reg, Vst). -set_type_y(Type, {y,Y}=Reg, #vst{current=#st{y=Ys0,numy=NumY}=St}=Vst) +set_type_y(Type, {y,Y}=Reg, #vst{current=#st{y=Ys0}=St}=Vst) when is_integer(Y), 0 =< Y -> - limit_check(Y), - case {Y,NumY} of - {_,none} -> - error({no_stack_frame,Reg}); - {_,_} when Y > NumY -> - error({y_reg_out_of_range,Reg,NumY}); - {_,_} -> - Ys = if Type =:= initialized_ct -> - gb_trees:enter(Y, initialized, Ys0); - true -> - case gb_trees:lookup(Y, Ys0) of - none -> - gb_trees:insert(Y, Type, Ys0); - {value,uinitialized} -> - gb_trees:insert(Y, Type, Ys0); - {value,{catchtag,_}=Tag} -> - error(Tag); - {value,{trytag,_}=Tag} -> - error(Tag); - {value,_} -> - gb_trees:update(Y, Type, Ys0) - end - end, - Vst#vst{current=St#st{y=Ys}} - end; + check_limit(Reg), + Ys = case gb_trees:lookup(Y, Ys0) of + none -> + error({invalid_store,Reg,Type}); + {value,{catchtag,_}=Tag} -> + error(Tag); + {value,{trytag,_}=Tag} -> + error(Tag); + {value,_} -> + gb_trees:update(Y, Type, Ys0) + end, + Vst#vst{current=St#st{y=Ys}}; set_type_y(Type, Reg, #vst{}) -> error({invalid_store,Reg,Type}). +set_catch_end({y,Y}, #vst{current=#st{y=Ys0}=St}=Vst) -> + Ys = gb_trees:update(Y, initialized, Ys0), + Vst#vst{current=St#st{y=Ys}}. + + +is_reg_defined({x,_}=Reg, Vst) -> is_type_defined_x(Reg, Vst); +is_reg_defined({y,_}=Reg, Vst) -> is_type_defined_y(Reg, Vst); +is_reg_defined(V, #vst{}) -> error({not_a_register, V}). + +is_type_defined_x({x,X}, #vst{current=#st{x=Xs}}) -> + gb_trees:is_defined(X,Xs). + +is_type_defined_y({y,Y}, #vst{current=#st{y=Ys}}) -> + gb_trees:is_defined(Y,Ys). + assert_term(Src, Vst) -> get_term_type(Src, Vst), ok. @@ -1354,7 +1162,7 @@ assert_term(Src, Vst) -> %% Thus 'exception' is never stored as type descriptor %% for a register. %% -%% {match_context,_,_} A matching context for bit syntax matching. We do allow +%% #ms{} A match context for bit syntax matching. We do allow %% it to moved/to from stack, but otherwise it must only %% be accessed by bit syntax matching instructions. %% @@ -1387,7 +1195,8 @@ assert_term(Src, Vst) -> %% %% number Integer or Float of unknown value %% - +%% map Map. +%% assert_type(WantedType, Term, Vst) -> assert_type(WantedType, get_term_type(Term, Vst)). @@ -1395,12 +1204,17 @@ assert_type(WantedType, Term, Vst) -> assert_type(Correct, Correct) -> ok; assert_type(float, {float,_}) -> ok; assert_type(tuple, {tuple,_}) -> ok; +assert_type(tuple, {literal,Tuple}) when is_tuple(Tuple) -> ok; assert_type({tuple_element,I}, {tuple,[Sz]}) when 1 =< I, I =< Sz -> ok; assert_type({tuple_element,I}, {tuple,Sz}) when is_integer(Sz), 1 =< I, I =< Sz -> ok; +assert_type({tuple_element,I}, {literal,Lit}) when I =< tuple_size(Lit) -> + ok; +assert_type(cons, {literal,[_|_]}) -> + ok; assert_type(Needed, Actual) -> error({bad_type,{needed,Needed},{actual,Actual}}). @@ -1455,7 +1269,7 @@ get_term_type(Src, Vst) -> initialized -> error({unassigned,Src}); {catchtag,_} -> error({catchtag,Src}); {trytag,_} -> error({trytag,Src}); - {match_context,_,_} -> error({match_context,Src}); + #ms{} -> error({match_context,Src}); Type -> Type end. @@ -1469,6 +1283,7 @@ get_term_type_1(nil=T, _) -> T; get_term_type_1({atom,A}=T, _) when is_atom(A) -> T; get_term_type_1({float,F}=T, _) when is_float(F) -> T; get_term_type_1({integer,I}=T, _) when is_integer(I) -> T; +get_term_type_1({literal,Map}, _) when is_map(Map) -> map; get_term_type_1({literal,_}=T, _) -> T; get_term_type_1({x,X}=Reg, #vst{current=#st{x=Xs}}) when is_integer(X) -> case gb_trees:lookup(X, Xs) of @@ -1523,14 +1338,13 @@ merge_states(L, St, Branched) when L =/= 0 -> {value,OtherSt} -> merge_states_1(St, OtherSt) end. -merge_states_1(#st{x=Xs0,y=Ys0,numy=NumY0,h=H0,ct=Ct0,bsm=Bsm0}=St, - #st{x=Xs1,y=Ys1,numy=NumY1,h=H1,ct=Ct1,bsm=Bsm1}) -> +merge_states_1(#st{x=Xs0,y=Ys0,numy=NumY0,h=H0,ct=Ct0}, + #st{x=Xs1,y=Ys1,numy=NumY1,h=H1,ct=Ct1}) -> NumY = merge_stk(NumY0, NumY1), Xs = merge_regs(Xs0, Xs1), Ys = merge_y_regs(Ys0, Ys1), Ct = merge_ct(Ct0, Ct1), - Bsm = merge_bsm(Bsm0, Bsm1), - St#st{x=Xs,y=Ys,numy=NumY,h=min(H0, H1),ct=Ct,bsm=Bsm}. + #st{x=Xs,y=Ys,numy=NumY,h=min(H0, H1),ct=Ct}. merge_stk(S, S) -> S; merge_stk(_, _) -> undecided. @@ -1560,20 +1374,24 @@ merge_regs_1([], [_|_]) -> []; merge_regs_1([_|_], []) -> []. merge_y_regs(Rs0, Rs1) -> - Rs = merge_y_regs_1(gb_trees:to_list(Rs0), gb_trees:to_list(Rs1)), - gb_trees_from_list(Rs). + case {gb_trees:size(Rs0),gb_trees:size(Rs1)} of + {Sz0,Sz1} when Sz0 < Sz1 -> + merge_y_regs_1(Sz0-1, Rs1, Rs0); + {_,Sz1} -> + merge_y_regs_1(Sz1-1, Rs0, Rs1) + end. -merge_y_regs_1([Same|Rs1], [Same|Rs2]) -> - [Same|merge_y_regs_1(Rs1, Rs2)]; -merge_y_regs_1([{R1,_}|Rs1], [{R2,_}|_]=Rs2) when R1 < R2 -> - [{R1,uninitialized}|merge_y_regs_1(Rs1, Rs2)]; -merge_y_regs_1([{R1,_}|_]=Rs1, [{R2,_}|Rs2]) when R1 > R2 -> - [{R2,uninitialized}|merge_y_regs_1(Rs1, Rs2)]; -merge_y_regs_1([{R,Type1}|Rs1], [{R,Type2}|Rs2]) -> - [{R,merge_types(Type1, Type2)}|merge_y_regs_1(Rs1, Rs2)]; -merge_y_regs_1([], []) -> []; -merge_y_regs_1([], [_|_]=Rs) -> Rs; -merge_y_regs_1([_|_]=Rs, []) -> Rs. +merge_y_regs_1(Y, S, Regs0) when Y >= 0 -> + Type0 = gb_trees:get(Y, Regs0), + case gb_trees:get(Y, S) of + Type0 -> + merge_y_regs_1(Y-1, S, Regs0); + Type1 -> + Type = merge_types(Type0, Type1), + Regs = gb_trees:update(Y, Type, Regs0), + merge_y_regs_1(Y-1, S, Regs) + end; +merge_y_regs_1(_, _, Regs) -> Regs. %% merge_types(Type1, Type2) -> Type %% Return the most specific type possible. @@ -1603,20 +1421,17 @@ merge_types(bool, {atom,A}) -> merge_bool(A); merge_types({atom,A}, bool) -> merge_bool(A); -merge_types({match_context,B0,Slots},{match_context,B1,Slots}) -> - {match_context,B0 bor B1,Slots}; -merge_types({match_context,_,_}=M, _) -> +merge_types(#ms{id=Id,valid=B0,slots=Slots}=M, + #ms{id=Id,valid=B1,slots=Slots}) -> + M#ms{valid=B0 bor B1,slots=Slots}; +merge_types(#ms{}=M, _) -> M; -merge_types(_, {match_context,_,_}=M) -> +merge_types(_, #ms{}=M) -> M; merge_types(T1, T2) when T1 =/= T2 -> %% Too different. All we know is that the type is a 'term'. term. -merge_bsm(undefined, _) -> undefined; -merge_bsm(_, undefined) -> undefined; -merge_bsm(Bsm0, Bsm1) -> gb_sets:intersection(Bsm0, Bsm1). - tuple_sz([Sz]) -> Sz; tuple_sz(Sz) -> Sz. @@ -1723,6 +1538,7 @@ bif_type(is_float, [_], _) -> bool; bif_type(is_function, [_], _) -> bool; bif_type(is_integer, [_], _) -> bool; bif_type(is_list, [_], _) -> bool; +bif_type(is_map, [_], _) -> bool; bif_type(is_number, [_], _) -> bool; bif_type(is_pid, [_], _) -> bool; bif_type(is_port, [_], _) -> bool; @@ -1734,7 +1550,6 @@ bif_type(node, [_], _) -> {atom,[]}; bif_type(hd, [_], _) -> term; bif_type(tl, [_], _) -> term; bif_type(get, [_], _) -> term; -bif_type(raise, [_,_], _) -> exception; bif_type(Bif, _, _) when is_atom(Bif) -> term. is_bif_safe('/=', 2) -> true; @@ -1748,10 +1563,12 @@ is_bif_safe('>=', 2) -> true; is_bif_safe(is_atom, 1) -> true; is_bif_safe(is_boolean, 1) -> true; is_bif_safe(is_binary, 1) -> true; +is_bif_safe(is_bitstring, 1) -> true; is_bif_safe(is_float, 1) -> true; is_bif_safe(is_function, 1) -> true; is_bif_safe(is_integer, 1) -> true; is_bif_safe(is_list, 1) -> true; +is_bif_safe(is_map, 1) -> true; is_bif_safe(is_number, 1) -> true; is_bif_safe(is_pid, 1) -> true; is_bif_safe(is_port, 1) -> true; @@ -1777,8 +1594,12 @@ return_type_1(erlang, setelement, 3, Vst) -> Tuple = {x,1}, TupleType = case get_term_type(Tuple, Vst) of - {tuple,_}=TT -> TT; - _ -> {tuple,[0]} + {tuple,_}=TT -> + TT; + {literal,Lit} when is_tuple(Lit) -> + {tuple,tuple_size(Lit)}; + _ -> + {tuple,[0]} end, case get_term_type({x,0}, Vst) of {integer,[]} -> TupleType; @@ -1794,8 +1615,6 @@ return_type_1(M, F, A, _) when is_atom(M), is_atom(F), is_integer(A), A >= 0 -> return_type_erl(exit, 1) -> exception; return_type_erl(throw, 1) -> exception; -return_type_erl(fault, 1) -> exception; -return_type_erl(fault, 2) -> exception; return_type_erl(error, 1) -> exception; return_type_erl(error, 2) -> exception; return_type_erl(F, A) when is_atom(F), is_integer(A), A >= 0 -> term. @@ -1816,6 +1635,7 @@ return_type_math(erf, 1) -> {float,[]}; return_type_math(erfc, 1) -> {float,[]}; return_type_math(exp, 1) -> {float,[]}; return_type_math(log, 1) -> {float,[]}; +return_type_math(log2, 1) -> {float,[]}; return_type_math(log10, 1) -> {float,[]}; return_type_math(sqrt, 1) -> {float,[]}; return_type_math(atan2, 2) -> {float,[]}; @@ -1823,66 +1643,19 @@ return_type_math(pow, 2) -> {float,[]}; return_type_math(pi, 0) -> {float,[]}; return_type_math(F, A) when is_atom(F), is_integer(A), A >= 0 -> term. -limit_check(Num) when is_integer(Num), Num >= ?MAXREG -> - error(limit); -limit_check(_) -> ok. +check_limit({x,X}) when is_integer(X), X < 1023 -> + %% Note: x(1023) is reserved for use by the BEAM loader. + ok; +check_limit({y,Y}) when is_integer(Y), Y < 1024 -> + ok; +check_limit({fr,Fr}) when is_integer(Fr), Fr < 1024 -> + ok; +check_limit(_) -> + error(limit). min(A, B) when is_integer(A), is_integer(B), A < B -> A; min(A, B) when is_integer(A), is_integer(B) -> B. gb_trees_from_list(L) -> gb_trees:from_orddict(lists:sort(L)). --ifdef(DEBUG). -error(Error) -> exit(Error). --else. error(Error) -> throw(Error). --endif. - - -%%% -%%% Rewrite disassembled code to the same format as we used internally -%%% to not have to worry later. -%%% - -normalize_disassembled_code(Fs) -> - Index = ndc_index(Fs, []), - ndc(Fs, Index, []). - -ndc_index([{function,Name,Arity,Entry,_Code}|Fs], Acc) -> - ndc_index(Fs, [{{Name,Arity},Entry}|Acc]); -ndc_index([], Acc) -> - gb_trees:from_orddict(lists:sort(Acc)). - -ndc([{function,Name,Arity,Entry,Code0}|Fs], D, Acc) -> - Code = ndc_1(Code0, D, []), - ndc(Fs, D, [{function,Name,Arity,Entry,Code}|Acc]); -ndc([], _, Acc) -> reverse(Acc). - -ndc_1([{call=Op,A,{_,F,A}}|Is], D, Acc) -> - ndc_1(Is, D, [{Op,A,{f,gb_trees:get({F,A}, D)}}|Acc]); -ndc_1([{call_only=Op,A,{_,F,A}}|Is], D, Acc) -> - ndc_1(Is, D, [{Op,A,{f,gb_trees:get({F,A}, D)}}|Acc]); -ndc_1([{call_last=Op,A,{_,F,A},Sz}|Is], D, Acc) -> - ndc_1(Is, D, [{Op,A,{f,gb_trees:get({F,A}, D)},Sz}|Acc]); -ndc_1([{arithbif,Op,F,Src,Dst}|Is], D, Acc) -> - ndc_1(Is, D, [{bif,Op,F,Src,Dst}|Acc]); -ndc_1([{arithfbif,Op,F,Src,Dst}|Is], D, Acc) -> - ndc_1(Is, D, [{bif,Op,F,Src,Dst}|Acc]); -ndc_1([{test,bs_start_match2=Op,F,[A1,Live,A3,Dst]}|Is], D, Acc) -> - ndc_1(Is, D, [{test,Op,F,Live,[A1,A3],Dst}|Acc]); -ndc_1([{test,bs_get_binary2=Op,F,[A1,Live,A3,A4,A5,Dst]}|Is], D, Acc) -> - ndc_1(Is, D, [{test,Op,F,Live,[A1,A3,A4,A5],Dst}|Acc]); -ndc_1([{test,bs_get_float2=Op,F,[A1,Live,A3,A4,A5,Dst]}|Is], D, Acc) -> - ndc_1(Is, D, [{test,Op,F,Live,[A1,A3,A4,A5],Dst}|Acc]); -ndc_1([{test,bs_get_integer2=Op,F,[A1,Live,A3,A4,A5,Dst]}|Is], D, Acc) -> - ndc_1(Is, D, [{test,Op,F,Live,[A1,A3,A4,A5],Dst}|Acc]); -ndc_1([{test,bs_get_utf8=Op,F,[A1,Live,A3,Dst]}|Is], D, Acc) -> - ndc_1(Is, D, [{test,Op,F,Live,[A1,A3],Dst}|Acc]); -ndc_1([{test,bs_get_utf16=Op,F,[A1,Live,A3,Dst]}|Is], D, Acc) -> - ndc_1(Is, D, [{test,Op,F,Live,[A1,A3],Dst}|Acc]); -ndc_1([{test,bs_get_utf32=Op,F,[A1,Live,A3,Dst]}|Is], D, Acc) -> - ndc_1(Is, D, [{test,Op,F,Live,[A1,A3],Dst}|Acc]); -ndc_1([I|Is], D, Acc) -> - ndc_1(Is, D, [I|Acc]); -ndc_1([], _, Acc) -> - reverse(Acc). diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl index c2a6ef604e..6c7f8543c2 100644 --- a/lib/compiler/src/beam_z.erl +++ b/lib/compiler/src/beam_z.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -23,6 +24,8 @@ -export([module/2]). +-import(lists, [dropwhile/2]). + module({Mod,Exp,Attr,Fs0,Lc}, _Opt) -> Fs = [function(F) || F <- Fs0], {ok,{Mod,Exp,Attr,Fs,Lc}}. @@ -50,6 +53,16 @@ undo_renames([{call,A,F},return|Is]) -> [{call_only,A,F}|undo_renames(Is)]; undo_renames([{call_ext,A,F},return|Is]) -> [{call_ext_only,A,F}|undo_renames(Is)]; +undo_renames([{bif,raise,_,_,_}=I|Is0]) -> + %% A minor optimization. Done here because: + %% (1) beam_jump may move or share 'raise' instructions, and that + %% may confuse beam_validator. + %% (2) beam_trim cannot do its optimization if the 'deallocate' + %% instruction after 'raise' has been removed. + Is = dropwhile(fun({label,_}) -> false; + (_) -> true + end, Is0), + [I|undo_renames(Is)]; undo_renames([I|Is]) -> [undo_rename(I)|undo_renames(Is)]; undo_renames([]) -> []. @@ -74,22 +87,21 @@ undo_rename({bs_init,F,{I,Extra,U,Flags},Live,[Sz,Src],Dst}) -> {I,F,Sz,Extra,Live,U,Src,Flags,Dst}; undo_rename({bs_init,_,bs_init_writable=I,_,_,_}) -> I; +undo_rename({test,bs_match_string=Op,F,[Ctx,Bin0]}) -> + Bits = bit_size(Bin0), + Bin = case Bits rem 8 of + 0 -> Bin0; + Rem -> <<Bin0/bitstring,0:(8-Rem)>> + end, + {test,Op,F,[Ctx,Bits,{string,binary_to_list(Bin)}]}; undo_rename({put_map,Fail,assoc,S,D,R,L}) -> {put_map_assoc,Fail,S,D,R,L}; undo_rename({put_map,Fail,exact,S,D,R,L}) -> {put_map_exact,Fail,S,D,R,L}; undo_rename({test,has_map_fields,Fail,[Src|List]}) -> - {test,has_map_fields,Fail,Src,{list,[to_typed_literal(V)||V<-List]}}; -undo_rename({get_map_elements,Fail,Src,{list, List}}) -> - {get_map_elements,Fail,Src,{list,[to_typed_literal(V)||V<-List]}}; + {test,has_map_fields,Fail,Src,{list,List}}; +undo_rename({get_map_elements,Fail,Src,{list,List}}) -> + {get_map_elements,Fail,Src,{list,List}}; undo_rename({select,I,Reg,Fail,List}) -> {I,Reg,Fail,{list,List}}; undo_rename(I) -> I. - -%% to_typed_literal(Arg) -%% transform Arg to specific literal i.e. float | integer | atom if applicable -to_typed_literal({literal, V}) when is_float(V) -> {float, V}; -to_typed_literal({literal, V}) when is_atom(V) -> {atom, V}; -to_typed_literal({literal, V}) when is_integer(V) -> {integer, V}; -to_typed_literal({literal, []}) -> nil; -to_typed_literal(V) -> V. diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 9d6768b157..61abae344c 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% @@ -123,19 +124,24 @@ bitstr_flags/1, %% keep map exports here for now + c_map_pattern/1, + is_c_map/1, + is_c_map_pattern/1, map_es/1, map_arg/1, update_c_map/3, c_map/1, is_c_map_empty/1, ann_c_map/2, ann_c_map/3, + ann_c_map_pattern/2, map_pair_op/1,map_pair_key/1,map_pair_val/1, update_c_map_pair/4, - c_map_pair/2, + c_map_pair/2, c_map_pair_exact/2, ann_c_map_pair/4 ]). -export_type([c_binary/0, c_bitstr/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, - c_literal/0, c_map/0, c_map_pair/0, c_module/0, c_tuple/0, + c_let/0, c_literal/0, c_map/0, c_map_pair/0, + c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]). -include("core_parse.hrl"). @@ -252,7 +258,7 @@ %% @see c_primop/2 %% @see c_receive/1 %% @see c_seq/2 -%% @see c_try/3 +%% @see c_try/5 %% @see c_tuple/1 %% @see c_values/1 %% @see c_var/1 @@ -431,6 +437,8 @@ is_literal_term([H | T]) -> is_literal_term(T) when is_tuple(T) -> is_literal_term_list(tuple_to_list(T)); is_literal_term(B) when is_bitstring(B) -> true; +is_literal_term(M) when is_map(M) -> + is_literal_term_list(maps:to_list(M)); is_literal_term(_) -> false. @@ -1450,7 +1458,7 @@ is_proper_list(_) -> %% X4]</code>. %% %% @see c_cons/2 -%% @see c_nil/1 +%% @see c_nil/0 %% @see is_c_list/1 %% @see list_length/1 %% @see make_list/2 @@ -1481,7 +1489,7 @@ abstract_list([]) -> %% efficient.</p> %% %% @see c_cons/2 -%% @see c_nil/1 +%% @see c_nil/0 %% @see is_c_list/1 %% @see list_elements/1 @@ -1577,20 +1585,51 @@ ann_make_list(_, [], Node) -> %% --------------------------------------------------------------------- %% maps --spec map_es(c_map()) -> [c_map_pair()]. +%% @spec is_c_map(Node::cerl()) -> boolean() +%% +%% @doc Returns <code>true</code> if <code>Node</code> is an abstract +%% map constructor, otherwise <code>false</code>. + +-spec is_c_map(cerl()) -> boolean(). +is_c_map(#c_map{}) -> + true; +is_c_map(#c_literal{val = V}) when is_map(V) -> + true; +is_c_map(_) -> + false. + +-spec map_es(c_map() | c_literal()) -> [c_map_pair()]. + +map_es(#c_literal{anno=As,val=M}) when is_map(M) -> + [ann_c_map_pair(As, + #c_literal{anno=As,val='assoc'}, + #c_literal{anno=As,val=K}, + #c_literal{anno=As,val=V}) || {K,V} <- maps:to_list(M)]; map_es(#c_map{es = Es}) -> Es. --spec map_arg(c_map()) -> c_map() | c_literal(). +-spec map_arg(c_map() | c_literal()) -> c_map() | c_literal(). +map_arg(#c_literal{anno=As,val=M}) when is_map(M) -> + #c_literal{anno=As,val=#{}}; map_arg(#c_map{arg=M}) -> M. -spec c_map([c_map_pair()]) -> c_map(). c_map(Pairs) -> - #c_map{es=Pairs}. + ann_c_map([], Pairs). + +-spec c_map_pattern([c_map_pair()]) -> c_map(). + +c_map_pattern(Pairs) -> + #c_map{es=Pairs, is_pat=true}. + +-spec ann_c_map_pattern([term()], [c_map_pair()]) -> c_map(). + +ann_c_map_pattern(As, Pairs) -> + #c_map{anno=As, es=Pairs, is_pat=true}. -spec is_c_map_empty(c_map() | c_literal()) -> boolean(). @@ -1598,25 +1637,18 @@ is_c_map_empty(#c_map{ es=[] }) -> true; is_c_map_empty(#c_literal{val=M}) when is_map(M),map_size(M) =:= 0 -> true; is_c_map_empty(_) -> false. --spec ann_c_map([term()], [cerl()]) -> c_map() | c_literal(). +-spec is_c_map_pattern(c_map()) -> boolean(). -ann_c_map(As,Es) -> +is_c_map_pattern(#c_map{is_pat=IsPat}) -> + IsPat. + +-spec ann_c_map([term()], [c_map_pair()]) -> c_map() | c_literal(). + +ann_c_map(As, Es) -> ann_c_map(As, #c_literal{val=#{}}, Es). -spec ann_c_map([term()], c_map() | c_literal(), [c_map_pair()]) -> c_map() | c_literal(). -ann_c_map(As,#c_literal{val=Mval}=M,Es) when is_map(Mval), map_size(Mval) =:= 0 -> - Pairs = [[Ck,Cv]||#c_map_pair{key=Ck,val=Cv}<-Es], - IsLit = lists:foldl(fun(Pair,Res) -> - Res andalso is_lit_list(Pair) - end, true, Pairs), - Fun = fun(Pair) -> [K,V] = lit_list_vals(Pair), {K,V} end, - case IsLit of - false -> - #c_map{arg=M, es=Es, anno=As }; - true -> - #c_literal{anno=As, val=maps:from_list(lists:map(Fun, Pairs))} - end; ann_c_map(As,#c_literal{val=M},Es) when is_map(M) -> fold_map_pairs(As,Es,M); ann_c_map(As,M,Es) -> @@ -1644,14 +1676,14 @@ fold_map_pairs(As,[#c_map_pair{op=#c_literal{val=exact},key=Ck,val=Cv}=E|Es],M) end; false -> #c_map{arg=#c_literal{val=M,anno=As}, es=[E|Es], anno=As } - end; -fold_map_pairs(As,Es,M) -> - #c_map{arg=#c_literal{val=M,anno=As}, es=Es, anno=As }. + end. -%-spec update_c_map(c_map() | c_literal(), [c_map_pair()]) -> c_map() | c_literal(). +-spec update_c_map(c_map(), cerl(), [cerl()]) -> c_map() | c_literal(). -update_c_map(Old,M,Es) -> - #c_map{arg=M, es = Es, anno = get_ann(Old)}. +update_c_map(#c_map{is_pat=true}=Old, M, Es) -> + Old#c_map{arg=M, es=Es}; +update_c_map(#c_map{is_pat=false}=Old, M, Es) -> + ann_c_map(get_ann(Old), M, Es). map_pair_key(#c_map_pair{key=K}) -> K. map_pair_val(#c_map_pair{val=V}) -> V. @@ -1662,6 +1694,11 @@ map_pair_op(#c_map_pair{op=Op}) -> Op. c_map_pair(Key,Val) -> #c_map_pair{op=#c_literal{val=assoc},key=Key,val=Val}. +-spec c_map_pair_exact(cerl(), cerl()) -> c_map_pair(). + +c_map_pair_exact(Key,Val) -> + #c_map_pair{op=#c_literal{val=exact},key=Key,val=Val}. + -spec ann_c_map_pair([term()], cerl(), cerl(), cerl()) -> c_map_pair(). @@ -1918,7 +1955,7 @@ is_c_var(_) -> false. -%% @spec c_fname(Name::atom(), Arity::integer()) -> cerl() +%% @spec c_fname(Name::atom(), Arity::arity()) -> cerl() %% @equiv c_var({Name, Arity}) %% @see fname_id/1 %% @see fname_arity/1 @@ -1926,18 +1963,18 @@ is_c_var(_) -> %% @see ann_c_fname/3 %% @see update_c_fname/3 --spec c_fname(atom(), non_neg_integer()) -> c_var(). +-spec c_fname(atom(), arity()) -> c_var(). c_fname(Atom, Arity) -> c_var({Atom, Arity}). -%% @spec ann_c_fname(As::[term()], Name::atom(), Arity::integer()) -> +%% @spec ann_c_fname(As::[term()], Name::atom(), Arity::arity()) -> %% cerl() %% @equiv ann_c_var(As, {Atom, Arity}) %% @see c_fname/2 --spec ann_c_fname([term()], atom(), non_neg_integer()) -> c_var(). +-spec ann_c_fname([term()], atom(), arity()) -> c_var(). ann_c_fname(As, Atom, Arity) -> ann_c_var(As, {Atom, Arity}). @@ -1955,13 +1992,13 @@ update_c_fname(#c_var{name = {_, Arity}, anno = As}, Atom) -> #c_var{name = {Atom, Arity}, anno = As}. -%% @spec update_c_fname(Old::cerl(), Name::atom(), Arity::integer()) -> +%% @spec update_c_fname(Old::cerl(), Name::atom(), Arity::arity()) -> %% cerl() %% @equiv update_c_var(Old, {Atom, Arity}) %% @see update_c_fname/2 %% @see c_fname/2 --spec update_c_fname(c_var(), atom(), integer()) -> c_var(). +-spec update_c_fname(c_var(), atom(), arity()) -> c_var(). update_c_fname(Node, Atom, Arity) -> update_c_var(Node, {Atom, Arity}). @@ -1974,7 +2011,7 @@ update_c_fname(Node, Atom, Arity) -> %% %% @see c_fname/2 %% @see c_var/1 -%% @see c_var_name/1 +%% @see var_name/1 -spec is_c_fname(cerl()) -> boolean(). @@ -2010,14 +2047,14 @@ fname_id(#c_var{name={A,_}}) -> A. -%% @spec fname_arity(cerl()) -> byte() +%% @spec fname_arity(cerl()) -> arity() %% %% @doc Returns the arity part of an abstract function name variable. %% %% @see fname_id/1 %% @see c_fname/2 --spec fname_arity(c_var()) -> byte(). +-spec fname_arity(c_var()) -> arity(). fname_arity(#c_var{name={_,N}}) -> N. @@ -2463,7 +2500,7 @@ fun_body(Node) -> Node#c_fun.body. -%% @spec fun_arity(Node::cerl()) -> integer() +%% @spec fun_arity(Node::cerl()) -> arity() %% %% @doc Returns the number of parameter subtrees of an abstract %% fun-expression. @@ -2474,7 +2511,7 @@ fun_body(Node) -> %% @see c_fun/2 %% @see fun_vars/1 --spec fun_arity(c_fun()) -> non_neg_integer(). +-spec fun_arity(c_fun()) -> arity(). fun_arity(Node) -> length(fun_vars(Node)). @@ -3063,10 +3100,12 @@ pat_vars(Node, Vs) -> map -> pat_list_vars(map_es(Node), Vs); map_pair -> - pat_list_vars([map_pair_op(Node),map_pair_key(Node),map_pair_val(Node)],Vs); + %% map_pair_key is not a pattern var, excluded + pat_list_vars([map_pair_op(Node),map_pair_val(Node)],Vs); binary -> pat_list_vars(binary_segments(Node), Vs); bitstr -> + %% bitstr_size is not a pattern var, excluded pat_vars(bitstr_val(Node), Vs); alias -> pat_vars(alias_pat(Node), [alias_var(Node) | Vs]) @@ -3379,7 +3418,7 @@ apply_args(Node) -> Node#c_apply.args. -%% @spec apply_arity(Node::cerl()) -> integer() +%% @spec apply_arity(Node::cerl()) -> arity() %% %% @doc Returns the number of argument subtrees of an abstract %% function application. @@ -3391,7 +3430,7 @@ apply_args(Node) -> %% @see c_apply/2 %% @see apply_args/1 --spec apply_arity(c_apply()) -> non_neg_integer(). +-spec apply_arity(c_apply()) -> arity(). apply_arity(Node) -> length(apply_args(Node)). @@ -3497,7 +3536,7 @@ call_args(Node) -> Node#c_call.args. -%% @spec call_arity(Node::cerl()) -> integer() +%% @spec call_arity(Node::cerl()) -> arity() %% %% @doc Returns the number of argument subtrees of an abstract %% inter-module call. @@ -3509,7 +3548,7 @@ call_args(Node) -> %% @see c_call/3 %% @see call_args/1 --spec call_arity(c_call()) -> non_neg_integer(). +-spec call_arity(c_call()) -> arity(). call_arity(Node) -> length(call_args(Node)). @@ -3601,7 +3640,7 @@ primop_args(Node) -> Node#c_primop.args. -%% @spec primop_arity(Node::cerl()) -> integer() +%% @spec primop_arity(Node::cerl()) -> arity() %% %% @doc Returns the number of argument subtrees of an abstract %% primitive operation call. @@ -3613,7 +3652,7 @@ primop_args(Node) -> %% @see c_primop/2 %% @see primop_args/1 --spec primop_arity(c_primop()) -> non_neg_integer(). +-spec primop_arity(c_primop()) -> arity(). primop_arity(Node) -> length(primop_args(Node)). @@ -3650,7 +3689,7 @@ c_try(Expr, Vs, Body, Evs, Handler) -> %% @spec ann_c_try(As::[term()], Expression::cerl(), %% Variables::[cerl()], Body::cerl(), %% EVars::[cerl()], Handler::cerl()) -> cerl() -%% @see c_try/3 +%% @see c_try/5 -spec ann_c_try([term()], cerl(), [cerl()], cerl(), [cerl()], cerl()) -> c_try(). @@ -3663,7 +3702,7 @@ ann_c_try(As, Expr, Vs, Body, Evs, Handler) -> %% @spec update_c_try(Old::cerl(), Expression::cerl(), %% Variables::[cerl()], Body::cerl(), %% EVars::[cerl()], Handler::cerl()) -> cerl() -%% @see c_try/3 +%% @see c_try/5 -spec update_c_try(c_try(), cerl(), [cerl()], cerl(), [cerl()], cerl()) -> c_try(). @@ -3678,7 +3717,7 @@ update_c_try(Node, Expr, Vs, Body, Evs, Handler) -> %% @doc Returns <code>true</code> if <code>Node</code> is an abstract %% try-expression, otherwise <code>false</code>. %% -%% @see c_try/3 +%% @see c_try/5 -spec is_c_try(cerl()) -> boolean(). @@ -3692,7 +3731,7 @@ is_c_try(_) -> %% %% @doc Returns the expression subtree of an abstract try-expression. %% -%% @see c_try/3 +%% @see c_try/5 -spec try_arg(c_try()) -> cerl(). @@ -3705,7 +3744,7 @@ try_arg(Node) -> %% @doc Returns the list of success variable subtrees of an abstract %% try-expression. %% -%% @see c_try/3 +%% @see c_try/5 -spec try_vars(c_try()) -> [cerl()]. @@ -3717,7 +3756,7 @@ try_vars(Node) -> %% %% @doc Returns the success body subtree of an abstract try-expression. %% -%% @see c_try/3 +%% @see c_try/5 -spec try_body(c_try()) -> cerl(). @@ -3730,7 +3769,7 @@ try_body(Node) -> %% @doc Returns the list of exception variable subtrees of an abstract %% try-expression. %% -%% @see c_try/3 +%% @see c_try/5 -spec try_evars(c_try()) -> [cerl()]. @@ -3743,7 +3782,7 @@ try_evars(Node) -> %% @doc Returns the exception body subtree of an abstract %% try-expression. %% -%% @see c_try/3 +%% @see c_try/5 -spec try_handler(c_try()) -> cerl(). @@ -3765,7 +3804,7 @@ try_handler(Node) -> %% @see update_c_catch/2 %% @see is_c_catch/1 %% @see catch_body/1 -%% @see c_try/3 +%% @see c_try/5 -spec c_catch(cerl()) -> c_catch(). diff --git a/lib/compiler/src/cerl_clauses.erl b/lib/compiler/src/cerl_clauses.erl index 87bd47c08b..805095e30c 100644 --- a/lib/compiler/src/cerl_clauses.erl +++ b/lib/compiler/src/cerl_clauses.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% @@ -354,29 +355,29 @@ match(P, E, Bs) -> {false, Bs} end end; - map -> - %% The most we can do is to say "definitely no match" if a - %% map pattern is matched against non-map data. - case E of - any -> - {false, Bs}; - _ -> - case type(E) of - literal -> - case is_map(concrete(E)) of - false -> - none; - true -> - {false, Bs} - end; - cons -> - none; - tuple -> - none; - _ -> - {false, Bs} - end - end; + map -> + %% The most we can do is to say "definitely no match" if a + %% map pattern is matched against non-map data. + case E of + any -> + {false, Bs}; + _ -> + case type(E) of + literal -> + case is_map(concrete(E)) of + false -> + none; + true -> + {false, Bs} + end; + cons -> + none; + tuple -> + none; + _ -> + {false, Bs} + end + end; _ -> match_1(P, E, Bs) end. diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl index 75740e8b9d..2a8cf2e758 100644 --- a/lib/compiler/src/cerl_inline.erl +++ b/lib/compiler/src/cerl_inline.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2012. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -445,15 +446,14 @@ i_var_1(R, Opnd, Ctxt, Env, S) -> residualize_var(R, S); false -> S1 = st__mark_inner_pending(L, S), - case catch {ok, visit(Opnd, S1)} of - {ok, {E, S2}} -> + try visit(Opnd, S1) of + {E, S2} -> %% Note that we pass the current environment and %% context to `copy', but not the current renaming. S3 = st__clear_inner_pending(L, S2), - copy(R, Opnd, E, Ctxt, Env, S3); - {'EXIT', X} -> - exit(X); - X -> + copy(R, Opnd, E, Ctxt, Env, S3) + catch + throw:X -> %% If we use destructive update for the %% `inner-pending' flag, we must make sure to clear %% it also if we make a nonlocal return. @@ -1128,8 +1128,8 @@ i_call_3(M, F, As, E, Ctxt, Env, S) -> %% Note that we extract the results of argument expessions here; the %% expressions could still be sequences with side effects. Vs = [concrete(result(A)) || A <- As], - case catch {ok, apply(atom_val(M), atom_val(F), Vs)} of - {ok, V} -> + try apply(atom_val(M), atom_val(F), Vs) of + V -> %% Evaluation completed normally - try to turn the result %% back into a syntax tree (representing a literal). case is_literal_term(V) of @@ -1142,8 +1142,9 @@ i_call_3(M, F, As, E, Ctxt, Env, S) -> false -> %% The result could not be represented as a literal. i_call_4(M, F, As, E, Ctxt, Env, S) - end; - _ -> + end + catch + error:_ -> %% The evaluation attempt did not complete normally. i_call_4(M, F, As, E, Ctxt, Env, S) end. @@ -1341,23 +1342,23 @@ i_bitstr(E, Ren, Env, S) -> S3 = count_size(weight(bitstr), S2), {update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}. -i_map(E, Ctx, Ren, Env, S) -> +i_map(E, Ctx, Ren, Env, S0) -> %% Visit the segments for value. - {M1, S1} = i(map_arg(E), value, Ren, Env, S), + {M1, S1} = i(map_arg(E), value, Ren, Env, S0), {Es, S2} = mapfoldl(fun (E, S) -> i_map_pair(E, Ctx, Ren, Env, S) end, S1, map_es(E)), S3 = count_size(weight(map), S2), {update_c_map(E, M1,Es), S3}. -i_map_pair(E, Ctx, Ren, Env, S) -> - %% It is not necessary to visit the Op and Key fields, - %% since these are always literals. - {Val, S1} = i(map_pair_val(E), Ctx, Ren, Env, S), +i_map_pair(E, Ctx, Ren, Env, S0) -> + %% It is not necessary to visit the Op field + %% since it is always a literal. + {Key, S1} = i(map_pair_key(E), value, Ren, Env, S0), + {Val, S2} = i(map_pair_val(E), Ctx, Ren, Env, S1), Op = map_pair_op(E), - Key = map_pair_key(E), - S2 = count_size(weight(map_pair), S1), - {update_c_map_pair(E, Op, Key, Val), S2}. + S3 = count_size(weight(map_pair), S2), + {update_c_map_pair(E, Op, Key, Val), S3}. %% This is a simplified version of `i_pattern', for lists of parameter @@ -1420,15 +1421,11 @@ i_pattern(E, Ren, Env, Ren0, Env0, S) -> S2 = count_size(weight(binary), S1), {update_c_binary(E, Es), S2}; map -> - %% map patterns should not have args - M = map_arg(E), - {Es, S1} = mapfoldl(fun (E, S) -> i_map_pair_pattern(E, Ren, Env, Ren0, Env0, S) - end, - S, map_es(E)), + end, S, map_es(E)), S2 = count_size(weight(map), S1), - {update_c_map(E, M, Es), S2}; + {update_c_map(E, map_arg(E), Es), S2}; _ -> case is_literal(E) of true -> @@ -1464,12 +1461,12 @@ i_bitstr_pattern(E, Ren, Env, Ren0, Env0, S) -> i_map_pair_pattern(E, Ren, Env, Ren0, Env0, S) -> %% It is not necessary to visit the Op it is always a literal. - %% Same goes for Key - {Val, S1} = i_pattern(map_pair_val(E), Ren, Env, Ren0, Env0, S), + %% Key is an expression + {Key, S1} = i(map_pair_key(E), value, Ren0, Env0, S), + {Val, S2} = i_pattern(map_pair_val(E), Ren, Env, Ren0, Env0, S1), Op = map_pair_op(E), %% should be 'exact' literal - Key = map_pair_key(E), - S2 = count_size(weight(map_pair), S1), - {update_c_map_pair(E, Op, Key, Val), S2}. + S3 = count_size(weight(map_pair), S2), + {update_c_map_pair(E, Op, Key, Val), S3}. %% --------------------------------------------------------------------- @@ -1740,12 +1737,11 @@ copy_1(R, Opnd, E, Ctxt, Env, S) -> copy_inline(R, Opnd, E, Ctxt, Env, S) -> S1 = st__mark_outer_pending(Opnd#opnd.loc, S), - case catch {ok, copy_inline_1(R, E, Ctxt, Env, S1)} of - {ok, {E1, S2}} -> - {E1, st__clear_outer_pending(Opnd#opnd.loc, S2)}; - {'EXIT', X} -> - exit(X); - X -> + try copy_inline_1(R, E, Ctxt, Env, S1) of + {E1, S2} -> + {E1, st__clear_outer_pending(Opnd#opnd.loc, S2)} + catch + throw:X -> %% If we use destructive update for the `outer-pending' %% flag, we must make sure to clear it upon a nonlocal %% return. @@ -1762,19 +1758,16 @@ copy_inline_1(R, E, Ctxt, Env, S) -> copy_inline_2(R, E, Ctxt, Env, S); false -> S1 = new_active_effort(get_effort_limit(S), S), - case catch {ok, copy_inline_2(R, E, Ctxt, Env, S1)} of - {ok, {E1, S2}} -> + try copy_inline_2(R, E, Ctxt, Env, S1) of + {E1, S2} -> %% Revert to the old effort counter. - {E1, revert_effort(S, S2)}; - {counter_exceeded, effort, _} -> + {E1, revert_effort(S, S2)} + catch + throw:{counter_exceeded, effort, _} -> %% Aborted this inlining attempt because too much %% effort was spent. Residualize the variable and %% revert to the previous state. - residualize_var(R, S); - {'EXIT', X} -> - exit(X); - X -> - throw(X) + residualize_var(R, S) end end. @@ -1800,11 +1793,12 @@ copy_inline_2(R, E, Ctxt, Env, S) -> %% close to zero at this point. (This is an extension to the %% original algorithm.) S1 = new_active_size(Limit + apply_size(length(Ctxt#app.opnds)), S), - case catch {ok, inline(E, Ctxt, ren__identity(), Env, S1)} of - {ok, {E1, S2}} -> + try inline(E, Ctxt, ren__identity(), Env, S1) of + {E1, S2} -> %% Revert to the old size counter. - {E1, revert_size(S, S2)}; - {counter_exceeded, size, S2} -> + {E1, revert_size(S, S2)} + catch + throw:{counter_exceeded, size, S2} -> %% Aborted this inlining attempt because it got too big. %% Residualize the variable and revert to the old size %% counter. (It is important that we do not also revert the @@ -1817,11 +1811,7 @@ copy_inline_2(R, E, Ctxt, Env, S) -> %% must make sure to clear the flags of any nested %% app-contexts upon aborting; see `inline' for details. S4 = reset_nested_apps(Ctxt, S3), % for effect - residualize_var(R, S4); - {'EXIT', X} -> - exit(X); - X -> - throw(X) + residualize_var(R, S4) end. reset_nested_apps(#app{ctxt = Ctxt, loc = L}, S) -> diff --git a/lib/compiler/src/cerl_sets.erl b/lib/compiler/src/cerl_sets.erl new file mode 100644 index 0000000000..0361186713 --- /dev/null +++ b/lib/compiler/src/cerl_sets.erl @@ -0,0 +1,207 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-2015. 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% +%% + +-module(cerl_sets). + +%% Standard interface. +-export([new/0,is_set/1,size/1,to_list/1,from_list/1]). +-export([is_element/2,add_element/2,del_element/2]). +-export([union/2,union/1,intersection/2,intersection/1]). +-export([is_disjoint/2]). +-export([subtract/2,is_subset/2]). +-export([fold/3,filter/2]). + +-export_type([set/0, set/1]). + +%%------------------------------------------------------------------------------ + +-type set() :: set(_). +-opaque set(Element) :: #{Element => 'ok'}. + +%%------------------------------------------------------------------------------ + +%% new() -> Set +-spec new() -> set(). + +new() -> #{}. + +%% is_set(Set) -> boolean(). +%% Return 'true' if Set is a set of elements, else 'false'. +-spec is_set(Set) -> boolean() when + Set :: term(). + +is_set(S) when is_map(S) -> true; +is_set(_) -> false. + +%% size(Set) -> int(). +%% Return the number of elements in Set. +-spec size(Set) -> non_neg_integer() when + Set :: set(). + +size(S) -> maps:size(S). + +%% to_list(Set) -> [Elem]. +%% Return the elements in Set as a list. +-spec to_list(Set) -> List when + Set :: set(Element), + List :: [Element]. + +to_list(S) -> maps:keys(S). + +%% from_list([Elem]) -> Set. +%% Build a set from the elements in List. +-spec from_list(List) -> Set when + List :: [Element], + Set :: set(Element). +from_list(Ls) -> maps:from_list([{K,ok}||K<-Ls]). + +%% is_element(Element, Set) -> boolean(). +%% Return 'true' if Element is an element of Set, else 'false'. +-spec is_element(Element, Set) -> boolean() when + Set :: set(Element). + +is_element(E,S) -> + case S of + #{E := _} -> true; + _ -> false + end. + +%% add_element(Element, Set) -> Set. +%% Return Set with Element inserted in it. +-spec add_element(Element, Set1) -> Set2 when + Set1 :: set(Element), + Set2 :: set(Element). + +add_element(E,S) -> S#{E=>ok}. + +-spec del_element(Element, Set1) -> Set2 when + Set1 :: set(Element), + Set2 :: set(Element). + +%% del_element(Element, Set) -> Set. +%% Return Set but with Element removed. +del_element(E,S) -> maps:remove(E,S). + +%% union(Set1, Set2) -> Set +%% Return the union of Set1 and Set2. +-spec union(Set1, Set2) -> Set3 when + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). + +union(S1,S2) -> maps:merge(S1,S2). + +%% union([Set]) -> Set +%% Return the union of the list of sets. +-spec union(SetList) -> Set when + SetList :: [set(Element)], + Set :: set(Element). + +union([S1,S2|Ss]) -> + union1(union(S1, S2), Ss); +union([S]) -> S; +union([]) -> new(). + +union1(S1, [S2|Ss]) -> + union1(union(S1, S2), Ss); +union1(S1, []) -> S1. + +%% intersection(Set1, Set2) -> Set. +%% Return the intersection of Set1 and Set2. +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). + +intersection(S1, S2) -> + filter(fun (E) -> is_element(E, S1) end, S2). + +%% intersection([Set]) -> Set. +%% Return the intersection of the list of sets. +-spec intersection(SetList) -> Set when + SetList :: [set(Element),...], + Set :: set(Element). + +intersection([S1,S2|Ss]) -> + intersection1(intersection(S1, S2), Ss); +intersection([S]) -> S. + +intersection1(S1, [S2|Ss]) -> + intersection1(intersection(S1, S2), Ss); +intersection1(S1, []) -> S1. + +%% is_disjoint(Set1, Set2) -> boolean(). +%% Check whether Set1 and Set2 are disjoint. +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: set(Element), + Set2 :: set(Element). + +is_disjoint(S1, S2) when map_size(S1) < map_size(S2) -> + fold(fun (_, false) -> false; + (E, true) -> not is_element(E, S2) + end, true, S1); +is_disjoint(S1, S2) -> + fold(fun (_, false) -> false; + (E, true) -> not is_element(E, S1) + end, true, S2). + +%% subtract(Set1, Set2) -> Set. +%% Return all and only the elements of Set1 which are not also in +%% Set2. +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). + +subtract(S1, S2) -> + filter(fun (E) -> not is_element(E, S2) end, S1). + +%% is_subset(Set1, Set2) -> boolean(). +%% Return 'true' when every element of Set1 is also a member of +%% Set2, else 'false'. +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: set(Element), + Set2 :: set(Element). + +is_subset(S1, S2) -> + fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1). + +%% fold(Fun, Accumulator, Set) -> Accumulator. +%% Fold function Fun over all elements in Set and return Accumulator. +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((Element, AccIn) -> AccOut), + Set :: set(Element), + Acc0 :: Acc, + Acc1 :: Acc, + AccIn :: Acc, + AccOut :: Acc. + +fold(F, Init, D) -> + lists:foldl(fun(E,Acc) -> F(E,Acc) end,Init,maps:keys(D)). + +%% filter(Fun, Set) -> Set. +%% Filter Set with Fun. +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((Element) -> boolean()), + Set1 :: set(Element), + Set2 :: set(Element). + +filter(F, D) -> + maps:from_list(lists:filter(fun({K,_}) -> F(K) end, maps:to_list(D))). diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl index e53bdd4efb..b3decbec1f 100644 --- a/lib/compiler/src/cerl_trees.erl +++ b/lib/compiler/src/cerl_trees.erl @@ -1,32 +1,33 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @doc Basic functions on Core Erlang abstract syntax trees. %% %% <p>Syntax trees are defined in the module <a -%% href=""><code>cerl</code></a>.</p> +%% href="cerl"><code>cerl</code></a>.</p> %% %% @type cerl() = cerl:cerl() -module(cerl_trees). -export([depth/1, fold/3, free_variables/1, get_label/1, label/1, label/2, - map/2, mapfold/3, size/1, variables/1]). + map/2, mapfold/3, mapfold/4, size/1, variables/1]). -import(cerl, [alias_pat/1, alias_var/1, ann_c_alias/3, ann_c_apply/3, ann_c_binary/2, ann_c_bitstr/6, ann_c_call/4, @@ -60,6 +61,7 @@ map_arg/1, map_es/1, ann_c_map/3, update_c_map/3, + is_c_map_pattern/1, ann_c_map_pattern/2, map_pair_key/1,map_pair_val/1,map_pair_op/1, ann_c_map_pair/4, update_c_map_pair/4 @@ -339,136 +341,162 @@ fold_pairs(_, S, []) -> %% starting with the given value <code>Initial</code>, while doing a %% post-order traversal of the tree, much like <code>fold/3</code>. %% +%% This is the same as mapfold/4, with an identity function as the +%% pre-operation. +%% %% @see map/2 %% @see fold/3 +%% @see mapfold/4 -spec mapfold(fun((cerl:cerl(), term()) -> {cerl:cerl(), term()}), term(), cerl:cerl()) -> {cerl:cerl(), term()}. mapfold(F, S0, T) -> + mapfold(fun(T0, A) -> {T0, A} end, F, S0, T). + + +%% @spec mapfold(Pre, Post, Initial::term(), Tree::cerl()) -> +%% {cerl(), term()} +%% +%% Pre = Post = (cerl(), term()) -> {cerl(), term()} +%% +%% @doc Does a combined map/fold operation on the nodes of the +%% tree. It begins by calling <code>Pre</code> on the tree, using the +%% <code>Initial</code> value. It then deconstructs the top node of +%% the returned tree and recurses on the children, using the returned +%% value as the new initial and carrying the returned values from one +%% call to the next. Finally it reassembles the top node from the +%% children, calls <code>Post</code> on it and returns the result. + +-spec mapfold(fun((cerl:cerl(), term()) -> {cerl:cerl(), term()}), + fun((cerl:cerl(), term()) -> {cerl:cerl(), term()}), + term(), cerl:cerl()) -> {cerl:cerl(), term()}. + +mapfold(Pre, Post, S00, T0) -> + {T, S0} = Pre(T0, S00), case type(T) of literal -> case concrete(T) of [_ | _] -> - {T1, S1} = mapfold(F, S0, cons_hd(T)), - {T2, S2} = mapfold(F, S1, cons_tl(T)), - F(update_c_cons(T, T1, T2), S2); + {T1, S1} = mapfold(Pre, Post, S0, cons_hd(T)), + {T2, S2} = mapfold(Pre, Post, S1, cons_tl(T)), + Post(update_c_cons(T, T1, T2), S2); V when tuple_size(V) > 0 -> - {Ts, S1} = mapfold_list(F, S0, tuple_es(T)), - F(update_c_tuple(T, Ts), S1); + {Ts, S1} = mapfold_list(Pre, Post, S0, tuple_es(T)), + Post(update_c_tuple(T, Ts), S1); _ -> - F(T, S0) + Post(T, S0) end; var -> - F(T, S0); + Post(T, S0); values -> - {Ts, S1} = mapfold_list(F, S0, values_es(T)), - F(update_c_values(T, Ts), S1); + {Ts, S1} = mapfold_list(Pre, Post, S0, values_es(T)), + Post(update_c_values(T, Ts), S1); cons -> - {T1, S1} = mapfold(F, S0, cons_hd(T)), - {T2, S2} = mapfold(F, S1, cons_tl(T)), - F(update_c_cons_skel(T, T1, T2), S2); + {T1, S1} = mapfold(Pre, Post, S0, cons_hd(T)), + {T2, S2} = mapfold(Pre, Post, S1, cons_tl(T)), + Post(update_c_cons_skel(T, T1, T2), S2); tuple -> - {Ts, S1} = mapfold_list(F, S0, tuple_es(T)), - F(update_c_tuple_skel(T, Ts), S1); + {Ts, S1} = mapfold_list(Pre, Post, S0, tuple_es(T)), + Post(update_c_tuple_skel(T, Ts), S1); map -> - {M , S1} = mapfold(F, S0, map_arg(T)), - {Ts, S2} = mapfold_list(F, S1, map_es(T)), - F(update_c_map(T, M, Ts), S2); + {M , S1} = mapfold(Pre, Post, S0, map_arg(T)), + {Ts, S2} = mapfold_list(Pre, Post, S1, map_es(T)), + Post(update_c_map(T, M, Ts), S2); map_pair -> - {Op, S1} = mapfold(F, S0, map_pair_op(T)), - {Key, S2} = mapfold(F, S1, map_pair_key(T)), - {Val, S3} = mapfold(F, S2, map_pair_val(T)), - F(update_c_map_pair(T,Op,Key,Val), S3); + {Op, S1} = mapfold(Pre, Post, S0, map_pair_op(T)), + {Key, S2} = mapfold(Pre, Post, S1, map_pair_key(T)), + {Val, S3} = mapfold(Pre, Post, S2, map_pair_val(T)), + Post(update_c_map_pair(T,Op,Key,Val), S3); 'let' -> - {Vs, S1} = mapfold_list(F, S0, let_vars(T)), - {A, S2} = mapfold(F, S1, let_arg(T)), - {B, S3} = mapfold(F, S2, let_body(T)), - F(update_c_let(T, Vs, A, B), S3); + {Vs, S1} = mapfold_list(Pre, Post, S0, let_vars(T)), + {A, S2} = mapfold(Pre, Post, S1, let_arg(T)), + {B, S3} = mapfold(Pre, Post, S2, let_body(T)), + Post(update_c_let(T, Vs, A, B), S3); seq -> - {A, S1} = mapfold(F, S0, seq_arg(T)), - {B, S2} = mapfold(F, S1, seq_body(T)), - F(update_c_seq(T, A, B), S2); + {A, S1} = mapfold(Pre, Post, S0, seq_arg(T)), + {B, S2} = mapfold(Pre, Post, S1, seq_body(T)), + Post(update_c_seq(T, A, B), S2); apply -> - {E, S1} = mapfold(F, S0, apply_op(T)), - {As, S2} = mapfold_list(F, S1, apply_args(T)), - F(update_c_apply(T, E, As), S2); + {E, S1} = mapfold(Pre, Post, S0, apply_op(T)), + {As, S2} = mapfold_list(Pre, Post, S1, apply_args(T)), + Post(update_c_apply(T, E, As), S2); call -> - {M, S1} = mapfold(F, S0, call_module(T)), - {N, S2} = mapfold(F, S1, call_name(T)), - {As, S3} = mapfold_list(F, S2, call_args(T)), - F(update_c_call(T, M, N, As), S3); + {M, S1} = mapfold(Pre, Post, S0, call_module(T)), + {N, S2} = mapfold(Pre, Post, S1, call_name(T)), + {As, S3} = mapfold_list(Pre, Post, S2, call_args(T)), + Post(update_c_call(T, M, N, As), S3); primop -> - {N, S1} = mapfold(F, S0, primop_name(T)), - {As, S2} = mapfold_list(F, S1, primop_args(T)), - F(update_c_primop(T, N, As), S2); + {N, S1} = mapfold(Pre, Post, S0, primop_name(T)), + {As, S2} = mapfold_list(Pre, Post, S1, primop_args(T)), + Post(update_c_primop(T, N, As), S2); 'case' -> - {A, S1} = mapfold(F, S0, case_arg(T)), - {Cs, S2} = mapfold_list(F, S1, case_clauses(T)), - F(update_c_case(T, A, Cs), S2); + {A, S1} = mapfold(Pre, Post, S0, case_arg(T)), + {Cs, S2} = mapfold_list(Pre, Post, S1, case_clauses(T)), + Post(update_c_case(T, A, Cs), S2); clause -> - {Ps, S1} = mapfold_list(F, S0, clause_pats(T)), - {G, S2} = mapfold(F, S1, clause_guard(T)), - {B, S3} = mapfold(F, S2, clause_body(T)), - F(update_c_clause(T, Ps, G, B), S3); + {Ps, S1} = mapfold_list(Pre, Post, S0, clause_pats(T)), + {G, S2} = mapfold(Pre, Post, S1, clause_guard(T)), + {B, S3} = mapfold(Pre, Post, S2, clause_body(T)), + Post(update_c_clause(T, Ps, G, B), S3); alias -> - {V, S1} = mapfold(F, S0, alias_var(T)), - {P, S2} = mapfold(F, S1, alias_pat(T)), - F(update_c_alias(T, V, P), S2); + {V, S1} = mapfold(Pre, Post, S0, alias_var(T)), + {P, S2} = mapfold(Pre, Post, S1, alias_pat(T)), + Post(update_c_alias(T, V, P), S2); 'fun' -> - {Vs, S1} = mapfold_list(F, S0, fun_vars(T)), - {B, S2} = mapfold(F, S1, fun_body(T)), - F(update_c_fun(T, Vs, B), S2); + {Vs, S1} = mapfold_list(Pre, Post, S0, fun_vars(T)), + {B, S2} = mapfold(Pre, Post, S1, fun_body(T)), + Post(update_c_fun(T, Vs, B), S2); 'receive' -> - {Cs, S1} = mapfold_list(F, S0, receive_clauses(T)), - {E, S2} = mapfold(F, S1, receive_timeout(T)), - {A, S3} = mapfold(F, S2, receive_action(T)), - F(update_c_receive(T, Cs, E, A), S3); + {Cs, S1} = mapfold_list(Pre, Post, S0, receive_clauses(T)), + {E, S2} = mapfold(Pre, Post, S1, receive_timeout(T)), + {A, S3} = mapfold(Pre, Post, S2, receive_action(T)), + Post(update_c_receive(T, Cs, E, A), S3); 'try' -> - {E, S1} = mapfold(F, S0, try_arg(T)), - {Vs, S2} = mapfold_list(F, S1, try_vars(T)), - {B, S3} = mapfold(F, S2, try_body(T)), - {Evs, S4} = mapfold_list(F, S3, try_evars(T)), - {H, S5} = mapfold(F, S4, try_handler(T)), - F(update_c_try(T, E, Vs, B, Evs, H), S5); + {E, S1} = mapfold(Pre, Post, S0, try_arg(T)), + {Vs, S2} = mapfold_list(Pre, Post, S1, try_vars(T)), + {B, S3} = mapfold(Pre, Post, S2, try_body(T)), + {Evs, S4} = mapfold_list(Pre, Post, S3, try_evars(T)), + {H, S5} = mapfold(Pre, Post, S4, try_handler(T)), + Post(update_c_try(T, E, Vs, B, Evs, H), S5); 'catch' -> - {B, S1} = mapfold(F, S0, catch_body(T)), - F(update_c_catch(T, B), S1); + {B, S1} = mapfold(Pre, Post, S0, catch_body(T)), + Post(update_c_catch(T, B), S1); binary -> - {Ds, S1} = mapfold_list(F, S0, binary_segments(T)), - F(update_c_binary(T, Ds), S1); + {Ds, S1} = mapfold_list(Pre, Post, S0, binary_segments(T)), + Post(update_c_binary(T, Ds), S1); bitstr -> - {Val, S1} = mapfold(F, S0, bitstr_val(T)), - {Size, S2} = mapfold(F, S1, bitstr_size(T)), - {Unit, S3} = mapfold(F, S2, bitstr_unit(T)), - {Type, S4} = mapfold(F, S3, bitstr_type(T)), - {Flags, S5} = mapfold(F, S4, bitstr_flags(T)), - F(update_c_bitstr(T, Val, Size, Unit, Type, Flags), S5); + {Val, S1} = mapfold(Pre, Post, S0, bitstr_val(T)), + {Size, S2} = mapfold(Pre, Post, S1, bitstr_size(T)), + {Unit, S3} = mapfold(Pre, Post, S2, bitstr_unit(T)), + {Type, S4} = mapfold(Pre, Post, S3, bitstr_type(T)), + {Flags, S5} = mapfold(Pre, Post, S4, bitstr_flags(T)), + Post(update_c_bitstr(T, Val, Size, Unit, Type, Flags), S5); letrec -> - {Ds, S1} = mapfold_pairs(F, S0, letrec_defs(T)), - {B, S2} = mapfold(F, S1, letrec_body(T)), - F(update_c_letrec(T, Ds, B), S2); + {Ds, S1} = mapfold_pairs(Pre, Post, S0, letrec_defs(T)), + {B, S2} = mapfold(Pre, Post, S1, letrec_body(T)), + Post(update_c_letrec(T, Ds, B), S2); module -> - {N, S1} = mapfold(F, S0, module_name(T)), - {Es, S2} = mapfold_list(F, S1, module_exports(T)), - {As, S3} = mapfold_pairs(F, S2, module_attrs(T)), - {Ds, S4} = mapfold_pairs(F, S3, module_defs(T)), - F(update_c_module(T, N, Es, As, Ds), S4) + {N, S1} = mapfold(Pre, Post, S0, module_name(T)), + {Es, S2} = mapfold_list(Pre, Post, S1, module_exports(T)), + {As, S3} = mapfold_pairs(Pre, Post, S2, module_attrs(T)), + {Ds, S4} = mapfold_pairs(Pre, Post, S3, module_defs(T)), + Post(update_c_module(T, N, Es, As, Ds), S4) end. -mapfold_list(F, S0, [T | Ts]) -> - {T1, S1} = mapfold(F, S0, T), - {Ts1, S2} = mapfold_list(F, S1, Ts), +mapfold_list(Pre, Post, S0, [T | Ts]) -> + {T1, S1} = mapfold(Pre, Post, S0, T), + {Ts1, S2} = mapfold_list(Pre, Post, S1, Ts), {[T1 | Ts1], S2}; -mapfold_list(_, S, []) -> +mapfold_list(_, _, S, []) -> {[], S}. -mapfold_pairs(F, S0, [{T1, T2} | Ps]) -> - {T3, S1} = mapfold(F, S0, T1), - {T4, S2} = mapfold(F, S1, T2), - {Ps1, S3} = mapfold_pairs(F, S2, Ps), +mapfold_pairs(Pre, Post, S0, [{T1, T2} | Ps]) -> + {T3, S1} = mapfold(Pre, Post, S0, T1), + {T4, S2} = mapfold(Pre, Post, S1, T2), + {Ps1, S3} = mapfold_pairs(Pre, Post, S2, Ps), {[{T3, T4} | Ps1], S3}; -mapfold_pairs(_, S, []) -> +mapfold_pairs(_, _, S, []) -> {[], S}. @@ -520,9 +548,9 @@ variables(T, S) -> tuple -> vars_in_list(tuple_es(T), S); map -> - vars_in_list(map_es(T), S); + vars_in_list([map_arg(T)|map_es(T)], S); map_pair -> - vars_in_list([map_pair_op(T),map_pair_key(T), map_pair_val(T)], S); + vars_in_list([map_pair_op(T),map_pair_key(T),map_pair_val(T)], S); 'let' -> Vs = variables(let_body(T), S), Vs1 = var_list_names(let_vars(T)), @@ -639,8 +667,8 @@ vars_in_list([], _, A) -> vars_in_defs(Ds, S) -> vars_in_defs(Ds, S, []). -vars_in_defs([{_, F} | Ds], S, A) -> - vars_in_defs(Ds, S, ordsets:union(variables(F, S), A)); +vars_in_defs([{_, Post} | Ds], S, A) -> + vars_in_defs(Ds, S, ordsets:union(variables(Post, S), A)); vars_in_defs([], _, A) -> A. @@ -702,13 +730,14 @@ label(T, N, Env) -> %% Constant literals are not labeled. {T, N}; var -> - case dict:find(var_name(T), Env) of - {ok, L} -> - {As, _} = label_ann(T, L), - N1 = N; - error -> - {As, N1} = label_ann(T, N) - end, + {As, N1} = + case dict:find(var_name(T), Env) of + {ok, L} -> + {A, _} = label_ann(T, L), + {A, N}; + error -> + label_ann(T, N) + end, {set_ann(T, As), N1}; values -> {Ts, N1} = label_list(values_es(T), N, Env), @@ -724,14 +753,21 @@ label(T, N, Env) -> {As, N2} = label_ann(T, N1), {ann_c_tuple_skel(As, Ts), N2}; map -> - {M, N1} = label(map_arg(T), N, Env), - {Ts, N2} = label_list(map_es(T), N1, Env), - {As, N3} = label_ann(T, N2), - {ann_c_map(As, M, Ts), N3}; + case is_c_map_pattern(T) of + false -> + {M, N1} = label(map_arg(T), N, Env), + {Ts, N2} = label_list(map_es(T), N1, Env), + {As, N3} = label_ann(T, N2), + {ann_c_map(As, M, Ts), N3}; + true -> + {Ts, N1} = label_list(map_es(T), N, Env), + {As, N2} = label_ann(T, N1), + {ann_c_map_pattern(As, Ts), N2} + end; map_pair -> {Op, N1} = label(map_pair_op(T), N, Env), - {Val, N2} = label(map_pair_key(T), N1, Env), - {Key, N3} = label(map_pair_val(T), N2, Env), + {Key, N2} = label(map_pair_key(T), N1, Env), + {Val, N3} = label(map_pair_val(T), N2, Env), {As, N4} = label_ann(T, N3), {ann_c_map_pair(As,Op,Key,Val), N4}; 'let' -> diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index c7d91070f6..e951a25e04 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2015. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -25,6 +26,7 @@ -export([forms/1,forms/2,noenv_forms/2]). -export([output_generated/1,noenv_output_generated/1]). -export([options/0]). +-export([env_compiler_options/0]). %% Erlc interface. -export([compile/3,compile_beam/3,compile_asm/3,compile_core/3]). @@ -39,14 +41,23 @@ %%---------------------------------------------------------------------- +-type abstract_code() :: [erl_parse:abstract_form()]. + +%% Internal representations used for 'from_asm' and 'from_beam' compilation can +%% also be valid, but have no relevant types defined. +-type forms() :: abstract_code() | cerl:c_module(). + -type option() :: atom() | {atom(), term()} | {'d', atom(), term()}. --type err_info() :: {erl_scan:line() | 'none', +-type err_info() :: {erl_anno:line() | 'none', module(), term()}. %% ErrorDescriptor -type errors() :: [{file:filename(), [err_info()]}]. -type warnings() :: [{file:filename(), [err_info()]}]. -type mod_ret() :: {'ok', module()} | {'ok', module(), cerl:c_module()} %% with option 'to_core' + | {'ok', %% with option 'to_pp' + module() | [], %% module() if 'to_exp' + abstract_code()} | {'ok', module(), warnings()}. -type bin_ret() :: {'ok', module(), binary()} | {'ok', module(), binary(), warnings()}. @@ -77,7 +88,11 @@ file(File, Opts) when is_list(Opts) -> file(File, Opt) -> file(File, [Opt|?DEFAULT_OPTIONS]). -forms(File) -> forms(File, ?DEFAULT_OPTIONS). +-spec forms(abstract_code()) -> comp_ret(). + +forms(Forms) -> forms(Forms, ?DEFAULT_OPTIONS). + +-spec forms(forms(), [option()] | option()) -> comp_ret(). forms(Forms, Opts) when is_list(Opts) -> do_compile({forms,Forms}, [binary|Opts++env_default_opts()]); @@ -105,6 +120,8 @@ noenv_file(File, Opts) when is_list(Opts) -> noenv_file(File, Opt) -> noenv_file(File, [Opt|?DEFAULT_OPTIONS]). +-spec noenv_forms(forms(), [option()] | option()) -> comp_ret(). + noenv_forms(Forms, Opts) when is_list(Opts) -> do_compile({forms,Forms}, [binary|Opts]); noenv_forms(Forms, Opt) when is_atom(Opt) -> @@ -119,6 +136,14 @@ noenv_output_generated(Opts) -> end, Passes). %% +%% Retrieve ERL_COMPILER_OPTIONS as a list of terms +%% + +-spec env_compiler_options() -> [term()]. + +env_compiler_options() -> env_default_opts(). + +%% %% Local functions %% @@ -132,7 +157,8 @@ env_default_opts() -> Str when is_list(Str) -> case erl_scan:string(Str) of {ok,Tokens,_} -> - case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of + Dot = {dot, erl_anno:new(1)}, + case erl_parse:parse_term(Tokens ++ [Dot]) of {ok,List} when is_list(List) -> List; {ok,Term} -> [Term]; {error,_Reason} -> @@ -214,6 +240,8 @@ format_error({epp,E}) -> epp:format_error(E); format_error(write_error) -> "error writing file"; +format_error({write_error, Error}) -> + io_lib:format("error writing file: ~ts", [file:format_error(Error)]); format_error({rename,From,To,Error}) -> io_lib:format("failed to rename ~ts to ~ts: ~ts", [From,To,file:format_error(Error)]); @@ -285,11 +313,20 @@ internal_comp(Passes, File, Suffix, St0) -> St1 = St0#compile{filename=File, dir=Dir, base=Base, ifile=erlfile(Dir, Base, Suffix), ofile=objfile(Base, St0)}, - Run = case member(time, St1#compile.options) of - true -> - io:format("Compiling ~tp\n", [File]), - fun run_tc/2; - false -> fun({_Name,Fun}, St) -> catch Fun(St) end + Opts = St1#compile.options, + Run0 = case member(time, Opts) of + true -> + io:format("Compiling ~tp\n", [File]), + fun run_tc/2; + false -> fun({_Name,Fun}, St) -> catch Fun(St) end + end, + Run = case keyfind(eprof, 1, Opts) of + {eprof,EprofPass} -> + fun(P, St) -> + run_eprof(P, EprofPass, St) + end; + false -> + Run0 end, case fold_comp(Passes, Run, St1) of {ok,St2} -> comp_ret_ok(St2); @@ -320,17 +357,26 @@ fold_comp([{Name,Pass}|Ps], Run, St0) -> fold_comp([], _Run, St) -> {ok,St}. run_tc({Name,Fun}, St) -> - Before0 = statistics(runtime), + T1 = erlang:monotonic_time(), Val = (catch Fun(St)), - After0 = statistics(runtime), - {Before_c, _} = Before0, - {After_c, _} = After0, + T2 = erlang:monotonic_time(), + Elapsed = erlang:convert_time_unit(T2 - T1, native, milli_seconds), Mem0 = erts_debug:flat_size(Val)*erlang:system_info(wordsize), Mem = lists:flatten(io_lib:format("~.1f kB", [Mem0/1024])), - io:format(" ~-30s: ~10.2f s ~12s\n", - [Name,(After_c-Before_c) / 1000,Mem]), + io:format(" ~-30s: ~10.3f s ~12s\n", + [Name,Elapsed/1000,Mem]), Val. +run_eprof({Name,Fun}, Name, St) -> + io:format("~p: Running eprof\n", [Name]), + c:appcall(tools, eprof, start_profiling, [[self()]]), + Val = (catch Fun(St)), + c:appcall(tools, eprof, stop_profiling, []), + c:appcall(tools, eprof, analyze, []), + Val; +run_eprof({_,Fun}, _, St) -> + catch Fun(St). + comp_ret_ok(#compile{code=Code,warnings=Warn0,module=Mod,options=Opts}=St) -> case werror(St) of true -> @@ -431,11 +477,6 @@ pass(from_core) -> {".core",[?pass(parse_core)|core_passes()]}; pass(from_asm) -> {".S",[?pass(beam_consult_asm)|asm_passes()]}; -pass(asm) -> - %% TODO: remove 'asm' in 18.0 - io:format("compile:file/2 option 'asm' has been deprecated and will be~n" - "removed in the 18.0 release. Use 'from_asm' instead.~n"), - pass(from_asm); pass(from_beam) -> {".beam",[?pass(read_beam_file)|binary_passes()]}; pass(_) -> none. @@ -611,7 +652,7 @@ standard_passes() -> {iff,'to_exp',{done,"E"}}, %% Conversion to Core Erlang. - ?pass(core_module), + {pass,v3_core}, {iff,'dcore',{listing,"core"}}, {iff,'to_core0',{done,"core"}} | core_passes()]. @@ -623,7 +664,7 @@ core_passes() -> [{unless,no_copt, [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1}, {iff,doldinline,{listing,"oldinline"}}, - ?pass(core_fold_module), + {pass,sys_core_fold}, {iff,dcorefold,{listing,"corefold"}}, {core_inline_module,fun test_core_inliner/1,fun core_inline_module/1}, {iff,dinline,{listing,"inline"}}, @@ -636,14 +677,14 @@ core_passes() -> kernel_passes() -> %% Destructive setelement/3 optimization and core lint. - [?pass(core_dsetel_module), + [{pass,sys_core_dsetel}, {iff,dsetel,{listing,"dsetel"}}, {iff,clint,?pass(core_lint_module)}, {iff,core,?pass(save_core_code)}, %% Kernel Erlang and code generation. - ?pass(kernel_module), + {pass,v3_kernel}, {iff,dkern,{listing,"kernel"}}, {iff,'to_kernel',{done,"kernel"}}, {pass,v3_life}, @@ -656,11 +697,16 @@ asm_passes() -> %% Assembly level optimisations. [{delay, [{pass,beam_a}, + {iff,da,{listing,"a"}}, {unless,no_postopt, - [{pass,beam_block}, + [{unless,no_reorder,{pass,beam_reorder}}, + {iff,dre,{listing,"reorder"}}, + {pass,beam_block}, {iff,dblk,{listing,"block"}}, {unless,no_except,{pass,beam_except}}, {iff,dexcept,{listing,"except"}}, + {unless,no_bs_opt,{pass,beam_bs}}, + {iff,dbs,{listing,"bs"}}, {unless,no_bopt,{pass,beam_bool}}, {iff,dbool,{listing,"bool"}}, {unless,no_topt,{pass,beam_type}}, @@ -688,6 +734,7 @@ asm_passes() -> {iff,no_postopt,[{pass,beam_clean}]}, {pass,beam_z}, + {iff,dz,{listing,"z"}}, {iff,dopt,{listing,"optimize"}}, {iff,'S',{listing,"S"}}, {iff,'to_asm',{done,"S"}}]}, @@ -906,28 +953,35 @@ transform_module(#compile{options=Opt,code=Code0}=St0) -> foldl_transform(St, [T|Ts]) -> Name = "transform " ++ atom_to_list(T), - Fun = fun(S) -> T:parse_transform(S#compile.code, S#compile.options) end, - Run = case member(time, St#compile.options) of - true -> fun run_tc/2; - false -> fun({_Name,F}, S) -> catch F(S) end - end, - case Run({Name, Fun}, St) of - {error,Es,Ws} -> - {error,St#compile{warnings=St#compile.warnings ++ Ws, - errors=St#compile.errors ++ Es}}; - {'EXIT',{undef,_}} -> - Es = [{St#compile.ifile,[{none,compile, - {undef_parse_transform,T}}]}], - {error,St#compile{errors=St#compile.errors ++ Es}}; - {'EXIT',R} -> - Es = [{St#compile.ifile,[{none,compile,{parse_transform,T,R}}]}], - {error,St#compile{errors=St#compile.errors ++ Es}}; - {warning, Forms, Ws} -> - foldl_transform( - St#compile{code=Forms, - warnings=St#compile.warnings ++ Ws}, Ts); - Forms -> - foldl_transform(St#compile{code=Forms}, Ts) + case code:ensure_loaded(T) =:= {module,T} andalso + erlang:function_exported(T, parse_transform, 2) of + true -> + Fun = fun(S) -> + T:parse_transform(S#compile.code, S#compile.options) + end, + Run = case member(time, St#compile.options) of + true -> fun run_tc/2; + false -> fun({_Name,F}, S) -> catch F(S) end + end, + case Run({Name, Fun}, St) of + {error,Es,Ws} -> + {error,St#compile{warnings=St#compile.warnings ++ Ws, + errors=St#compile.errors ++ Es}}; + {'EXIT',R} -> + Es = [{St#compile.ifile,[{none,compile, + {parse_transform,T,R}}]}], + {error,St#compile{errors=St#compile.errors ++ Es}}; + {warning, Forms, Ws} -> + foldl_transform( + St#compile{code=Forms, + warnings=St#compile.warnings ++ Ws}, Ts); + Forms -> + foldl_transform(St#compile{code=Forms}, Ts) + end; + false -> + Es = [{St#compile.ifile,[{none,compile, + {undef_parse_transform,T}}]}], + {error,St#compile{errors=St#compile.errors ++ Es}} end; foldl_transform(St, []) -> {ok,St}. @@ -1158,7 +1212,7 @@ makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) -> end, {ok,St} catch - exit:_ -> + error:_ -> %% Couldn't write to output Makefile. Err = {St#compile.ifile,[{none,?MODULE,write_error}]}, {error,St#compile{errors=St#compile.errors++[Err]}} @@ -1181,14 +1235,6 @@ expand_module(#compile{code=Code,options=Opts0}=St0) -> Opts = expand_opts(Opts1), {ok,St0#compile{module=Mod,options=Opts,code={Mod,Exp,Forms}}}. -core_module(#compile{code=Code0,options=Opts}=St) -> - {ok,Code,Ws} = v3_core:module(Code0, Opts), - {ok,St#compile{code=Code,warnings=St#compile.warnings ++ Ws}}. - -core_fold_module(#compile{code=Code0,options=Opts,warnings=Warns}=St) -> - {ok,Code,Ws} = sys_core_fold:module(Code0, Opts), - {ok,St#compile{code=Code,warnings=Warns ++ Ws}}. - core_fold_module_after_inlining(#compile{code=Code0,options=Opts}=St) -> %% Inlining may produce code that generates spurious warnings. %% Ignore all warnings. @@ -1224,14 +1270,6 @@ core_inline_module(#compile{code=Code0,options=Opts}=St) -> Code = cerl_inline:core_transform(Code0, Opts), {ok,St#compile{code=Code}}. -core_dsetel_module(#compile{code=Code0,options=Opts}=St) -> - {ok,Code} = sys_core_dsetel:module(Code0, Opts), - {ok,St#compile{code=Code}}. - -kernel_module(#compile{code=Code0,options=Opts}=St) -> - {ok,Code,Ws} = v3_kernel:module(Code0, Opts), - {ok,St#compile{code=Code,warnings=St#compile.warnings ++ Ws}}. - save_abstract_code(#compile{ifile=File}=St) -> case abstract_code(St) of {ok,Code} -> @@ -1240,7 +1278,8 @@ save_abstract_code(#compile{ifile=File}=St) -> {error,St#compile{errors=St#compile.errors ++ [{File,Es}]}} end. -abstract_code(#compile{code=Code,options=Opts,ofile=OFile}) -> +abstract_code(#compile{code=Code0,options=Opts,ofile=OFile}) -> + Code = erl_parse:anno_to_term(Code0), Abstr = erlang:term_to_binary({raw_abstract_v1,Code}, [compressed]), case member(encrypt_debug_info, Opts) of true -> @@ -1293,26 +1332,18 @@ generate_key(String) when is_list(String) -> encrypt({des3_cbc=Type,Key,IVec,BlockSize}, Bin0) -> Bin1 = case byte_size(Bin0) rem BlockSize of 0 -> Bin0; - N -> list_to_binary([Bin0,random_bytes(BlockSize-N)]) + N -> list_to_binary([Bin0,crypto:strong_rand_bytes(BlockSize-N)]) end, Bin = crypto:block_encrypt(Type, Key, IVec, Bin1), TypeString = atom_to_list(Type), list_to_binary([0,length(TypeString),TypeString,Bin]). -random_bytes(N) -> - {A,B,C} = now(), - _ = random:seed(A, B, C), - random_bytes_1(N, []). - -random_bytes_1(0, Acc) -> Acc; -random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]). - save_core_code(St) -> {ok,St#compile{core_code=cerl:from_records(St#compile.code)}}. beam_asm(#compile{ifile=File,code=Code0, abstract_code=Abst,mod_options=Opts0}=St) -> - Source = filename:absname(File), + Source = paranoid_absname(File), Opts1 = lists:map(fun({debug_info_key,_}) -> {debug_info_key,'********'}; (Other) -> Other end, Opts0), @@ -1321,6 +1352,16 @@ beam_asm(#compile{ifile=File,code=Code0, {ok,Code} -> {ok,St#compile{code=Code,abstract_code=[]}} end. +paranoid_absname(""=File) -> + File; +paranoid_absname(File) -> + case file:get_cwd() of + {ok,Cwd} -> + filename:absname(File, Cwd); + _ -> + File + end. + test_native(#compile{options=Opts}) -> %% This test is done late, in case some other option has turned off native. %% 'native' given on the command line can be overridden by @@ -1444,8 +1485,8 @@ save_binary_1(St) -> end, {error,St#compile{errors=St#compile.errors ++ Es}} end; - {error,_Error} -> - Es = [{Tfile,[{none,compile,write_error}]}], + {error,Error} -> + Es = [{Tfile,[{none,compile,{write_error,Error}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. @@ -1593,8 +1634,8 @@ listing(LFun, Ext, St) -> LFun(Lf, Code), ok = file:close(Lf), {ok,St}; - {error,_Error} -> - Es = [{Lfile,[{none,compile,write_error}]}], + {error,Error} -> + Es = [{Lfile,[{none,compile,{write_error,Error}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. @@ -1604,11 +1645,8 @@ output_encoding(F, #compile{encoding = Encoding}) -> ok = io:setopts(F, [{encoding, Encoding}]), ok = io:fwrite(F, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]). -restore_expanded_types("P", Fs) -> - epp:restore_typed_record_fields(Fs); restore_expanded_types("E", {M,I,Fs0}) -> - Fs1 = restore_expand_module(Fs0), - Fs = epp:restore_typed_record_fields(Fs1), + Fs = restore_expand_module(Fs0), {M,I,Fs}; restore_expanded_types(_Ext, Code) -> Code. @@ -1620,6 +1658,8 @@ restore_expand_module([{attribute,Line,spec,[Arg]}|Fs]) -> [{attribute,Line,spec,Arg}|restore_expand_module(Fs)]; restore_expand_module([{attribute,Line,callback,[Arg]}|Fs]) -> [{attribute,Line,callback,Arg}|restore_expand_module(Fs)]; +restore_expand_module([{attribute,Line,record,[R]}|Fs]) -> + [{attribute,Line,record,R}|restore_expand_module(Fs)]; restore_expand_module([F|Fs]) -> [F|restore_expand_module(Fs)]; restore_expand_module([]) -> []. @@ -1666,6 +1706,7 @@ help(_) -> %% Compile entry point for erl_compile. compile(File0, _OutFile, Options) -> + pre_load(), File = shorten_filename(File0), case file(File, make_erl_options(Options)) of {ok,_Mod} -> ok; @@ -1730,3 +1771,47 @@ make_erl_options(Opts) -> end, Options ++ [report_errors, {cwd, Cwd}, {outdir, Outdir}| [{i, Dir} || Dir <- Includes]] ++ Specific. + +pre_load() -> + L = [beam_a, + beam_asm, + beam_block, + beam_bool, + beam_bs, + beam_bsm, + beam_clean, + beam_dead, + beam_dict, + beam_except, + beam_flatten, + beam_jump, + beam_opcodes, + beam_peep, + beam_receive, + beam_reorder, + beam_split, + beam_trim, + beam_type, + beam_utils, + beam_validator, + beam_z, + cerl, + cerl_clauses, + cerl_sets, + cerl_trees, + core_lib, + epp, + erl_bifs, + erl_expand_records, + erl_lint, + erl_parse, + erl_scan, + sys_core_dsetel, + sys_core_fold, + sys_pre_expand, + v3_codegen, + v3_core, + v3_kernel, + v3_life], + _ = code:ensure_modules_loaded(L), + ok. diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src index 8f68915f8e..1fd7800e85 100644 --- a/lib/compiler/src/compiler.app.src +++ b/lib/compiler/src/compiler.app.src @@ -1,18 +1,19 @@ % This is an -*- erlang -*- file. %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% @@ -24,6 +25,7 @@ beam_asm, beam_block, beam_bool, + beam_bs, beam_bsm, beam_clean, beam_dead, @@ -36,6 +38,7 @@ beam_opcodes, beam_peep, beam_receive, + beam_reorder, beam_split, beam_trim, beam_type, @@ -45,6 +48,7 @@ cerl, cerl_clauses, cerl_inline, + cerl_sets, cerl_trees, compile, core_scan, @@ -56,6 +60,7 @@ rec_env, sys_core_dsetel, sys_core_fold, + sys_core_fold_lists, sys_core_inline, sys_pre_attributes, sys_pre_expand, @@ -68,5 +73,5 @@ {registered, []}, {applications, [kernel, stdlib]}, {env, []}, - {runtime_dependencies, ["stdlib-2.0","kernel-3.0","hipe-3.10.3","erts-6.0", - "crypto-3.3"]}]}. + {runtime_dependencies, ["stdlib-2.5","kernel-4.0","hipe-3.12","erts-7.0", + "crypto-3.6"]}]}. diff --git a/lib/compiler/src/compiler.appup.src b/lib/compiler/src/compiler.appup.src index fe273b269c..e9db37e203 100644 --- a/lib/compiler/src/compiler.appup.src +++ b/lib/compiler/src/compiler.appup.src @@ -1,18 +1,19 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2014. All Rights Reserved. +%% Copyright Ericsson AB 2014-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% {"%VSN%", diff --git a/lib/compiler/src/core_lib.erl b/lib/compiler/src/core_lib.erl index 0d95971f91..c1806272bd 100644 --- a/lib/compiler/src/core_lib.erl +++ b/lib/compiler/src/core_lib.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -20,78 +21,16 @@ -module(core_lib). --export([get_anno/1,set_anno/2]). --export([is_literal/1,is_literal_list/1]). --export([literal_value/1]). -export([make_values/1]). -export([is_var_used/2]). -include("core_parse.hrl"). -%% -%% Generic get/set annotation that should be used only with cerl() structures. -%% --spec get_anno(cerl:cerl()) -> term(). - -get_anno(C) -> element(2, C). - --spec set_anno(cerl:cerl(), term()) -> cerl:cerl(). - -set_anno(C, A) -> setelement(2, C, A). - --spec is_literal(cerl:cerl()) -> boolean(). - -is_literal(#c_literal{}) -> true; -is_literal(#c_cons{hd=H,tl=T}) -> - is_literal(H) andalso is_literal(T); -is_literal(#c_tuple{es=Es}) -> is_literal_list(Es); -is_literal(#c_binary{segments=Es}) -> is_lit_bin(Es); -is_literal(_) -> false. - --spec is_literal_list([cerl:cerl()]) -> boolean(). - -is_literal_list(Es) -> lists:all(fun is_literal/1, Es). - -is_lit_bin(Es) -> - lists:all(fun (#c_bitstr{val=E,size=S}) -> - is_literal(E) andalso is_literal(S) - end, Es). - -%% Return the value of LitExpr. --spec literal_value(cerl:c_literal() | cerl:c_binary() | - cerl:c_map() | cerl:c_cons() | cerl:c_tuple()) -> term(). - -literal_value(#c_literal{val=V}) -> V; -literal_value(#c_binary{segments=Es}) -> - list_to_binary([literal_value_bin(Bit) || Bit <- Es]); -literal_value(#c_cons{hd=H,tl=T}) -> - [literal_value(H)|literal_value(T)]; -literal_value(#c_tuple{es=Es}) -> - list_to_tuple(literal_value_list(Es)); -literal_value(#c_map{arg=Cm,es=Cmps}) -> - M = literal_value(Cm), - lists:foldl(fun(#c_map_pair{ key=Ck, val=Cv },Mi) -> - K = literal_value(Ck), - V = literal_value(Cv), - maps:put(K,V,Mi) - end, M, Cmps). - -literal_value_list(Vals) -> [literal_value(V) || V <- Vals]. - -literal_value_bin(#c_bitstr{val=Val,size=Sz,unit=U,type=T,flags=Fs}) -> - %% We will only handle literals constructed by make_literal/1. - %% Could be made more general in the future if the need arises. - 8 = literal_value(Sz), - 1 = literal_value(U), - integer = literal_value(T), - [unsigned,big] = literal_value(Fs), - literal_value(Val). - %% Make a suitable values structure, expr or values, depending on Expr. -spec make_values([cerl:cerl()] | cerl:cerl()) -> cerl:cerl(). make_values([E]) -> E; -make_values([H|_]=Es) -> #c_values{anno=get_anno(H),es=Es}; +make_values([H|_]=Es) -> #c_values{anno=cerl:get_ann(H),es=Es}; make_values([]) -> #c_values{es=[]}; make_values(E) -> E. @@ -236,10 +175,15 @@ vu_pat_seg_list(V, Ss, St) -> end end, St, Ss). -vu_map_pairs(V, [#c_map_pair{val=Pat}|T], St0) -> - case vu_pattern(V, Pat, St0) of - {true,_}=St -> St; - St -> vu_map_pairs(V, T, St) +vu_map_pairs(V, [#c_map_pair{key=Key,val=Pat}|T], St0) -> + case vu_expr(V, Key) of + true -> + {true,false}; + false -> + case vu_pattern(V, Pat, St0) of + {true,_}=St -> St; + St -> vu_map_pairs(V, T, St) + end end; vu_map_pairs(_, [], St) -> St. diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl index 25df33a287..7d3513c0ba 100644 --- a/lib/compiler/src/core_lint.erl +++ b/lib/compiler/src/core_lint.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2015. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -33,9 +34,6 @@ %% Values only as multiple values/variables/patterns. %% Return same number of values as requested %% Correct number of arguments -%% -%% Checks to add: -%% %% Consistency of values/variables %% Consistency of function return values/calls. %% @@ -75,7 +73,7 @@ %% Define the lint state record. -record(lint, {module :: module(), % Current module - func :: fa(), % Current function + func :: fa() | 'undefined', % Current function errors = [] :: [error()], % Errors warnings= [] :: [warning()]}). % Warnings @@ -176,7 +174,7 @@ check_exports(Es, St) -> end. check_attrs(As, St) -> - case all(fun ({#c_literal{},V}) -> core_lib:is_literal(V); + case all(fun ({#c_literal{},#c_literal{}}) -> true; (_) -> false end, As) of true -> St; @@ -211,7 +209,7 @@ functions(Fs, Def, St0) -> function({#c_var{name={_,_}},B}, Def, St) -> %% Body must be a fun! case B of - #c_fun{} -> expr(B, Def, any, St); + #c_fun{} -> expr(B, Def, 1, St); _ -> add_error({illegal_expr,St#lint.func}, St) end. @@ -247,40 +245,42 @@ gbody(E, Def, Rt, St0) -> false -> St1 end. -gexpr(#c_var{name=N}, Def, _Rt, St) when is_atom(N); is_integer(N) -> - expr_var(N, Def, St); -gexpr(#c_literal{}, _Def, _Rt, St) -> St; -gexpr(#c_cons{hd=H,tl=T}, Def, _Rt, St) -> - gexpr_list([H,T], Def, St); -gexpr(#c_tuple{es=Es}, Def, _Rt, St) -> - gexpr_list(Es, Def, St); -gexpr(#c_map{es=Es}, Def, _Rt, St) -> - gexpr_list(Es, Def, St); -gexpr(#c_map_pair{key=K,val=V}, Def, _Rt, St) -> - gexpr_list([K,V], Def, St); -gexpr(#c_binary{segments=Ss}, Def, _Rt, St) -> - gbitstr_list(Ss, Def, St); +gexpr(#c_var{name=N}, Def, Rt, St) when is_atom(N); is_integer(N) -> + return_match(Rt, 1, expr_var(N, Def, St)); +gexpr(#c_literal{}, _Def, Rt, St) -> + return_match(Rt, 1, St); +gexpr(#c_cons{hd=H,tl=T}, Def, Rt, St) -> + return_match(Rt, 1, gexpr_list([H,T], Def, St)); +gexpr(#c_tuple{es=Es}, Def, Rt, St) -> + return_match(Rt, 1, gexpr_list(Es, Def, St)); +gexpr(#c_map{es=Es}, Def, Rt, St) -> + return_match(Rt, 1, gexpr_list(Es, Def, St)); +gexpr(#c_map_pair{key=K,val=V}, Def, Rt, St) -> + return_match(Rt, 1, gexpr_list([K,V], Def, St)); +gexpr(#c_binary{segments=Ss}, Def, Rt, St) -> + return_match(Rt, 1, gbitstr_list(Ss, Def, St)); gexpr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) -> - St1 = gexpr(Arg, Def, any, St0), %Ignore values - gbody(B, Def, Rt, St1); + St1 = gexpr(Arg, Def, 1, St0), + return_match(Rt, 1, gbody(B, Def, Rt, St1)); gexpr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) -> St1 = gbody(Arg, Def, let_varcount(Vs), St0), %This is a guard body {Lvs,St2} = variable_list(Vs, St1), gbody(B, union(Lvs, Def), Rt, St2); gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=is_record}, args=[Arg,#c_literal{val=Tag},#c_literal{val=Size}]}, - Def, 1, St) when is_atom(Tag), is_integer(Size) -> - gexpr(Arg, Def, 1, St); + Def, Rt, St) when is_atom(Tag), is_integer(Size) -> + return_match(Rt, 1, gexpr(Arg, Def, 1, St)); gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=is_record}}, - _Def, 1, St) -> - add_error({illegal_guard,St#lint.func}, St); + _Def, Rt, St) -> + return_match(Rt, 1, add_error({illegal_guard,St#lint.func}, St)); gexpr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=Name},args=As}, - Def, 1, St) when is_atom(Name) -> + Def, Rt, St0) when is_atom(Name) -> + St1 = return_match(Rt, 1, St0), case is_guard_bif(Name, length(As)) of true -> - gexpr_list(As, Def, St); + gexpr_list(As, Def, St1); false -> - add_error({illegal_guard,St#lint.func}, St) + add_error({illegal_guard,St1#lint.func}, St1) end; gexpr(#c_primop{name=#c_literal{val=A},args=As}, Def, _Rt, St0) when is_atom(A) -> gexpr_list(As, Def, St0); @@ -319,23 +319,25 @@ is_guard_bif(Name, Arity) -> %% expr(Expr, Defined, RetCount, State) -> State. -expr(#c_var{name={_,_}=FA}, Def, _Rt, St) -> - expr_fname(FA, Def, St); -expr(#c_var{name=N}, Def, _Rt, St) -> expr_var(N, Def, St); -expr(#c_literal{}, _Def, _Rt, St) -> St; -expr(#c_cons{hd=H,tl=T}, Def, _Rt, St) -> - expr_list([H,T], Def, St); -expr(#c_tuple{es=Es}, Def, _Rt, St) -> - expr_list(Es, Def, St); -expr(#c_map{es=Es}, Def, _Rt, St) -> - expr_list(Es, Def, St); -expr(#c_map_pair{key=K,val=V},Def,_Rt,St) -> - expr_list([K,V],Def,St); -expr(#c_binary{segments=Ss}, Def, _Rt, St) -> - bitstr_list(Ss, Def, St); +expr(#c_var{name={_,_}=FA}, Def, Rt, St) -> + return_match(Rt, 1, expr_fname(FA, Def, St)); +expr(#c_var{name=N}, Def, Rt, St) -> + return_match(Rt, 1, expr_var(N, Def, St)); +expr(#c_literal{}, _Def, Rt, St) -> + return_match(Rt, 1, St); +expr(#c_cons{hd=H,tl=T}, Def, Rt, St) -> + return_match(Rt, 1, expr_list([H,T], Def, St)); +expr(#c_tuple{es=Es}, Def, Rt, St) -> + return_match(Rt, 1, expr_list(Es, Def, St)); +expr(#c_map{es=Es}, Def, Rt, St) -> + return_match(Rt, 1, expr_list(Es, Def, St)); +expr(#c_map_pair{key=K,val=V}, Def, Rt, St) -> + return_match(Rt, 1, expr_list([K,V], Def, St)); +expr(#c_binary{segments=Ss}, Def, Rt, St) -> + return_match(Rt, 1, bitstr_list(Ss, Def, St)); expr(#c_fun{vars=Vs,body=B}, Def, Rt, St0) -> {Vvs,St1} = variable_list(Vs, St0), - return_match(Rt, 1, body(B, union(Vvs, Def), any, St1)); + return_match(Rt, 1, body(B, union(Vvs, Def), 1, St1)); expr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) -> St1 = expr(Arg, Def, 1, St0), body(B, Def, Rt, St1); @@ -361,15 +363,26 @@ expr(#c_receive{clauses=Cs,timeout=T,action=A}, Def, Rt, St0) -> St1 = expr(T, Def, 1, St0), St2 = body(A, Def, Rt, St1), clauses(Cs, Def, 1, Rt, St2); -expr(#c_apply{op=Op,args=As}, Def, _Rt, St0) -> +expr(#c_apply{op=Op,args=As}, Def, Rt, St0) -> St1 = apply_op(Op, Def, length(As), St0), - expr_list(As, Def, St1); + return_match(Rt, 1, expr_list(As, Def, St1)); +expr(#c_call{module=#c_literal{val=erlang},name=#c_literal{val=Name},args=As}, + Def, Rt, St0) when is_atom(Name) -> + St1 = expr_list(As, Def, St0), + case erl_bifs:is_exit_bif(erlang, Name, length(As)) of + true -> St1; + false -> return_match(Rt, 1, St1) + end; expr(#c_call{module=M,name=N,args=As}, Def, _Rt, St0) -> St1 = expr(M, Def, 1, St0), St2 = expr(N, Def, 1, St1), expr_list(As, Def, St2); -expr(#c_primop{name=#c_literal{val=A},args=As}, Def, _Rt, St0) when is_atom(A) -> - expr_list(As, Def, St0); +expr(#c_primop{name=#c_literal{val=A},args=As}, Def, Rt, St0) when is_atom(A) -> + St1 = expr_list(As, Def, St0), + case A of + match_fail -> St1; + _ -> return_match(Rt, 1, St1) + end; expr(#c_catch{body=B}, Def, Rt, St) -> return_match(Rt, 1, body(B, Def, 1, St)); expr(#c_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Def, Rt, St0) -> diff --git a/lib/compiler/src/core_parse.hrl b/lib/compiler/src/core_parse.hrl index 4a00535360..83a6f0179c 100644 --- a/lib/compiler/src/core_parse.hrl +++ b/lib/compiler/src/core_parse.hrl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -72,7 +73,8 @@ -record(c_map, {anno=[], arg=#c_literal{val=#{}} :: cerl:c_var() | cerl:c_literal(), - es :: [cerl:c_map_pair()]}). + es :: [cerl:c_map_pair()], + is_pat=false :: boolean()}). -record(c_map_pair, {anno=[], op :: #c_literal{val::'assoc'} | #c_literal{val::'exact'}, diff --git a/lib/compiler/src/core_parse.yrl b/lib/compiler/src/core_parse.yrl index a66ad4235f..8028aa99bb 100644 --- a/lib/compiler/src/core_parse.yrl +++ b/lib/compiler/src/core_parse.yrl @@ -1,18 +1,19 @@ %% -*-Erlang-*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -46,19 +47,22 @@ receive_expr timeout try_expr sequence catch_expr variable clause clause_pattern -map_expr map_pairs map_pair map_pair_assoc map_pair_exact +map_expr anno_map_expr map_pairs anno_map_pair map_pair map_pair_assoc map_pair_exact map_pattern map_pair_patterns map_pair_pattern -annotation anno_fun anno_expression anno_expressions +annotation anno_atom anno_fun anno_expression anno_expressions anno_variable anno_variables anno_pattern anno_patterns anno_function_name +anno_literal +anno_segment anno_segment_pattern anno_clause anno_clauses. Terminals %% Separators -'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#' '~' '::' +'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#' +'~' '=>' ':=' %% Keywords (atoms are assumed to always be single-quoted). @@ -88,7 +92,7 @@ module_definition -> module_definition -> '(' 'module' atom module_export module_attribute module_defs 'end' '-|' annotation ')' : - #c_module{anno='$9',name=tok_val('$3'),exports='$4', + #c_module{anno='$9',name=#c_literal{val=tok_val('$3')},exports='$4', attrs='$5',defs='$6'}. module_export -> '[' ']' : []. @@ -97,7 +101,7 @@ module_export -> '[' exported_names ']' : '$2'. exported_names -> exported_name ',' exported_names : ['$1' | '$3']. exported_names -> exported_name : ['$1']. -exported_name -> function_name : '$1'. +exported_name -> anno_function_name : '$1'. module_attribute -> 'attributes' '[' ']' : []. module_attribute -> 'attributes' '[' attribute_list ']' : '$3'. @@ -105,8 +109,16 @@ module_attribute -> 'attributes' '[' attribute_list ']' : '$3'. attribute_list -> attribute ',' attribute_list : ['$1' | '$3']. attribute_list -> attribute : ['$1']. -attribute -> atom '=' literal : - {#c_literal{val=tok_val('$1')},'$3'}. +attribute -> anno_atom '=' anno_literal : + {'$1','$3'}. + +anno_atom -> atom : + cerl:c_atom(tok_val('$1')). +anno_atom -> '(' atom '-|' annotation ')' : + cerl:ann_c_atom('$4', tok_val('$2')). + +anno_literal -> literal : '$1'. +anno_literal -> '(' literal '-|' annotation ')' : cerl:set_ann('$2', '$4'). module_defs -> function_definitions : '$1'. @@ -123,7 +135,7 @@ function_definition -> {'$1','$3'}. anno_fun -> '(' fun_expr '-|' annotation ')' : - core_lib:set_anno('$2', '$4'). + cerl:set_ann('$2', '$4'). anno_fun -> fun_expr : '$1'. %% Constant terms for annotations and attributes. @@ -162,7 +174,7 @@ tail_constant -> ',' constant tail_constant : ['$2'|'$3']. %% ( ( V -| <anno> ) = ( {a} -| <anno> ) -| <anno> ) anno_pattern -> '(' other_pattern '-|' annotation ')' : - core_lib:set_anno('$2', '$4'). + cerl:set_ann('$2', '$4'). anno_pattern -> other_pattern : '$1'. anno_pattern -> anno_variable : '$1'. @@ -182,31 +194,41 @@ atomic_pattern -> atomic_literal : '$1'. tuple_pattern -> '{' '}' : c_tuple([]). tuple_pattern -> '{' anno_patterns '}' : c_tuple('$2'). -map_pattern -> '~' '{' '}' '~' : #c_map{es=[]}. +map_pattern -> '~' '{' '}' '~' : c_map_pattern([]). map_pattern -> '~' '{' map_pair_patterns '}' '~' : - #c_map{es=lists:sort('$3')}. + c_map_pattern('$3'). +map_pattern -> '~' '{' map_pair_patterns '|' anno_map_expr '}' '~' : + ann_c_map_pattern('$5', '$3'). map_pair_patterns -> map_pair_pattern : ['$1']. map_pair_patterns -> map_pair_pattern ',' map_pair_patterns : ['$1' | '$3']. -map_pair_pattern -> '~' '<' anno_pattern ',' anno_pattern '>' : - #c_map_pair{op=#c_literal{val=exact},key='$3',val='$5'}. +map_pair_pattern -> anno_expression ':=' anno_pattern : + #c_map_pair{op=#c_literal{val=exact}, + key='$1',val='$3'}. +map_pair_pattern -> '(' anno_expression ':=' anno_pattern '-|' annotation ')' : + #c_map_pair{anno='$6',op=#c_literal{val=exact}, + key='$2',val='$4'}. cons_pattern -> '[' anno_pattern tail_pattern : - #c_cons{hd='$2',tl='$3'}. + c_cons('$2', '$3'). tail_pattern -> ']' : #c_literal{val=[]}. tail_pattern -> '|' anno_pattern ']' : '$2'. tail_pattern -> ',' anno_pattern tail_pattern : - #c_cons{hd='$2',tl='$3'}. + c_cons('$2', '$3'). binary_pattern -> '#' '{' '}' '#' : #c_binary{segments=[]}. binary_pattern -> '#' '{' segment_patterns '}' '#' : #c_binary{segments='$3'}. -segment_patterns -> segment_pattern ',' segment_patterns : ['$1' | '$3']. -segment_patterns -> segment_pattern : ['$1']. +segment_patterns -> anno_segment_pattern ',' segment_patterns : ['$1' | '$3']. +segment_patterns -> anno_segment_pattern : ['$1']. -segment_pattern -> '#' '<' anno_pattern '>' '(' anno_patterns ')': +anno_segment_pattern -> segment_pattern : '$1'. +anno_segment_pattern -> '(' segment_pattern '-|' annotation ')' : + cerl:set_ann('$2', '$4'). + +segment_pattern -> '#' '<' anno_pattern '>' '(' anno_expressions ')': case '$6' of [S,U,T,Fs] -> #c_bitstr{val='$3',size=S,unit=U,type=T,flags=Fs}; @@ -222,7 +244,7 @@ anno_variables -> anno_variable : ['$1']. anno_variable -> variable : '$1'. anno_variable -> '(' variable '-|' annotation ')' : - core_lib:set_anno('$2', '$4'). + cerl:set_ann('$2', '$4'). %% Expressions %% Must split expressions into two levels as nested value expressions @@ -230,7 +252,7 @@ anno_variable -> '(' variable '-|' annotation ')' : anno_expression -> expression : '$1'. anno_expression -> '(' expression '-|' annotation ')' : - core_lib:set_anno('$2', '$4'). + cerl:set_ann('$2', '$4'). anno_expressions -> anno_expression ',' anno_expressions : ['$1' | '$3']. anno_expressions -> anno_expression : ['$1']. @@ -279,26 +301,32 @@ cons_literal -> '[' literal tail_literal : c_cons('$2', '$3'). tail_literal -> ']' : #c_literal{val=[]}. tail_literal -> '|' literal ']' : '$2'. -tail_literal -> ',' literal tail_literal : #c_cons{hd='$2',tl='$3'}. +tail_literal -> ',' literal tail_literal : c_cons('$2', '$3'). tuple -> '{' '}' : c_tuple([]). tuple -> '{' anno_expressions '}' : c_tuple('$2'). -map_expr -> '~' '{' '}' '~' : #c_map{es=[]}. -map_expr -> '~' '{' map_pairs '}' '~' : #c_map{es='$3'}. -map_expr -> '~' '{' map_pairs '|' variable '}' '~' : #c_map{arg='$5',es='$3'}. -map_expr -> '~' '{' map_pairs '|' map_expr '}' '~' : #c_map{arg='$5',es='$3'}. +map_expr -> '~' '{' '}' '~' : c_map([]). +map_expr -> '~' '{' map_pairs '}' '~' : c_map('$3'). +map_expr -> '~' '{' map_pairs '|' anno_variable '}' '~' : ann_c_map([], '$5', '$3'). +map_expr -> '~' '{' map_pairs '|' anno_map_expr '}' '~' : ann_c_map([], '$5', '$3'). + +anno_map_expr -> map_expr : '$1'. +anno_map_expr -> '(' map_expr '-|' annotation ')' : cerl:set_ann('$2', '$4'). + +map_pairs -> anno_map_pair : ['$1']. +map_pairs -> anno_map_pair ',' map_pairs : ['$1' | '$3']. -map_pairs -> map_pair : ['$1']. -map_pairs -> map_pair ',' map_pairs : ['$1' | '$3']. +anno_map_pair -> map_pair : '$1'. +anno_map_pair -> '(' map_pair '-|' annotation ')' : cerl:set_ann('$2', '$4'). map_pair -> map_pair_assoc : '$1'. map_pair -> map_pair_exact : '$1'. -map_pair_assoc -> '::' '<' anno_expression ',' anno_expression'>' : - #c_map_pair{op=#c_literal{val=assoc},key='$3',val='$5'}. -map_pair_exact -> '~' '<' anno_expression ',' anno_expression'>' : - #c_map_pair{op=#c_literal{val=exact},key='$3',val='$5'}. +map_pair_assoc -> anno_expression '=>' anno_expression : + #c_map_pair{op=#c_literal{val=assoc},key='$1',val='$3'}. +map_pair_exact -> anno_expression ':=' anno_expression : + #c_map_pair{op=#c_literal{val=exact},key='$1',val='$3'}. cons -> '[' anno_expression tail : c_cons('$2', '$3'). @@ -307,10 +335,13 @@ tail -> '|' anno_expression ']' : '$2'. tail -> ',' anno_expression tail : c_cons('$2', '$3'). binary -> '#' '{' '}' '#' : #c_literal{val = <<>>}. -binary -> '#' '{' segments '}' '#' : #c_binary{segments='$3'}. +binary -> '#' '{' segments '}' '#' : make_binary('$3'). -segments -> segment ',' segments : ['$1' | '$3']. -segments -> segment : ['$1']. +segments -> anno_segment ',' segments : ['$1' | '$3']. +segments -> anno_segment : ['$1']. + +anno_segment -> segment : '$1'. +anno_segment -> '(' segment '-|' annotation ')' : cerl:set_ann('$2', '$4'). segment -> '#' '<' anno_expression '>' '(' anno_expressions ')': case '$6' of @@ -326,7 +357,7 @@ function_name -> atom '/' integer : anno_function_name -> function_name : '$1'. anno_function_name -> '(' function_name '-|' annotation ')' : - core_lib:set_anno('$2', '$4'). + cerl:set_ann('$2', '$4'). let_vars -> anno_variable : ['$1']. let_vars -> '<' '>' : []. @@ -354,7 +385,7 @@ anno_clauses -> anno_clause : ['$1']. anno_clause -> clause : '$1'. anno_clause -> '(' clause '-|' annotation ')' : - core_lib:set_anno('$2', '$4'). + cerl:set_ann('$2', '$4'). clause -> clause_pattern 'when' anno_expression '->' anno_expression : #c_clause{pats='$1',guard='$3',body='$5'}. @@ -410,9 +441,56 @@ Erlang code. -include("core_parse.hrl"). --import(cerl, [c_cons/2,c_tuple/1]). +-import(cerl, [ann_c_map/3,ann_c_map_pattern/2,c_cons/2,c_map/1, + c_map_pattern/1,c_tuple/1]). tok_val(T) -> element(3, T). tok_line(T) -> element(2, T). +%% make_binary([#c_bitstr{}]) -> #c_binary{} | #c_literal{} +%% Create either #c_binary{} or #c_literal{} from the binary segments. +%% In certain contexts, such as keys for maps, only literals and +%% variables are allowed, so we must not create a #c_binary{} +%% record in those situation. +%% +%% To keep this function simple, we use a crude heuristic. We will +%% assume that Core Erlang has been produced by core_pp. If the +%% segments *could* have been output from a literal binary by +%% core_pp, we will create a #c_literal{}. Otherwise we will create a +%% #c_binary{} record. + +make_binary(Segs) -> + try make_lit_bin(<<>>, Segs) of + Bs when is_bitstring(Bs) -> + #c_literal{val=Bs} + catch + throw:impossible -> + #c_binary{segments=Segs} + end. + +make_lit_bin(Acc, [#c_bitstr{val=I0,size=Sz0,unit=U0,type=Type0,flags=F0}|T]) -> + I = get_lit_val(I0), + Sz = get_lit_val(Sz0), + U = get_lit_val(U0), + Type = get_lit_val(Type0), + F = get_lit_val(F0), + if + is_integer(I), U =:= 1, Type =:= integer, F =:= [unsigned,big] -> + ok; + true -> + throw(impossible) + end, + if + Sz =< 8, T =:= [] -> + <<Acc/binary,I:Sz>>; + Sz =:= 8 -> + make_lit_bin(<<Acc/binary,I:8>>, T); + true -> + throw(impossible) + end; +make_lit_bin(Acc, []) -> Acc. + +get_lit_val(#c_literal{val=Val}) -> Val; +get_lit_val(_) -> throw(impossible). + %% vim: syntax=erlang diff --git a/lib/compiler/src/core_pp.erl b/lib/compiler/src/core_pp.erl index 83412ecdd7..67209d06be 100644 --- a/lib/compiler/src/core_pp.erl +++ b/lib/compiler/src/core_pp.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -20,7 +21,7 @@ -module(core_pp). --export([format/1]). +-export([format/1,format_all/1]). -include("core_parse.hrl"). @@ -32,25 +33,35 @@ %% Prettyprint-formats (naively) an abstract Core Erlang syntax %% tree. --record(ctxt, {class = term :: 'clause' | 'def' | 'expr' | 'term', - indent = 0 :: integer(), +-record(ctxt, {indent = 0 :: integer(), item_indent = 2 :: integer(), body_indent = 4 :: integer(), - tab_width = 8 :: non_neg_integer(), - line = 0 :: integer()}). + line = 0 :: integer(), + clean = true :: boolean()}). + +-define(TAB_WIDTH, 8). -spec format(cerl:cerl()) -> iolist(). format(Node) -> format(Node, #ctxt{}). -maybe_anno(Node, Fun, Ctxt) -> - As = core_lib:get_anno(Node), - case get_line(As) of +-spec format_all(cerl:cerl()) -> iolist(). + +format_all(Node) -> + format(Node, #ctxt{clean=false}). + +maybe_anno(Node, Fun, #ctxt{clean=false}=Ctxt) -> + As = cerl:get_ann(Node), + maybe_anno(Node, Fun, Ctxt, As); +maybe_anno(Node, Fun, #ctxt{clean=true}=Ctxt) -> + As0 = cerl:get_ann(Node), + case get_line(As0) of none -> - maybe_anno(Node, Fun, Ctxt, As); + maybe_anno(Node, Fun, Ctxt, As0); Line -> - if Line > Ctxt#ctxt.line -> + As = strip_line(As0), + if Line > Ctxt#ctxt.line -> [io_lib:format("%% Line ~w",[Line]), nl_indent(Ctxt), maybe_anno(Node, Fun, Ctxt#ctxt{line = Line}, As) @@ -60,22 +71,22 @@ maybe_anno(Node, Fun, Ctxt) -> end end. -maybe_anno(Node, Fun, Ctxt, As) -> - case strip_line(As) of - [] -> - Fun(Node, Ctxt); - List -> - Ctxt1 = add_indent(Ctxt, 2), - Ctxt2 = add_indent(Ctxt1, 3), - ["( ", - Fun(Node, Ctxt1), - nl_indent(Ctxt1), - "-| ",format_anno(List, Ctxt2)," )" - ] - end. +maybe_anno(Node, Fun, Ctxt, []) -> + Fun(Node, Ctxt); +maybe_anno(Node, Fun, Ctxt, List) -> + Ctxt1 = add_indent(Ctxt, 2), + Ctxt2 = add_indent(Ctxt1, 3), + ["( ", + Fun(Node, Ctxt1), + nl_indent(Ctxt1), + "-| ",format_anno(List, Ctxt2)," )" + ]. format_anno([_|_]=List, Ctxt) -> [$[,format_anno_list(List, Ctxt),$]]; +format_anno({file,Name}, _Ctxt) -> + %% Optimization: Reduces file size considerably. + io_lib:format("{'file',~p}", [Name]); format_anno(Tuple, Ctxt) when is_tuple(Tuple) -> [${,format_anno_list(tuple_to_list(Tuple), Ctxt),$}]; format_anno(Val, Ctxt) when is_atom(Val) -> @@ -120,14 +131,11 @@ format_1(#c_literal{anno=A,val=Bitstring}, Ctxt) when is_bitstring(Bitstring) -> format_1(#c_binary{anno=A,segments=Segs}, Ctxt); format_1(#c_literal{anno=A,val=M},Ctxt) when is_map(M) -> Pairs = maps:to_list(M), - Op = case Ctxt of - #ctxt{ class = clause } -> exact; - _ -> assoc - end, - Cpairs = [#c_map_pair{op=#c_literal{val=Op}, - key=#c_literal{val=V}, - val=#c_literal{val=K}} || {K,V} <- Pairs], - format_1(#c_map{anno=A,arg=#c_literal{val=#{}},es=Cpairs},Ctxt); + Op = #c_literal{val=assoc}, + Cpairs = [#c_map_pair{op=Op, + key=#c_literal{val=K}, + val=#c_literal{val=V}} || {K,V} <- Pairs], + format_1(#c_map{anno=A,arg=#c_literal{val=#{}},es=Cpairs},Ctxt); format_1(#c_var{name={I,A}}, _) -> [core_atom(I),$/,integer_to_list(A)]; format_1(#c_var{name=V}, _) -> @@ -171,7 +179,8 @@ format_1(#c_tuple{es=Es}, Ctxt) -> format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), $} ]; -format_1(#c_map{arg=#c_literal{val=M},es=Es}, Ctxt) when is_map(M),map_size(M)=:=0 -> +format_1(#c_map{arg=#c_literal{anno=[],val=M},es=Es}, Ctxt) + when is_map(M), map_size(M) =:= 0 -> ["~{", format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2), "}~" @@ -183,15 +192,9 @@ format_1(#c_map{arg=Var,es=Es}, Ctxt) -> "}~" ]; format_1(#c_map_pair{op=#c_literal{val=assoc},key=K,val=V}, Ctxt) -> - ["::<", - format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2), - ">" - ]; + format_map_pair("=>", K, V, Ctxt); format_1(#c_map_pair{op=#c_literal{val=exact},key=K,val=V}, Ctxt) -> - ["~<", - format_hseq([K,V], ",", add_indent(Ctxt, 1), fun format/2), - ">" - ]; + format_map_pair(":=", K, V, Ctxt); format_1(#c_cons{hd=H,tl=T}, Ctxt) -> Txt = ["["|format(H, add_indent(Ctxt, 1))], [Txt|format_list_tail(T, add_indent(Ctxt, width(Txt, Ctxt)))]; @@ -200,9 +203,16 @@ format_1(#c_values{es=Es}, Ctxt) -> format_1(#c_alias{var=V,pat=P}, Ctxt) -> Txt = [format(V, Ctxt)|" = "], [Txt|format(P, add_indent(Ctxt, width(Txt, Ctxt)))]; -format_1(#c_let{vars=Vs0,arg=A,body=B}, Ctxt) -> - Vs = [core_lib:set_anno(V, []) || V <- Vs0], - case is_simple_term(A) of +format_1(#c_let{anno=Anno0,vars=Vs0,arg=A0,body=B}, #ctxt{clean=Clean}=Ctxt) -> + {Vs,A,Anno} = case Clean of + false -> + {Vs0,A0,Anno0}; + true -> + {[cerl:set_ann(V, []) || V <- Vs0], + cerl:set_ann(A0, []), + []} + end, + case is_simple_term(A) andalso Anno =:= [] of false -> Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent), ["let ", @@ -219,7 +229,7 @@ format_1(#c_let{vars=Vs0,arg=A,body=B}, Ctxt) -> ["let ", format_values(Vs, add_indent(Ctxt, 4)), " = ", - format(core_lib:set_anno(A, []), Ctxt1), + format(A, Ctxt1), nl_indent(Ctxt), "in " | format(B, add_indent(Ctxt, 4)) @@ -326,35 +336,30 @@ format_1(#c_module{name=N,exports=Es,attrs=As,defs=Ds}, Ctxt) -> [Mod," [", format_vseq(Es, "", ",", - add_indent(set_class(Ctxt, term), width(Mod, Ctxt)+2), + add_indent(Ctxt, width(Mod, Ctxt)+2), fun format/2), "]", nl_indent(Ctxt), " attributes [", format_vseq(As, "", ",", - add_indent(set_class(Ctxt, def), 16), + add_indent(Ctxt, 16), fun format_def/2), "]", nl_indent(Ctxt), format_funcs(Ds, Ctxt), nl_indent(Ctxt) | "end" - ]; -format_1(Type, _) -> - ["** Unsupported type: ", - io_lib:write(Type) - | " **" ]. format_funcs(Fs, Ctxt) -> format_vseq(Fs, "", "", - set_class(Ctxt, def), + Ctxt, fun format_def/2). format_def({N,V}, Ctxt0) -> - Ctxt1 = add_indent(set_class(Ctxt0, expr), Ctxt0#ctxt.body_indent), + Ctxt1 = add_indent(Ctxt0, Ctxt0#ctxt.body_indent), [format(N, Ctxt0), " =", nl_indent(Ctxt1) @@ -367,7 +372,10 @@ format_values(Vs, Ctxt) -> format_hseq(Vs, ",", add_indent(Ctxt, 1), fun format/2), $>]. -format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) -> +format_bitstr(Node, Ctxt) -> + maybe_anno(Node, fun do_format_bitstr/2, Ctxt). + +do_format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) -> Vs = [S, U, T, Fs], Ctxt1 = add_indent(Ctxt0, 2), Val = format(V, Ctxt1), @@ -375,8 +383,7 @@ format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) -> ["#<", Val, ">(", format_hseq(Vs,",", Ctxt2, fun format/2), $)]. format_clauses(Cs, Ctxt) -> - format_vseq(Cs, "", "", set_class(Ctxt, clause), - fun format_clause/2). + format_vseq(Cs, "", "", Ctxt, fun format_clause/2). format_clause(Node, Ctxt) -> maybe_anno(Node, fun format_clause_1/2, Ctxt). @@ -388,15 +395,13 @@ format_clause_1(#c_clause{pats=Ps,guard=G,body=B}, Ctxt) -> case is_trivial_guard(G) of true -> [" when ", - format_guard(G, add_indent(set_class(Ctxt, expr), - width(Ptxt, Ctxt) + 6))]; + format_guard(G, add_indent(Ctxt, width(Ptxt, Ctxt) + 6))]; false -> [nl_indent(Ctxt2), "when ", format_guard(G, add_indent(Ctxt2, 2))] end++ " ->", - nl_indent(Ctxt2) - | format(B, set_class(Ctxt2, expr)) + nl_indent(Ctxt2) | format(B, Ctxt2) ]. is_trivial_guard(#c_literal{val=Val}) when is_atom(Val) -> true; @@ -448,46 +453,55 @@ format_list_tail(#c_cons{anno=[],hd=H,tl=T}, Ctxt) -> format_list_tail(Tail, Ctxt) -> ["|",format(Tail, add_indent(Ctxt, 1)),"]"]. -indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt). +format_map_pair(Op, K, V, Ctxt0) -> + Ctxt1 = add_indent(Ctxt0, 1), + Txt = format(K, Ctxt1), + Ctxt2 = add_indent(Ctxt0, width(Txt, Ctxt1)), + [Txt,Op,format(V, Ctxt2)]. -indent(N, _) when N =< 0 -> ""; -indent(N, Ctxt) -> - T = Ctxt#ctxt.tab_width, - string:chars($\t, N div T, string:chars($\s, N rem T)). +indent(#ctxt{indent=N}) -> + if + N =< 0 -> + ""; + true -> + string:chars($\t, N div ?TAB_WIDTH, spaces(N rem ?TAB_WIDTH)) + end. nl_indent(Ctxt) -> [$\n|indent(Ctxt)]. +spaces(0) -> ""; +spaces(1) -> " "; +spaces(2) -> " "; +spaces(3) -> " "; +spaces(4) -> " "; +spaces(5) -> " "; +spaces(6) -> " "; +spaces(7) -> " ". +%% Undo indentation done by nl_indent/1. unindent(T, Ctxt) -> - unindent(T, Ctxt#ctxt.indent, Ctxt, []). + unindent(T, Ctxt#ctxt.indent, []). -unindent(T, N, _, C) when N =< 0 -> +unindent(T, N, C) when N =< 0 -> [T|C]; -unindent([$\s|T], N, Ctxt, C) -> - unindent(T, N - 1, Ctxt, C); -unindent([$\t|T], N, Ctxt, C) -> - Tab = Ctxt#ctxt.tab_width, +unindent([$\s|T], N, C) -> + unindent(T, N - 1, C); +unindent([$\t|T], N, C) -> + Tab = ?TAB_WIDTH, if N >= Tab -> - unindent(T, N - Tab, Ctxt, C); + unindent(T, N - Tab, C); true -> - unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C) + unindent([spaces(Tab - N)|T], 0, C) end; -unindent([L|T], N, Ctxt, C) when is_list(L) -> - unindent(L, N, Ctxt, [T|C]); -unindent([H|T], _, _, C) -> - [H|[T|C]]; -unindent([], N, Ctxt, [H|T]) -> - unindent(H, N, Ctxt, T); -unindent([], _, _, []) -> []. +unindent([L|T], N, C) when is_list(L) -> + unindent(L, N, [T|C]). width(Txt, Ctxt) -> - try width(Txt, 0, Ctxt, []) - catch error:_ -> exit({bad_text,Txt}) - end. + width(Txt, 0, Ctxt, []). width([$\t|T], A, Ctxt, C) -> - width(T, A + Ctxt#ctxt.tab_width, Ctxt, C); + width(T, A + ?TAB_WIDTH, Ctxt, C); width([$\n|T], _, Ctxt, C) -> width(unindent([T|C], Ctxt), Ctxt); width([H|T], A, Ctxt, C) when is_list(H) -> @@ -501,14 +515,9 @@ width([], A, _, []) -> A. add_indent(Ctxt, Dx) -> Ctxt#ctxt{indent = Ctxt#ctxt.indent + Dx}. -set_class(Ctxt, Class) -> - Ctxt#ctxt{class = Class}. - core_atom(A) -> io_lib:write_string(atom_to_list(A), $'). -is_simple_term(#c_values{es=Es}) -> - length(Es) < 3 andalso lists:all(fun is_simple_term/1, Es); is_simple_term(#c_tuple{es=Es}) -> length(Es) < 4 andalso lists:all(fun is_simple_term/1, Es); is_simple_term(#c_var{}) -> true; diff --git a/lib/compiler/src/core_scan.erl b/lib/compiler/src/core_scan.erl index b7799b373a..11b52f6c5f 100644 --- a/lib/compiler/src/core_scan.erl +++ b/lib/compiler/src/core_scan.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -271,8 +272,10 @@ scan1("->" ++ Cs, Toks, Pos) -> scan1(Cs, [{'->',Pos}|Toks], Pos); scan1("-|" ++ Cs, Toks, Pos) -> scan1(Cs, [{'-|',Pos}|Toks], Pos); -scan1("::" ++ Cs, Toks, Pos) -> - scan1(Cs, [{'::',Pos}|Toks], Pos); +scan1(":=" ++ Cs, Toks, Pos) -> + scan1(Cs, [{':=',Pos}|Toks], Pos); +scan1("=>" ++ Cs, Toks, Pos) -> + scan1(Cs, [{'=>',Pos}|Toks], Pos); scan1([C|Cs], Toks, Pos) -> %Punctuation character P = list_to_atom([C]), scan1(Cs, [{P,Pos}|Toks], Pos); diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl index 6c75538194..6b2d781a76 100644 --- a/lib/compiler/src/erl_bifs.erl +++ b/lib/compiler/src/erl_bifs.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2013. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -134,6 +135,7 @@ is_pure(math, erf, 1) -> true; is_pure(math, erfc, 1) -> true; is_pure(math, exp, 1) -> true; is_pure(math, log, 1) -> true; +is_pure(math, log2, 1) -> true; is_pure(math, log10, 1) -> true; is_pure(math, pow, 2) -> true; is_pure(math, sin, 1) -> true; diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab index 7d6bf56ccb..dcbdeb32e6 100755 --- a/lib/compiler/src/genop.tab +++ b/lib/compiler/src/genop.tab @@ -1,18 +1,19 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1998-2011. All Rights Reserved. +# Copyright Ericsson AB 1998-2016. All Rights Reserved. # -# The contents of this file are subject to the Erlang Public License, -# Version 1.1, (the "License"); you may not use this file except in -# compliance with the License. You should have received a copy of the -# Erlang Public License along with this software. If not, it can be -# retrieved online at http://www.erlang.org/. +# 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 # -# Software distributed under the License is distributed on an "AS IS" -# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -# the License for the specific language governing rights and limitations -# under the License. +# 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% # @@ -44,7 +45,7 @@ BEAM_FORMAT_NUMBER=0 ## Save the next instruction as the return address in the CP register. 4: call/2 -## @spec call_last Arity Label Dellocate +## @spec call_last Arity Label Deallocate ## @doc Deallocate and do a tail recursive call to the function at Label. ## Do not update the CP register. ## Before the call deallocate Deallocate words of stack. @@ -136,7 +137,7 @@ BEAM_FORMAT_NUMBER=0 # Sending & receiving. # ## @spec send -## @doc Send argument in x(0) as a message to the destination process in x(0). +## @doc Send argument in x(1) as a message to the destination process in x(0). ## The message in x(1) ends up as the result of the send in x(0). 20: send/0 @@ -163,12 +164,12 @@ BEAM_FORMAT_NUMBER=0 25: wait/1 ## @spec wait_timeout Lable Time -## @doc Sets up a timeout of Time milllisecons and saves the address of the +## @doc Sets up a timeout of Time milliseconds and saves the address of the ## following instruction as the entry point if the timeout triggers. 26: wait_timeout/2 # -# Arithmethic opcodes. +# Arithmetic opcodes. # 27: -m_plus/4 28: -m_minus/4 @@ -315,7 +316,7 @@ BEAM_FORMAT_NUMBER=0 66: get_tuple_element/3 ## @spec set_tuple_element NewElement Tuple Position -## @doc Update the element at postition Position of the tuple Tuple +## @doc Update the element at position Position of the tuple Tuple ## with the new element NewElement. 67: set_tuple_element/3 diff --git a/lib/compiler/src/rec_env.erl b/lib/compiler/src/rec_env.erl index 555a331bd7..cdc513e57c 100644 --- a/lib/compiler/src/rec_env.erl +++ b/lib/compiler/src/rec_env.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2014. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -21,8 +22,7 @@ %% @doc Abstract environments, supporting self-referential bindings and %% automatic new-key generation. -%% The current implementation is based on Erlang standard library -%% dictionaries. +%% The current implementation is based on Erlang standard library maps. %%% -define(DEBUG, true). @@ -61,7 +61,7 @@ test_0(Type, N) -> io:fwrite("\ncalls: ~w.\n", [get(new_key_calls)]), io:fwrite("\nretries: ~w.\n", [get(new_key_retries)]), io:fwrite("\nmax: ~w.\n", [get(new_key_max)]), - dict:to_list(element(1,Env)). + maps:to_list(element(1,Env)). test_1(integer = Type, N, Env) when is_integer(N), N > 0 -> Key = new_key(Env), @@ -79,14 +79,13 @@ test_1(_,0, Env) -> %% %% environment() = [Mapping] %% -%% Mapping = {map, Dict} | {rec, Dict, Dict} -%% Dict = dict:dictionary() +%% Mapping = {map, map()} | {rec, map(), map()} %% -%% An empty environment is a list containing a single `{map, Dict}' +%% An empty environment is a list containing a single `{map, map()}' %% element - empty lists are not valid environments. To find a key in an %% environment, it is searched for in each mapping in the list, in %% order, until it the key is found in some mapping, or the end of the -%% list is reached. In a 'rec' mapping, we keep the original dictionary +%% list is reached. In a 'rec' mapping, we keep the original map %% together with a version where entries may have been deleted - this %% makes it possible to garbage collect the entire 'rec' mapping when %% all its entries are unused (for example, by being shadowed by later @@ -96,7 +95,7 @@ test_1(_,0, Env) -> %% ===================================================================== %% @type environment(). An abstract environment. --type mapping() :: {'map', dict:dict()} | {'rec', dict:dict(), dict:dict()}. +-type mapping() :: {'map', map()} | {'rec', map(), map()}. -type environment() :: [mapping(),...]. %% ===================================================================== @@ -107,7 +106,7 @@ test_1(_,0, Env) -> -spec empty() -> environment(). empty() -> - [{map, dict:new()}]. + [{map, #{}}]. %% ===================================================================== @@ -118,14 +117,14 @@ empty() -> -spec is_empty(environment()) -> boolean(). -is_empty([{map, Dict} | Es]) -> - N = dict:size(Dict), +is_empty([{map, Map} | Es]) -> + N = map_size(Map), if N =/= 0 -> false; Es =:= [] -> true; true -> is_empty(Es) end; -is_empty([{rec, Dict, _} | Es]) -> - N = dict:size(Dict), +is_empty([{rec, Map, _} | Es]) -> + N = map_size(Map), if N =/= 0 -> false; Es =:= [] -> true; true -> is_empty(Es) @@ -145,12 +144,12 @@ is_empty([{rec, Dict, _} | Es]) -> size(Env) -> env_size(Env). -env_size([{map, Dict}]) -> - dict:size(Dict); -env_size([{map, Dict} | Env]) -> - dict:size(Dict) + env_size(Env); -env_size([{rec, Dict, _Dict0} | Env]) -> - dict:size(Dict) + env_size(Env). +env_size([{map, Map}]) -> + map_size(Map); +env_size([{map, Map} | Env]) -> + map_size(Map) + env_size(Env); +env_size([{rec, Map, _Map0} | Env]) -> + map_size(Map) + env_size(Env). %% ===================================================================== @@ -164,8 +163,8 @@ env_size([{rec, Dict, _Dict0} | Env]) -> -spec is_defined(term(), environment()) -> boolean(). -is_defined(Key, [{map, Dict} | Env]) -> - case dict:is_key(Key, Dict) of +is_defined(Key, [{map, Map} | Env]) -> + case maps:is_key(Key, Map) of true -> true; false when Env =:= [] -> @@ -173,8 +172,8 @@ is_defined(Key, [{map, Dict} | Env]) -> false -> is_defined(Key, Env) end; -is_defined(Key, [{rec, Dict, _Dict0} | Env]) -> - dict:is_key(Key, Dict) orelse is_defined(Key, Env). +is_defined(Key, [{rec, Map, _Map0} | Env]) -> + maps:is_key(Key, Map) orelse is_defined(Key, Env). %% ===================================================================== @@ -187,12 +186,12 @@ is_defined(Key, [{rec, Dict, _Dict0} | Env]) -> keys(Env) -> lists:sort(keys(Env, [])). -keys([{map, Dict}], S) -> - dict:fetch_keys(Dict) ++ S; -keys([{map, Dict} | Env], S) -> - keys(Env, dict:fetch_keys(Dict) ++ S); -keys([{rec, Dict, _Dict0} | Env], S) -> - keys(Env, dict:fetch_keys(Dict) ++ S). +keys([{map, Map}], S) -> + maps:keys(Map) ++ S; +keys([{map, Map} | Env], S) -> + keys(Env, maps:keys(Map) ++ S); +keys([{rec, Map, _Map0} | Env], S) -> + keys(Env, maps:keys(Map) ++ S). %% ===================================================================== @@ -211,12 +210,12 @@ keys([{rec, Dict, _Dict0} | Env], S) -> to_list(Env) -> lists:sort(to_list(Env, [])). -to_list([{map, Dict}], S) -> - dict:to_list(Dict) ++ S; -to_list([{map, Dict} | Env], S) -> - to_list(Env, dict:to_list(Dict) ++ S); -to_list([{rec, Dict, _Dict0} | Env], S) -> - to_list(Env, dict:to_list(Dict) ++ S). +to_list([{map, Map}], S) -> + maps:to_list(Map) ++ S; +to_list([{map, Map} | Env], S) -> + to_list(Env, maps:to_list(Map) ++ S); +to_list([{rec, Map, _Map0} | Env], S) -> + to_list(Env, maps:to_list(Map) ++ S). %% ===================================================================== @@ -235,12 +234,12 @@ to_list([{rec, Dict, _Dict0} | Env], S) -> -spec bind(term(), term(), environment()) -> environment(). -bind(Key, Value, [{map, Dict}]) -> - [{map, dict:store(Key, Value, Dict)}]; -bind(Key, Value, [{map, Dict} | Env]) -> - [{map, dict:store(Key, Value, Dict)} | delete_any(Key, Env)]; +bind(Key, Value, [{map, Map}]) -> + [{map, maps:put(Key, Value, Map)}]; +bind(Key, Value, [{map, Map} | Env]) -> + [{map, maps:put(Key, Value, Map)} | delete_any(Key, Env)]; bind(Key, Value, Env) -> - [{map, dict:store(Key, Value, dict:new())} | delete_any(Key, Env)]. + [{map, maps:put(Key, Value, #{})} | delete_any(Key, Env)]. %% ===================================================================== @@ -258,17 +257,17 @@ bind(Key, Value, Env) -> -spec bind_list([term()], [term()], environment()) -> environment(). -bind_list(Ks, Vs, [{map, Dict}]) -> - [{map, store_list(Ks, Vs, Dict)}]; -bind_list(Ks, Vs, [{map, Dict} | Env]) -> - [{map, store_list(Ks, Vs, Dict)} | delete_list(Ks, Env)]; +bind_list(Ks, Vs, [{map, Map}]) -> + [{map, store_list(Ks, Vs, Map)}]; +bind_list(Ks, Vs, [{map, Map} | Env]) -> + [{map, store_list(Ks, Vs, Map)} | delete_list(Ks, Env)]; bind_list(Ks, Vs, Env) -> - [{map, store_list(Ks, Vs, dict:new())} | delete_list(Ks, Env)]. + [{map, store_list(Ks, Vs, #{})} | delete_list(Ks, Env)]. -store_list([K | Ks], [V | Vs], Dict) -> - store_list(Ks, Vs, dict:store(K, V, Dict)); -store_list([], _, Dict) -> - Dict. +store_list([K | Ks], [V | Vs], Map) -> + store_list(Ks, Vs, maps:put(K, V, Map)); +store_list([], _, Map) -> + Map. delete_list([K | Ks], Env) -> delete_list(Ks, delete_any(K, Env)); @@ -297,48 +296,40 @@ delete_any(Key, Env) -> -spec delete(term(), environment()) -> environment(). -delete(Key, [{map, Dict} = E | Env]) -> - case dict:is_key(Key, Dict) of - true -> - [{map, dict:erase(Key, Dict)} | Env]; - false -> +delete(Key, [{map, Map} = E | Env]) -> + case maps:take(Key, Map) of + {_, Map1} -> + [{map, Map1} | Env]; + error -> delete_1(Key, Env, E) end; -delete(Key, [{rec, Dict, Dict0} = E | Env]) -> - case dict:is_key(Key, Dict) of - true -> - %% The Dict0 component must be preserved as it is until all - %% keys in Dict have been deleted. - Dict1 = dict:erase(Key, Dict), - case dict:size(Dict1) of - 0 -> - Env; % the whole {rec,...} is now garbage - _ -> - [{rec, Dict1, Dict0} | Env] - end; - false -> +delete(Key, [{rec, Map, Map0} = E | Env]) -> + case maps:take(Key, Map) of + {_, Map1} when map_size(Map1) =:= 0 -> + Env; % the whole {rec,...} is now garbage + %% The Map0 component must be preserved as it is until all + %% keys in Map have been deleted. + {_, Map1} -> + [{rec, Map1, Map0} | Env]; + error -> [E | delete(Key, Env)] end. %% This is just like above, except we pass on the preceding 'map' %% mapping in the list to enable merging when removing 'rec' mappings. -delete_1(Key, [{rec, Dict, Dict0} = E | Env], E1) -> - case dict:is_key(Key, Dict) of - true -> - Dict1 = dict:erase(Key, Dict), - case dict:size(Dict1) of - 0 -> - concat(E1, Env); - _ -> - [E1, {rec, Dict1, Dict0} | Env] - end; - false -> +delete_1(Key, [{rec, Map, Map0} = E | Env], E1) -> + case maps:take(Key, Map) of + {_, Map1} when map_size(Map1) =:= 0 -> + concat(E1, Env); + {_, Map1} -> + [E1, {rec, Map1, Map0} | Env]; + error -> [E1, E | delete(Key, Env)] end. -concat({map, D1}, [{map, D2} | Env]) -> - [dict:merge(fun (_K, V1, _V2) -> V1 end, D1, D2) | Env]; +concat({map, M1}, [{map, M2} | Env]) -> + [maps:merge(M2, M1) | Env]; concat(E1, Env) -> [E1 | Env]. @@ -391,15 +382,15 @@ bind_recursive([], [], _, Env) -> Env; bind_recursive(Ks, Vs, F, Env) -> F1 = fun (V) -> - fun (Dict) -> F(V, [{rec, Dict, Dict} | Env]) end + fun (Map) -> F(V, [{rec, Map, Map} | Env]) end end, - Dict = bind_recursive_1(Ks, Vs, F1, dict:new()), - [{rec, Dict, Dict} | Env]. + Map = bind_recursive_1(Ks, Vs, F1, #{}), + [{rec, Map, Map} | Env]. -bind_recursive_1([K | Ks], [V | Vs], F, Dict) -> - bind_recursive_1(Ks, Vs, F, dict:store(K, F(V), Dict)); -bind_recursive_1([], [], _, Dict) -> - Dict. +bind_recursive_1([K | Ks], [V | Vs], F, Map) -> + bind_recursive_1(Ks, Vs, F, maps:put(K, F(V), Map)); +bind_recursive_1([], [], _, Map) -> + Map. %% ===================================================================== @@ -415,8 +406,8 @@ bind_recursive_1([], [], _, Dict) -> -spec lookup(term(), environment()) -> 'error' | {'ok', term()}. -lookup(Key, [{map, Dict} | Env]) -> - case dict:find(Key, Dict) of +lookup(Key, [{map, Map} | Env]) -> + case maps:find(Key, Map) of {ok, _}=Value -> Value; error when Env =:= [] -> @@ -424,10 +415,10 @@ lookup(Key, [{map, Dict} | Env]) -> error -> lookup(Key, Env) end; -lookup(Key, [{rec, Dict, Dict0} | Env]) -> - case dict:find(Key, Dict) of +lookup(Key, [{rec, Map, Map0} | Env]) -> + case maps:find(Key, Map) of {ok, F} -> - {ok, F(Dict0)}; + {ok, F(Map0)}; error -> lookup(Key, Env) end. @@ -597,7 +588,17 @@ start_range(Env) -> %% (pseudo-)randomly distributed over the range. generate(_N, Range) -> - random:uniform(Range). % works well + %% We must use the same sequence of random variables to ensure + %% that two compilations of the same source code generates the + %% same BEAM code. + case rand:export_seed() of + undefined -> + _ = rand:seed(exsplus, {1,42,2053}), + ok; + _ -> + ok + end, + rand:uniform(Range). % works well %% ===================================================================== diff --git a/lib/compiler/src/sys_core_dsetel.erl b/lib/compiler/src/sys_core_dsetel.erl index 60d83763f8..bd3eeae238 100644 --- a/lib/compiler/src/sys_core_dsetel.erl +++ b/lib/compiler/src/sys_core_dsetel.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -71,7 +72,7 @@ module(M0, _Options) -> {ok,M}. visit_module(#c_module{defs=Ds0}=R) -> - Env = dict:new(), + Env = #{}, Ds = visit_module_1(Ds0, Env, []), R#c_module{defs=Ds}. @@ -94,9 +95,11 @@ visit(Env, #c_var{name={_,_}}=R) -> {R, Env}; visit(Env0, #c_var{name=X}=R) -> %% There should not be any free variables. If there are, - %% the next line will cause an exception. - {ok, N} = dict:find(X, Env0), - {R, dict:store(X, N+1, Env0)}; + %% the case will fail with an exception. + case Env0 of + #{X:=N} -> + {R, Env0#{X:=N+1}} + end; visit(Env, #c_literal{}=R) -> {R, Env}; visit(Env0, #c_tuple{es=Es0}=R) -> @@ -202,7 +205,7 @@ bind_vars(Vs, Env) -> bind_vars(Vs, Env, []). bind_vars([#c_var{name=X}|Vs], Env0, Xs)-> - bind_vars(Vs, dict:store(X, 0, Env0), [X|Xs]); + bind_vars(Vs, Env0#{X=>0}, [X|Xs]); bind_vars([], Env,Xs) -> {Xs, Env}. @@ -216,7 +219,7 @@ visit_pats([], Env, Vs) -> {Vs, Env}. visit_pat(Env0, #c_var{name=V}, Vs) -> - {[V|Vs], dict:store(V, 0, Env0)}; + {[V|Vs], Env0#{V=>0}}; visit_pat(Env0, #c_tuple{es=Es}, Vs) -> visit_pats(Es, Env0, Vs); visit_pat(Env0, #c_map{es=Es}, Vs) -> @@ -234,23 +237,25 @@ visit_pat(Env0, #c_bitstr{val=Val,size=Sz}, Vs0) -> case Sz of #c_var{name=V} -> %% We don't tolerate free variables. - {ok, N} = dict:find(V, Env0), - {Vs0, dict:store(V, N+1, Env0)}; + case Env0 of + #{V:=N} -> + {Vs0, Env0#{V:=N+1}} + end; _ -> visit_pat(Env0, Sz, Vs0) end, visit_pat(Env1, Val, Vs1); visit_pat(Env0, #c_alias{pat=P,var=#c_var{name=V}}, Vs) -> - visit_pat(dict:store(V, 0, Env0), P, [V|Vs]); + visit_pat(Env0#{V=>0}, P, [V|Vs]); visit_pat(Env, #c_literal{}, Vs) -> {Vs, Env}. restore_vars([V|Vs], Env0, Env1) -> - case dict:find(V, Env0) of - {ok, N} -> - restore_vars(Vs, Env0, dict:store(V, N, Env1)); - error -> - restore_vars(Vs, Env0, dict:erase(V, Env1)) + case Env0 of + #{V:=N} -> + restore_vars(Vs, Env0, Env1#{V=>N}); + _ -> + restore_vars(Vs, Env0, maps:remove(V, Env1)) end; restore_vars([], _, Env1) -> Env1. @@ -348,8 +353,8 @@ is_safe(#c_literal{}) -> true; is_safe(_) -> false. is_single_use(V, Env) -> - case dict:find(V, Env) of - {ok, 1} -> + case Env of + #{V:=1} -> true; _ -> false diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index ed8f609082..e0de50f3ae 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -70,7 +71,8 @@ -export([module/2,format_error/1]). -import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,all/2,any/2, - reverse/1,reverse/2,member/2,nth/2,flatten/1,unzip/1]). + reverse/1,reverse/2,member/2,nth/2,flatten/1, + unzip/1,keyfind/3]). -import(cerl, [ann_c_cons/3,ann_c_map/3,ann_c_tuple/2]). @@ -91,10 +93,14 @@ -endif. %% Variable value info. --record(sub, {v=[], %Variable substitutions - s=[], %Variables in scope - t=[], %Types - in_guard=false}). %In guard or not. +-record(sub, {v=[], %Variable substitutions + s=cerl_sets:new() :: cerl_sets:set(), %Variables in scope + t=#{} :: map(), %Types + in_guard=false}). %In guard or not. + +-type type_info() :: cerl:cerl() | 'bool' | 'integer'. +-type yes_no_maybe() :: 'yes' | 'no' | 'maybe'. +-type sub() :: #sub{}. -spec module(cerl:c_module(), [compile:option()]) -> {'ok', cerl:c_module(), [_]}. @@ -271,7 +277,7 @@ expr(#c_fun{}=Fun, effect, _) -> add_warning(Fun, useless_building), void(); expr(#c_fun{vars=Vs0,body=B0}=Fun, Ctxt0, Sub0) -> - {Vs1,Sub1} = pattern_list(Vs0, Sub0), + {Vs1,Sub1} = var_list(Vs0, Sub0), Ctxt = case Ctxt0 of {letrec,Ctxt1} -> Ctxt1; value -> value @@ -293,7 +299,8 @@ expr(#c_seq{arg=Arg0,body=B0}=Seq0, Ctxt, Sub) -> false -> Seq0#c_seq{arg=Arg,body=B1} end end; -expr(#c_let{}=Let, Ctxt, Sub) -> +expr(#c_let{}=Let0, Ctxt, Sub) -> + Let = opt_case_in_let(Let0), case simplify_let(Let, Sub) of impossible -> %% The argument for the let is "simple", i.e. has no @@ -313,7 +320,7 @@ expr(#c_letrec{defs=Fs0,body=B0}=Letrec, Ctxt, Sub) -> Fs1 = map(fun ({Name,Fb}) -> {Name,expr(Fb, {letrec,Ctxt}, Sub)} end, Fs0), - B1 = body(B0, value, Sub), + B1 = body(B0, Ctxt, Sub), Letrec#c_letrec{defs=Fs1,body=B1}; expr(#c_case{}=Case0, Ctxt, Sub) -> %% Ideally, the compiler should only emit warnings when there is @@ -367,10 +374,21 @@ expr(#c_receive{clauses=Cs0,timeout=T0,action=A0}=Recv, Ctxt, Sub) -> T1 = expr(T0, value, Sub), A1 = body(A0, Ctxt, Sub), Recv#c_receive{clauses=Cs1,timeout=T1,action=A1}; -expr(#c_apply{op=Op0,args=As0}=App, _, Sub) -> +expr(#c_apply{anno=Anno,op=Op0,args=As0}=App, _, Sub) -> Op1 = expr(Op0, value, Sub), As1 = expr_list(As0, value, Sub), - App#c_apply{op=Op1,args=As1}; + case Op1 of + #c_var{} -> + App#c_apply{op=Op1,args=As1}; + _ -> + add_warning(App, invalid_call), + Err = #c_call{anno=Anno, + module=#c_literal{val=erlang}, + name=#c_literal{val=error}, + args=[#c_tuple{es=[#c_literal{val='badfun'}, + Op1]}]}, + make_effect_seq(As1++[Err], Sub) + end; expr(#c_call{module=M0,name=N0}=Call0, Ctxt, Sub) -> M1 = expr(M0, value, Sub), N1 = expr(N0, value, Sub), @@ -413,13 +431,13 @@ expr(#c_try{anno=A,arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=Try, _, Sub0) %% Here is the general try/catch construct outside of guards. %% We can remove try if the value is simple and replace it with a let. E1 = body(E0, value, Sub0), - {Vs1,Sub1} = pattern_list(Vs0, Sub0), + {Vs1,Sub1} = var_list(Vs0, Sub0), B1 = body(B0, value, Sub1), case is_safe_simple(E1, Sub0) of true -> expr(#c_let{anno=A,vars=Vs1,arg=E1,body=B1}, value, Sub0); false -> - {Evs1,Sub2} = pattern_list(Evs0, Sub0), + {Evs1,Sub2} = var_list(Evs0, Sub0), H1 = body(H0, value, Sub2), Try#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H1} end. @@ -462,10 +480,7 @@ is_safe_simple(#c_call{module=#c_literal{val=erlang}, case erl_internal:bool_op(Name, NumArgs) of true -> %% Boolean operators are safe if the arguments are boolean. - all(fun(#c_var{name=V}) -> is_boolean_type(V, Sub); - (#c_literal{val=Lit}) -> is_boolean(Lit); - (_) -> false - end, Args); + all(fun(C) -> is_boolean_type(C, Sub) =:= yes end, Args); false -> %% We need a rather complicated test to ensure that %% we only allow safe calls that are allowed in a guard. @@ -607,14 +622,6 @@ eval_binary_1([#c_bitstr{val=#c_literal{val=Val},size=#c_literal{val=Sz}, error:_ -> throw(impossible) end; -eval_binary_1([#c_bitstr{val=#c_literal{},size=#c_literal{}, - unit=#c_literal{},type=#c_literal{}, - flags=#c_cons{}=Flags}=Bitstr|Ss], Acc0) -> - case cerl:fold_literal(Flags) of - #c_literal{} = Flags1 -> - eval_binary_1([Bitstr#c_bitstr{flags=Flags1}|Ss], Acc0); - _ -> throw(impossible) - end; eval_binary_1([], Acc) -> Acc; eval_binary_1(_, _) -> throw(impossible). @@ -688,23 +695,15 @@ count_bits_1(Int, Bits) -> count_bits_1(Int bsr 64, Bits+64). %% a rewritten expression consisting of a sequence of %% the arguments only is returned. -useless_call(effect, #c_call{anno=Anno, - module=#c_literal{val=Mod}, +useless_call(effect, #c_call{module=#c_literal{val=Mod}, name=#c_literal{val=Name}, args=Args}=Call) -> A = length(Args), case erl_bifs:is_safe(Mod, Name, A) of false -> case erl_bifs:is_pure(Mod, Name, A) of - true -> - case member(result_not_wanted, Anno) of - false -> - add_warning(Call, result_ignored); - true -> - ok - end; - false -> - ok + true -> add_warning(Call, result_ignored); + false -> ok end, no; true -> @@ -730,385 +729,23 @@ make_effect_seq([], _) -> void(). call(#c_call{args=As}=Call, #c_literal{val=M}=M0, #c_literal{val=N}=N0, Sub) -> case get(no_inline_list_funcs) of true -> - call_0(Call, M0, N0, As, Sub); + call_1(Call, M0, N0, As, Sub); false -> - call_1(Call, M, N, As, Sub) + case sys_core_fold_lists:call(Call, M, N, As) of + none -> + call_1(Call, M, N, As, Sub); + Core -> + expr(Core, Sub) + end + end; call(#c_call{args=As}=Call, M, N, Sub) -> - call_0(Call, M, N, As, Sub). + call_1(Call, M, N, As, Sub). -call_0(Call, M, N, As0, Sub) -> +call_1(Call, M, N, As0, Sub) -> As1 = expr_list(As0, value, Sub), fold_call(Call#c_call{args=As1}, M, N, As1, Sub). -%% We inline some very common higher order list operations. -%% We use the same evaluation order as the library function. - -call_1(#c_call{anno=Anno}, lists, all, [Arg1,Arg2], Sub) -> - Loop = #c_var{name={'lists^all',1}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]}, - CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true}, - body=#c_apply{anno=Anno, op=Loop, args=[Xs]}}, - CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true}, - body=#c_literal{val=false}}, - CC3 = #c_clause{pats=[X], guard=#c_literal{val=true}, - body=match_fail(Anno, Err1)}, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, - body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]}, - clauses = [CC1, CC2, CC3]}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=1}]}, - body=#c_literal{val=true}}, - Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^all',1}}|Anno], Err2)}, - Fun = #c_fun{vars=[Xs], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, - body=#c_letrec{defs=[{Loop,Fun}], - body=#c_apply{anno=Anno, op=Loop, args=[L]}}}, - Sub); -call_1(#c_call{anno=Anno}, lists, any, [Arg1,Arg2], Sub) -> - Loop = #c_var{name={'lists^any',1}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]}, - CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true}, - body=#c_literal{val=true}}, - CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true}, - body=#c_apply{anno=Anno, op=Loop, args=[Xs]}}, - CC3 = #c_clause{pats=[X], guard=#c_literal{val=true}, - body=match_fail(Anno, Err1)}, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, - body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]}, - clauses = [CC1, CC2, CC3]}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=1}]}, - body=#c_literal{val=false}}, - Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^any',1}}|Anno], Err2)}, - Fun = #c_fun{vars=[Xs], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, - body=#c_letrec{defs=[{Loop,Fun}], - body=#c_apply{anno=Anno, op=Loop, args=[L]}}}, - Sub); -call_1(#c_call{anno=Anno}, lists, foreach, [Arg1,Arg2], Sub) -> - Loop = #c_var{name={'lists^foreach',1}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, - body=#c_seq{arg=#c_apply{anno=Anno, op=F, args=[X]}, - body=#c_apply{anno=Anno, op=Loop, args=[Xs]}}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=1}]}, - body=#c_literal{val=ok}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^foreach',1}}|Anno], Err)}, - Fun = #c_fun{vars=[Xs], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, - body=#c_letrec{defs=[{Loop,Fun}], - body=#c_apply{anno=Anno, op=Loop, args=[L]}}}, - Sub); -call_1(#c_call{anno=Anno}, lists, map, [Arg1,Arg2], Sub) -> - Loop = #c_var{name={'lists^map',1}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - H = #c_var{name='H'}, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, - body=#c_let{vars=[H], arg=#c_apply{anno=Anno, - op=F, - args=[X]}, - body=#c_cons{hd=H, - anno=[compiler_generated], - tl=#c_apply{anno=Anno, - op=Loop, - args=[Xs]}}}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=1}]}, - body=#c_literal{val=[]}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^map',1}}|Anno], Err)}, - Fun = #c_fun{vars=[Xs], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, - body=#c_letrec{defs=[{Loop,Fun}], - body=#c_apply{anno=Anno, op=Loop, args=[L]}}}, - Sub); -call_1(#c_call{anno=Anno}, lists, flatmap, [Arg1,Arg2], Sub) -> - Loop = #c_var{name={'lists^flatmap',1}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - H = #c_var{name='H'}, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, - body=#c_let{vars=[H], - arg=#c_apply{anno=Anno, op=F, args=[X]}, - body=#c_call{anno=[compiler_generated|Anno], - module=#c_literal{val=erlang}, - name=#c_literal{val='++'}, - args=[H, - #c_apply{anno=Anno, - op=Loop, - args=[Xs]}]}}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=1}]}, - body=#c_literal{val=[]}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^flatmap',1}}|Anno], Err)}, - Fun = #c_fun{vars=[Xs], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, - body=#c_letrec{defs=[{Loop,Fun}], - body=#c_apply{anno=Anno, op=Loop, args=[L]}}}, - Sub); -call_1(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2], Sub) -> - Loop = #c_var{name={'lists^filter',1}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - B = #c_var{name='B'}, - Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]}, - CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true}, - body=#c_cons{anno=[compiler_generated], hd=X, tl=Xs}}, - CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true}, - body=Xs}, - CC3 = #c_clause{pats=[X], guard=#c_literal{val=true}, - body=match_fail(Anno, Err1)}, - Case = #c_case{arg=B, clauses = [CC1, CC2, CC3]}, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, - body=#c_let{vars=[B], - arg=#c_apply{anno=Anno, op=F, args=[X]}, - body=#c_let{vars=[Xs], - arg=#c_apply{anno=Anno, - op=Loop, - args=[Xs]}, - body=Case}}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=1}]}, - body=#c_literal{val=[]}}, - Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^filter',1}}|Anno], Err2)}, - Fun = #c_fun{vars=[Xs], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, - body=#c_letrec{defs=[{Loop,Fun}], - body=#c_apply{anno=Anno, op=Loop, args=[L]}}}, - Sub); -call_1(#c_call{anno=Anno}, lists, foldl, [Arg1,Arg2,Arg3], Sub) -> - Loop = #c_var{name={'lists^foldl',2}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - A = #c_var{name='A'}, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, - body=#c_apply{anno=Anno, - op=Loop, - args=[Xs, #c_apply{anno=Anno, - op=F, - args=[X, A]}]}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=2}]}, - body=A}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^foldl',2}}|Anno], Err)}, - Fun = #c_fun{vars=[Xs, A], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, A, L], arg=#c_values{es=[Arg1, Arg2, Arg3]}, - body=#c_letrec{defs=[{Loop,Fun}], - body=#c_apply{anno=Anno, op=Loop, args=[L, A]}}}, - Sub); -call_1(#c_call{anno=Anno}, lists, foldr, [Arg1,Arg2,Arg3], Sub) -> - Loop = #c_var{name={'lists^foldr',2}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - A = #c_var{name='A'}, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, - body=#c_apply{anno=Anno, - op=F, - args=[X, #c_apply{anno=Anno, - op=Loop, - args=[Xs, A]}]}}, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=2}]}, - body=A}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^foldr',2}}|Anno], Err)}, - Fun = #c_fun{vars=[Xs, A], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, A, L], arg=#c_values{es=[Arg1, Arg2, Arg3]}, - body=#c_letrec{defs=[{Loop,Fun}], - body=#c_apply{anno=Anno, op=Loop, args=[L, A]}}}, - Sub); -call_1(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3], Sub) -> - Loop = #c_var{name={'lists^mapfoldl',2}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - Avar = #c_var{name='A'}, - Match = - fun (A, P, E) -> - C1 = #c_clause{pats=[P], guard=#c_literal{val=true}, body=E}, - Err = #c_tuple{es=[#c_literal{val='badmatch'}, X]}, - C2 = #c_clause{pats=[X], guard=#c_literal{val=true}, - body=match_fail(Anno, Err)}, - #c_case{arg=A, clauses=[C1, C2]} - end, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, - body=Match(#c_apply{anno=Anno, op=F, args=[X, Avar]}, - #c_tuple{es=[X, Avar]}, -%%% Tuple passing version - Match(#c_apply{anno=Anno, - op=Loop, - args=[Xs, Avar]}, - #c_tuple{es=[Xs, Avar]}, - #c_tuple{anno=[compiler_generated], - es=[#c_cons{anno=[compiler_generated], - hd=X, tl=Xs}, - Avar]}) -%%% Multiple-value version -%%% #c_let{vars=[Xs,A], -%%% %% The tuple here will be optimised -%%% %% away later; no worries. -%%% arg=#c_apply{op=Loop, args=[Xs, A]}, -%%% body=#c_values{es=[#c_cons{hd=X, tl=Xs}, -%%% A]}} - )}, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=2}]}, -%%% Tuple passing version - body=#c_tuple{anno=[compiler_generated], - es=[#c_literal{val=[]}, Avar]}}, -%%% Multiple-value version -%%% body=#c_values{es=[#c_literal{val=[]}, A]}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^mapfoldl',2}}|Anno], Err)}, - Fun = #c_fun{vars=[Xs, Avar], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, Avar, L], arg=#c_values{es=[Arg1, Arg2, Arg3]}, - body=#c_letrec{defs=[{Loop,Fun}], -%%% Tuple passing version - body=#c_apply{anno=Anno, - op=Loop, - args=[L, Avar]}}}, -%%% Multiple-value version -%%% body=#c_let{vars=[Xs, A], -%%% arg=#c_apply{op=Loop, -%%% args=[L, A]}, -%%% body=#c_tuple{es=[Xs, A]}}}}, - Sub); -call_1(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) -> - Loop = #c_var{name={'lists^mapfoldr',2}}, - F = #c_var{name='F'}, - Xs = #c_var{name='Xs'}, - X = #c_var{name='X'}, - Avar = #c_var{name='A'}, - Match = - fun (A, P, E) -> - C1 = #c_clause{pats=[P], guard=#c_literal{val=true}, body=E}, - Err = #c_tuple{es=[#c_literal{val='badmatch'}, X]}, - C2 = #c_clause{pats=[X], guard=#c_literal{val=true}, - body=match_fail(Anno, Err)}, - #c_case{arg=A, clauses=[C1, C2]} - end, - C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, -%%% Tuple passing version - body=Match(#c_apply{anno=Anno, - op=Loop, - args=[Xs, Avar]}, - #c_tuple{es=[Xs, Avar]}, - Match(#c_apply{anno=Anno, op=F, args=[X, Avar]}, - #c_tuple{es=[X, Avar]}, - #c_tuple{anno=[compiler_generated], - es=[#c_cons{anno=[compiler_generated], - hd=X, tl=Xs}, Avar]})) -%%% Multiple-value version -%%% body=#c_let{vars=[Xs,A], -%%% %% The tuple will be optimised away -%%% arg=#c_apply{op=Loop, args=[Xs, A]}, -%%% body=Match(#c_apply{op=F, args=[X, A]}, -%%% #c_tuple{es=[X, A]}, -%%% #c_values{es=[#c_cons{hd=X, tl=Xs}, -%%% A]})} - }, - C2 = #c_clause{pats=[#c_literal{val=[]}], - guard=#c_call{module=#c_literal{val=erlang}, - name=#c_literal{val=is_function}, - args=[F, #c_literal{val=2}]}, -%%% Tuple passing version - body=#c_tuple{anno=[compiler_generated], - es=[#c_literal{val=[]}, Avar]}}, -%%% Multiple-value version -%%% body=#c_values{es=[#c_literal{val=[]}, A]}}, - Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]}, - C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, - body=match_fail([{function_name,{'lists^mapfoldr',2}}|Anno], Err)}, - Fun = #c_fun{vars=[Xs, Avar], - body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, - L = #c_var{name='L'}, - expr(#c_let{vars=[F, Avar, L], arg=#c_values{es=[Arg1, Arg2, Arg3]}, - body=#c_letrec{defs=[{Loop,Fun}], -%%% Tuple passing version - body=#c_apply{anno=Anno, - op=Loop, - args=[L, Avar]}}}, -%%% Multiple-value version -%%% body=#c_let{vars=[Xs, A], -%%% arg=#c_apply{op=Loop, -%%% args=[L, A]}, -%%% body=#c_tuple{es=[Xs, A]}}}}, - Sub); -call_1(#c_call{module=M, name=N}=Call, _, _, As, Sub) -> - call_0(Call, M, N, As, Sub). - -match_fail(Anno, Arg) -> - #c_primop{anno=Anno, - name=#c_literal{val='match_fail'}, - args=[Arg]}. - %% fold_call(Call, Mod, Name, Args, Sub) -> Expr. %% Try to safely evaluate the call. Just try to evaluate arguments, %% do the call and convert return values to literals. If this @@ -1133,29 +770,33 @@ fold_call_1(Call, Mod, Name, Args, Sub) -> true -> fold_call_2(Call, Mod, Name, Args, Sub) end. -fold_call_2(Call, Module, Name, Args0, Sub) -> - try - Args = [core_lib:literal_value(A) || A <- Args0], - try apply(Module, Name, Args) of - Val -> - case cerl:is_literal_term(Val) of - true -> - #c_literal{val=Val}; - false -> - %% Successful evaluation, but it was not - %% possible to express the computed value as a literal. - Call - end - catch - error:Reason -> - %% Evaluation of the function failed. Warn and replace - %% the call with a call to erlang:error/1. - eval_failure(Call, Reason) - end +fold_call_2(Call, Module, Name, Args, Sub) -> + case all(fun cerl:is_literal/1, Args) of + true -> + %% All arguments are literals. + fold_lit_args(Call, Module, Name, Args); + false -> + %% At least one non-literal argument. + fold_non_lit_args(Call, Module, Name, Args, Sub) + end. + +fold_lit_args(Call, Module, Name, Args0) -> + Args = [cerl:concrete(A) || A <- Args0], + try apply(Module, Name, Args) of + Val -> + case cerl:is_literal_term(Val) of + true -> + cerl:ann_abstract(cerl:get_ann(Call), Val); + false -> + %% Successful evaluation, but it was not possible + %% to express the computed value as a literal. + Call + end catch - error:_ -> - %% There was at least one non-literal argument. - fold_non_lit_args(Call, Module, Name, Args0, Sub) + error:Reason -> + %% Evaluation of the function failed. Warn and replace + %% the call with a call to erlang:error/1. + eval_failure(Call, Reason) end. %% fold_non_lit_args(Call, Module, Name, Args, Sub) -> Expr. @@ -1194,36 +835,43 @@ fold_non_lit_args(Call, _, _, _, _) -> Call. %% Evaluate a relational operation using type information. eval_rel_op(Call, Op, [#c_var{name=V},#c_var{name=V}], _) -> Bool = erlang:Op(same, same), - #c_literal{anno=core_lib:get_anno(Call),val=Bool}; -eval_rel_op(Call, '=:=', [#c_var{name=V}=Var,#c_literal{val=true}], Sub) -> + #c_literal{anno=cerl:get_ann(Call),val=Bool}; +eval_rel_op(Call, '=:=', [Term,#c_literal{val=true}], Sub) -> %% BoolVar =:= true ==> BoolVar - case is_boolean_type(V, Sub) of - true -> Var; - false -> Call + case is_boolean_type(Term, Sub) of + yes -> Term; + maybe -> Call; + no -> #c_literal{val=false} end; -eval_rel_op(Call, '==', Ops, _Sub) -> - case is_exact_eq_ok(Ops) of +eval_rel_op(Call, '==', Ops, Sub) -> + case is_exact_eq_ok(Ops, Sub) of true -> - Name = #c_literal{anno=core_lib:get_anno(Call),val='=:='}, + Name = #c_literal{anno=cerl:get_ann(Call),val='=:='}, Call#c_call{name=Name}; false -> Call end; -eval_rel_op(Call, '/=', Ops, _Sub) -> - case is_exact_eq_ok(Ops) of +eval_rel_op(Call, '/=', Ops, Sub) -> + case is_exact_eq_ok(Ops, Sub) of true -> - Name = #c_literal{anno=core_lib:get_anno(Call),val='=/='}, + Name = #c_literal{anno=cerl:get_ann(Call),val='=/='}, Call#c_call{name=Name}; false -> Call end; eval_rel_op(Call, _, _, _) -> Call. -is_exact_eq_ok([#c_literal{val=Lit}|_]) -> +is_exact_eq_ok([A,B]=L, Sub) -> + case is_int_type(A, Sub) =:= yes andalso is_int_type(B, Sub) =:= yes of + true -> true; + false -> is_exact_eq_ok_1(L) + end. + +is_exact_eq_ok_1([#c_literal{val=Lit}|_]) -> is_non_numeric(Lit); -is_exact_eq_ok([_|T]) -> - is_exact_eq_ok(T); -is_exact_eq_ok([]) -> false. +is_exact_eq_ok_1([_|T]) -> + is_exact_eq_ok_1(T); +is_exact_eq_ok_1([]) -> false. is_non_numeric([H|T]) -> is_non_numeric(H) andalso is_non_numeric(T); @@ -1247,40 +895,31 @@ is_non_numeric_tuple(_Tuple, 0) -> true. %% there must be at least one non-literal argument (i.e. %% there is no need to handle the case that all argments %% are literal). -eval_bool_op(Call, 'and', [#c_literal{val=true},#c_var{name=V}=Res], Sub) -> - case is_boolean_type(V, Sub) of - true -> Res; - false-> Call - end; -eval_bool_op(Call, 'and', [#c_var{name=V}=Res,#c_literal{val=true}], Sub) -> - case is_boolean_type(V, Sub) of - true -> Res; - false-> Call - end; -eval_bool_op(Call, 'and', [#c_literal{val=false}=Res,#c_var{name=V}], Sub) -> - case is_boolean_type(V, Sub) of - true -> Res; - false-> Call - end; -eval_bool_op(Call, 'and', [#c_var{name=V},#c_literal{val=false}=Res], Sub) -> - case is_boolean_type(V, Sub) of - true -> Res; - false-> Call - end; + +eval_bool_op(Call, 'and', [#c_literal{val=true},Term], Sub) -> + eval_bool_op_1(Call, Term, Term, Sub); +eval_bool_op(Call, 'and', [Term,#c_literal{val=true}], Sub) -> + eval_bool_op_1(Call, Term, Term, Sub); +eval_bool_op(Call, 'and', [#c_literal{val=false}=Res,Term], Sub) -> + eval_bool_op_1(Call, Res, Term, Sub); +eval_bool_op(Call, 'and', [Term,#c_literal{val=false}=Res], Sub) -> + eval_bool_op_1(Call, Res, Term, Sub); eval_bool_op(Call, _, _, _) -> Call. +eval_bool_op_1(Call, Res, Term, Sub) -> + case is_boolean_type(Term, Sub) of + yes -> Res; + no -> eval_failure(Call, badarg); + maybe -> Call + end. + %% Evaluate is_boolean/1 using type information. -eval_is_boolean(Call, #c_var{name=V}, Sub) -> - case is_boolean_type(V, Sub) of - true -> #c_literal{val=true}; - false -> Call - end; -eval_is_boolean(_, #c_cons{}, _) -> - #c_literal{val=false}; -eval_is_boolean(_, #c_tuple{}, _) -> - #c_literal{val=false}; -eval_is_boolean(Call, _, _) -> - Call. +eval_is_boolean(Call, Term, Sub) -> + case is_boolean_type(Term, Sub) of + no -> #c_literal{val=false}; + yes -> #c_literal{val=true}; + maybe -> Call + end. %% eval_length(Call, List) -> Val. %% Evaluates the length for the prefix of List which has a known @@ -1330,36 +969,33 @@ eval_append(Call, X, Y) -> %% Evaluates element/2 if the position Pos is a literal and %% the shape of the tuple Tuple is known. %% -eval_element(Call, #c_literal{val=Pos}, #c_tuple{es=Es}, _Types) when is_integer(Pos) -> - if - 1 =< Pos, Pos =< length(Es) -> - lists:nth(Pos, Es); - true -> - eval_failure(Call, badarg) - end; -eval_element(Call, #c_literal{val=Pos}, #c_var{name=V}, Types) +eval_element(Call, #c_literal{val=Pos}, Tuple, Types) when is_integer(Pos) -> - case orddict:find(V, Types#sub.t) of - {ok,#c_tuple{es=Elements}} -> + case get_type(Tuple, Types) of + none -> + Call; + Type -> + Es = case cerl:is_c_tuple(Type) of + false -> []; + true -> cerl:tuple_es(Type) + end, if - 1 =< Pos, Pos =< length(Elements) -> - El = lists:nth(Pos, Elements), + 1 =< Pos, Pos =< length(Es) -> + El = lists:nth(Pos, Es), try - pat_to_expr(El) + cerl:set_ann(pat_to_expr(El), [compiler_generated]) catch throw:impossible -> Call end; true -> + %% Index outside tuple or not a tuple. eval_failure(Call, badarg) - end; - {ok,_} -> - eval_failure(Call, badarg); - error -> - Call + end end; -eval_element(Call, Pos, Tuple, _Types) -> - case is_not_integer(Pos) orelse is_not_tuple(Tuple) of +eval_element(Call, Pos, Tuple, Sub) -> + case is_int_type(Pos, Sub) =:= no orelse + is_tuple_type(Tuple, Sub) =:= no of true -> eval_failure(Call, badarg); false -> @@ -1369,58 +1005,55 @@ eval_element(Call, Pos, Tuple, _Types) -> %% eval_is_record(Call, Var, Tag, Size, Types) -> Val. %% Evaluates is_record/3 using type information. %% -eval_is_record(Call, #c_var{name=V}, #c_literal{val=NeededTag}=Lit, +eval_is_record(Call, Term, #c_literal{val=NeededTag}, #c_literal{val=Size}, Types) -> - case orddict:find(V, Types#sub.t) of - {ok,#c_tuple{es=[#c_literal{val=Tag}|_]=Es}} -> - Lit#c_literal{val=Tag =:= NeededTag andalso - length(Es) =:= Size}; - _ -> - Call + case get_type(Term, Types) of + none -> + Call; + Type -> + Es = case cerl:is_c_tuple(Type) of + false -> []; + true -> cerl:tuple_es(Type) + end, + case Es of + [#c_literal{val=Tag}|_] -> + Bool = Tag =:= NeededTag andalso + length(Es) =:= Size, + #c_literal{val=Bool}; + _ -> + #c_literal{val=false} + end end; eval_is_record(Call, _, _, _, _) -> Call. -%% is_not_integer(Core) -> true | false. -%% Returns true if Core is definitely not an integer. - -is_not_integer(#c_literal{val=Val}) when not is_integer(Val) -> true; -is_not_integer(#c_tuple{}) -> true; -is_not_integer(#c_cons{}) -> true; -is_not_integer(#c_map{}) -> true; -is_not_integer(_) -> false. - -%% is_not_tuple(Core) -> true | false. -%% Returns true if Core is definitely not a tuple. - -is_not_tuple(#c_literal{val=Val}) when not is_tuple(Val) -> true; -is_not_tuple(#c_cons{}) -> true; -is_not_tuple(#c_map{}) -> true; -is_not_tuple(_) -> false. - %% eval_setelement(Call, Pos, Tuple, NewVal) -> Core. %% Evaluates setelement/3 if position Pos is an integer -%% the shape of the tuple Tuple is known. +%% and the shape of the tuple Tuple is known. %% -eval_setelement(Call, Pos, Tuple, NewVal) -> - try - eval_setelement_1(Pos, Tuple, NewVal) - catch - error:_ -> - Call - end. - -eval_setelement_1(#c_literal{val=Pos}, #c_tuple{anno=A,es=Es}, NewVal) - when is_integer(Pos) -> - ann_c_tuple(A, eval_setelement_2(Pos, Es, NewVal)); -eval_setelement_1(#c_literal{val=Pos}, #c_literal{anno=A,val=Es0}, NewVal) +eval_setelement(Call, #c_literal{val=Pos}, Tuple, NewVal) when is_integer(Pos) -> - Es = [#c_literal{anno=A,val=E} || E <- tuple_to_list(Es0)], - ann_c_tuple(A, eval_setelement_2(Pos, Es, NewVal)). + case cerl:is_data(Tuple) of + false -> + Call; + true -> + Es0 = case cerl:is_c_tuple(Tuple) of + false -> []; + true -> cerl:tuple_es(Tuple) + end, + if + 1 =< Pos, Pos =< length(Es0) -> + Es = eval_setelement_1(Pos, Es0, NewVal), + cerl:update_c_tuple(Tuple, Es); + true -> + eval_failure(Call, badarg) + end + end; +eval_setelement(Call, _, _, _) -> Call. -eval_setelement_2(1, [_|T], NewVal) -> +eval_setelement_1(1, [_|T], NewVal) -> [NewVal|T]; -eval_setelement_2(Pos, [H|T], NewVal) when Pos > 1 -> - [H|eval_setelement_2(Pos-1, T, NewVal)]. +eval_setelement_1(Pos, [H|T], NewVal) when Pos > 1 -> + [H|eval_setelement_1(Pos-1, T, NewVal)]. %% eval_failure(Call, Reason) -> Core. %% Warn for a call that will fail and replace the call with @@ -1456,15 +1089,28 @@ is_atom_or_var(_) -> false. %% clause(Clause, Cepxr, Context, Sub) -> Clause. -clause(#c_clause{pats=Ps0,guard=G0,body=B0}=Cl, Cexpr, Ctxt, Sub0) -> - {Ps1,Sub1} = pattern_list(Ps0, Sub0), +clause(#c_clause{pats=Ps0}=Cl, Cexpr, Ctxt, Sub0) -> + try pattern_list(Ps0, Sub0) of + {Ps1,Sub1} -> + clause_1(Cl, Ps1, Cexpr, Ctxt, Sub1) + catch + nomatch -> + Cl#c_clause{anno=[compiler_generated], + guard=#c_literal{val=false}} + end. + +clause_1(#c_clause{guard=G0,body=B0}=Cl, Ps1, Cexpr, Ctxt, Sub1) -> Sub2 = update_types(Cexpr, Ps1, Sub1), - GSub = case {Cexpr,Ps1} of - {#c_var{name='_'},_} -> + GSub = case {Cexpr,Ps1,G0} of + {_,_,#c_literal{}} -> + %% No need for substitution tricks when the guard + %% does not contain any variables. + Sub2; + {#c_var{name='_'},_,_} -> %% In a 'receive', Cexpr is the variable '_', which represents the %% message being matched. We must NOT do any extra substiutions. Sub2; - {#c_var{},[#c_var{}=Var]} -> + {#c_var{},[#c_var{}=Var],_} -> %% The idea here is to optimize expressions such as %% %% case A of A -> ... @@ -1498,22 +1144,34 @@ clause(#c_clause{pats=Ps0,guard=G0,body=B0}=Cl, Cexpr, Ctxt, Sub0) -> %% the unsubstituted variables and values. let_substs(Vs0, As0, Sub0) -> - {Vs1,Sub1} = pattern_list(Vs0, Sub0), + {Vs1,Sub1} = var_list(Vs0, Sub0), {Vs2,As1,Ss} = let_substs_1(Vs1, As0, Sub1), - Sub2 = scope_add([V || #c_var{name=V} <- Vs2], Sub1), + Sub2 = sub_add_scope([V || #c_var{name=V} <- Vs2], Sub1), {Vs2,As1, - foldl(fun ({V,S}, Sub) -> sub_set_name(V, S, Sub) end, Sub2, Ss)}. + foldl(fun ({V,S}, Sub) -> sub_set_name(V, S, Sub) end, Sub2, Ss)}. let_substs_1(Vs, #c_values{es=As}, Sub) -> let_subst_list(Vs, As, Sub); let_substs_1([V], A, Sub) -> let_subst_list([V], [A], Sub); let_substs_1(Vs, A, _) -> {Vs,A,[]}. -let_subst_list([V|Vs0], [A|As0], Sub) -> +let_subst_list([V|Vs0], [A0|As0], Sub) -> {Vs1,As1,Ss} = let_subst_list(Vs0, As0, Sub), - case is_subst(A) of - true -> {Vs1,As1,sub_subst_var(V, A, Sub) ++ Ss}; - false -> {[V|Vs1],[A|As1],Ss} + case is_subst(A0) of + true -> + A = case is_compiler_generated(V) andalso + not is_compiler_generated(A0) of + true -> + %% Propagate the 'compiler_generated' annotation + %% along with the value. + Ann = [compiler_generated|cerl:get_ann(A0)], + cerl:set_ann(A0, Ann); + false -> + A0 + end, + {Vs1,As1,sub_subst_var(V, A, Sub) ++ Ss}; + false -> + {[V|Vs1],[A0|As1],Ss} end; let_subst_list([], [], _) -> {[],[],[]}. @@ -1535,7 +1193,7 @@ pattern(#c_var{}=Pat, Isub, Osub) -> true -> V1 = make_var_name(), Pat1 = #c_var{name=V1}, - {Pat1,sub_set_var(Pat, Pat1, scope_add([V1], Osub))}; + {Pat1,sub_set_var(Pat, Pat1, sub_add_scope([V1], Osub))}; false -> {Pat,sub_del_var(Pat, Osub)} end; @@ -1572,20 +1230,132 @@ bin_pattern_list(Ps0, Isub, Osub0) -> {Ps,{_,Osub}} = mapfoldl(fun bin_pattern/2, {Isub,Osub0}, Ps0), {Ps,Osub}. -bin_pattern(#c_bitstr{val=E0,size=Size0}=Pat, {Isub0,Osub0}) -> +bin_pattern(#c_bitstr{val=E0,size=Size0}=Pat0, {Isub0,Osub0}) -> Size1 = expr(Size0, Isub0), {E1,Osub} = pattern(E0, Isub0, Osub0), Isub = case E0 of #c_var{} -> sub_set_var(E0, E1, Isub0); _ -> Isub0 end, - {Pat#c_bitstr{val=E1,size=Size1},{Isub,Osub}}. + Pat = Pat0#c_bitstr{val=E1,size=Size1}, + bin_pat_warn(Pat), + {Pat,{Isub,Osub}}. pattern_list(Ps, Sub) -> pattern_list(Ps, Sub, Sub). pattern_list(Ps0, Isub, Osub0) -> mapfoldl(fun (P, Osub) -> pattern(P, Isub, Osub) end, Osub0, Ps0). +%% var_list([Var], InSub) -> {Pattern,OutSub}. +%% Works like pattern_list/2 but only accept variables and is +%% guaranteed not to throw an exception. + +var_list(Vs, Sub0) -> + mapfoldl(fun (#c_var{}=V, Sub) -> + pattern(V, Sub, Sub) + end, Sub0, Vs). + + +%%% +%%% Generate warnings for binary patterns that will not match. +%%% + +bin_pat_warn(#c_bitstr{type=#c_literal{val=Type}, + val=Val0, + size=#c_literal{val=Sz}, + unit=#c_literal{val=Unit}, + flags=Fl}=Pat) -> + case {Type,Sz} of + {_,_} when is_integer(Sz), Sz >= 0 -> ok; + {binary,all} -> ok; + {utf8,undefined} -> ok; + {utf16,undefined} -> ok; + {utf32,undefined} -> ok; + {_,_} -> + add_warning(Pat, {nomatch_bit_syntax_size,Sz}), + throw(nomatch) + end, + case {Type,Val0} of + {integer,#c_literal{val=Val}} when is_integer(Val) -> + Signedness = signedness(Fl), + TotalSz = Sz * Unit, + bit_pat_warn_int(Val, TotalSz, Signedness, Pat); + {float,#c_literal{val=Val}} when is_float(Val) -> + ok; + {utf8,#c_literal{val=Val}} when is_integer(Val) -> + bit_pat_warn_unicode(Val, Pat); + {utf16,#c_literal{val=Val}} when is_integer(Val) -> + bit_pat_warn_unicode(Val, Pat); + {utf32,#c_literal{val=Val}} when is_integer(Val) -> + bit_pat_warn_unicode(Val, Pat); + {_,#c_literal{val=Val}} -> + add_warning(Pat, {nomatch_bit_syntax_type,Val,Type}), + throw(nomatch); + {_,_} -> + ok + end; +bin_pat_warn(#c_bitstr{type=#c_literal{val=Type},val=Val0,flags=Fl}=Pat) -> + %% Size is variable. Not much that we can check. + case {Type,Val0} of + {integer,#c_literal{val=Val}} when is_integer(Val) -> + case signedness(Fl) of + unsigned when Val < 0 -> + add_warning(Pat, {nomatch_bit_syntax_unsigned,Val}), + throw(nomatch); + _ -> + ok + end; + {float,#c_literal{val=Val}} when is_float(Val) -> + ok; + {_,#c_literal{val=Val}} -> + add_warning(Pat, {nomatch_bit_syntax_type,Val,Type}), + throw(nomatch); + {_,_} -> + ok + end. + +bit_pat_warn_int(Val, 0, signed, Pat) -> + if + Val =:= 0 -> + ok; + true -> + add_warning(Pat, {nomatch_bit_syntax_truncated,signed,Val,0}), + throw(nomatch) + end; +bit_pat_warn_int(Val, Sz, signed, Pat) -> + if + Val < 0, Val bsr (Sz - 1) =/= -1 -> + add_warning(Pat, {nomatch_bit_syntax_truncated,signed,Val,Sz}), + throw(nomatch); + Val > 0, Val bsr (Sz - 1) =/= 0 -> + add_warning(Pat, {nomatch_bit_syntax_truncated,signed,Val,Sz}), + throw(nomatch); + true -> + ok + end; +bit_pat_warn_int(Val, _Sz, unsigned, Pat) when Val < 0 -> + add_warning(Pat, {nomatch_bit_syntax_unsigned,Val}), + throw(nomatch); +bit_pat_warn_int(Val, Sz, unsigned, Pat) -> + if + Val bsr Sz =:= 0 -> + ok; + true -> + add_warning(Pat, {nomatch_bit_syntax_truncated,unsigned,Val,Sz}), + throw(nomatch) + end. + +bit_pat_warn_unicode(U, _Pat) when 0 =< U, U =< 16#10FFFF -> + ok; +bit_pat_warn_unicode(U, Pat) -> + add_warning(Pat, {nomatch_bit_syntax_unicode,U}), + throw(nomatch). + +signedness(#c_literal{val=Flags}) -> + [S] = [F || F <- Flags, F =:= signed orelse F =:= unsigned], + S. + + %% is_subst(Expr) -> true | false. %% Test whether an expression is a suitable substitution. @@ -1605,6 +1375,7 @@ is_subst(_) -> false. %% sub_del_var(Var, #sub{}) -> #sub{}. %% sub_subst_var(Var, Value, #sub{}) -> [{Name,Value}]. %% sub_is_val(Var, #sub{}) -> boolean(). +%% sub_add_scope(#sub{}) -> #sub{} %% sub_subst_scope(#sub{}) -> #sub{} %% %% We use the variable name as key so as not have problems with @@ -1615,14 +1386,15 @@ is_subst(_) -> false. %% In addition to the list of substitutions, we also keep track of %% all variable currently live (the scope). %% -%% sub_subst_scope/1 adds dummy substitutions for all variables -%% in the scope in order to force renaming if variables in the -%% scope occurs as pattern variables. +%% sub_add_scope/2 adds variables to the scope. sub_subst_scope/1 +%% adds dummy substitutions for all variables in the scope in order +%% to force renaming if variables in the scope occurs as pattern +%% variables. -sub_new() -> #sub{v=orddict:new(),s=gb_trees:empty(),t=[]}. +sub_new() -> #sub{v=orddict:new(),s=cerl_sets:new(),t=#{}}. sub_new(#sub{}=Sub) -> - Sub#sub{v=orddict:new(),t=[]}. + Sub#sub{v=orddict:new(),t=#{}}. sub_new_preserve_types(#sub{}=Sub) -> Sub#sub{v=orddict:new()}. @@ -1639,16 +1411,16 @@ sub_set_var(#c_var{name=V}, Val, Sub) -> sub_set_name(V, Val, #sub{v=S,s=Scope,t=Tdb0}=Sub) -> Tdb1 = kill_types(V, Tdb0), Tdb = copy_type(V, Val, Tdb1), - Sub#sub{v=orddict:store(V, Val, S),s=gb_sets:add(V, Scope),t=Tdb}. + Sub#sub{v=orddict:store(V, Val, S),s=cerl_sets:add_element(V, Scope),t=Tdb}. sub_del_var(#c_var{name=V}, #sub{v=S,s=Scope,t=Tdb}=Sub) -> %% Profiling shows that for programs with many record operations, %% sub_del_var/2 is a bottleneck. Since the scope contains all %% variables that are live, we know that V cannot be present in S %% if it is not in the scope. - case gb_sets:is_member(V, Scope) of + case cerl_sets:is_element(V, Scope) of false -> - Sub#sub{s=gb_sets:insert(V, Scope)}; + Sub#sub{s=cerl_sets:add_element(V, Scope)}; true -> Sub#sub{v=orddict:erase(V, S),t=kill_types(V, Tdb)} end. @@ -1657,8 +1429,14 @@ sub_subst_var(#c_var{name=V}, Val, #sub{v=S0}) -> %% Fold chained substitutions. [{V,Val}] ++ [ {K,Val} || {K,#c_var{name=V1}} <- S0, V1 =:= V]. +sub_add_scope(Vs, #sub{s=Scope0}=Sub) -> + Scope = foldl(fun(V, S) when is_integer(V); is_atom(V) -> + cerl_sets:add_element(V, S) + end, Scope0, Vs), + Sub#sub{s=Scope}. + sub_subst_scope(#sub{v=S0,s=Scope}=Sub) -> - S = [{-1,#c_var{name=Sv}} || Sv <- gb_sets:to_list(Scope)]++S0, + S = [{-1,#c_var{name=Sv}} || Sv <- cerl_sets:to_list(Scope)]++S0, Sub#sub{v=S}. sub_is_val(#c_var{name=V}, #sub{v=S,s=Scope}) -> @@ -1666,7 +1444,7 @@ sub_is_val(#c_var{name=V}, #sub{v=S,s=Scope}) -> %% became the new bottleneck. Since the scope contains all %% live variables, a variable V can only be the target for %% a substitution if it is in the scope. - gb_sets:is_member(V, Scope) andalso v_is_value(V, S). + cerl_sets:is_element(V, Scope) andalso v_is_value(V, S). v_is_value(Var, [{_,#c_var{name=Var}}|_]) -> true; v_is_value(Var, [_|T]) -> v_is_value(Var, T); @@ -1704,7 +1482,7 @@ clauses(E, [C0|Cs], Ctxt, Sub, LitExpr) -> {yes,yes} -> case LitExpr of false -> - Line = get_line(core_lib:get_anno(C1)), + Line = get_line(cerl:get_ann(C1)), shadow_warning(Cs, Line); true -> %% If the case expression is a literal, @@ -1938,7 +1716,7 @@ opt_bool_case_guard(#c_case{arg=Arg,clauses=Cs0}=Case) -> Case; true -> Cs = opt_bool_case_guard(Arg, Cs0), - Case#c_case{arg=#c_values{anno=core_lib:get_anno(Arg),es=[]}, + Case#c_case{arg=#c_values{anno=cerl:get_ann(Arg),es=[]}, clauses=Cs} end. @@ -1986,6 +1764,7 @@ eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0, %% is correct, the clause will always match at run-time. Case; {true,Bs} -> + eval_case_warn(B), {Ps,As} = unzip(Bs), InnerLet = cerl:c_let(Ps, core_lib:make_values(As), B), Let = cerl:c_let(Vs, E, InnerLet), @@ -1993,6 +1772,18 @@ eval_case(#c_case{arg=E,clauses=[#c_clause{pats=Ps0, end; eval_case(Case, _) -> Case. +eval_case_warn(#c_primop{anno=Anno, + name=#c_literal{val=match_fail}, + args=[_]}=Core) -> + case keyfind(eval_failure, 1, Anno) of + false -> + ok; + {eval_failure,Reason} -> + %% Example: M = not_map, M#{k:=v} + add_warning(Core, {eval_failure,Reason}) + end; +eval_case_warn(_) -> ok. + %% case_opt(CaseArg, [Clause]) -> {CaseArg,[Clause]}. %% Try and optimise a case by avoid building tuples or lists %% in the case expression. Instead combine the variable parts @@ -2048,12 +1839,31 @@ case_opt_args([], Cs, _Sub, _LitExpr, Acc) -> %% Try to expand one argument to several arguments (if tuple/list) %% or to remove a literal argument. %% -case_opt_arg(E0, Sub, Cs0, LitExpr) -> - E = maybe_replace_var(E0, Sub), - case cerl:is_data(E) of +case_opt_arg(E0, Sub, Cs, LitExpr) -> + case cerl:is_c_var(E0) of + false -> + case_opt_arg_1(E0, Cs, LitExpr); + true -> + case case_will_var_match(Cs) of + true -> + %% All clauses will match a variable in the + %% current position. Don't expand this variable + %% (that can only make the code worse). + {error,Cs}; + false -> + %% If possible, expand this variable to a previously + %% matched term. + E = case_expand_var(E0, Sub), + case_opt_arg_1(E, Cs, LitExpr) + end + end. + +case_opt_arg_1(E0, Cs0, LitExpr) -> + case cerl:is_data(E0) of false -> {error,Cs0}; true -> + E = case_opt_compiler_generated(E0), Cs = case_opt_nomatch(E, Cs0, LitExpr), case cerl:data_type(E) of {atomic,_} -> @@ -2063,20 +1873,45 @@ case_opt_arg(E0, Sub, Cs0, LitExpr) -> end end. -%% maybe_replace_var(Expr0, Sub) -> Expr +%% case_will_var_match([Clause]) -> true | false. +%% Return if all clauses will match a variable in the +%% current position. +%% +case_will_var_match(Cs) -> + all(fun({[P|_],_,_,_}) -> + case cerl_clauses:match(P, any) of + {true,_} -> true; + _ -> false + end + end, Cs). + + +%% case_opt_compiler_generated(Core) -> Core' +%% Mark Core expressions as compiler generated to ensure that +%% no warnings are generated if they turn out to be unused. +%% To pretty-printed Core Erlang easier to read, don't mark +%% constructs that can't cause warnings to be emitted. +%% +case_opt_compiler_generated(Core) -> + F = fun(C) -> + case cerl:type(C) of + alias -> C; + var -> C; + _ -> cerl:set_ann(C, [compiler_generated]) + end + end, + cerl_trees:map(F, Core). + + +%% case_expand_var(Expr0, Sub) -> Expr %% If Expr0 is a variable that has been previously matched and %% is known to be a tuple, return the tuple instead. Otherwise %% return Expr0 unchanged. %% -maybe_replace_var(E, Sub) -> - case cerl:is_c_var(E) of - false -> E; - true -> maybe_replace_var_1(E, Sub) - end. - -maybe_replace_var_1(E, #sub{t=Tdb}) -> - case orddict:find(cerl:var_name(E), Tdb) of - {ok,T0} -> +case_expand_var(E, #sub{t=Tdb}) -> + Key = cerl:var_name(E), + case Tdb of + #{Key:=T0} -> case cerl:is_c_tuple(T0) of false -> E; @@ -2091,9 +1926,8 @@ maybe_replace_var_1(E, #sub{t=Tdb}) -> %% operator will fail when used in map %% construction (only the '=>' operator is allowed %% when constructing a map from scratch). - ToData = fun coerce_to_data/1, try - cerl_trees:map(ToData, T0) + cerl_trees:map(fun coerce_to_data/1, T0) catch throw:impossible -> %% Something unsuitable was found (map or @@ -2101,7 +1935,7 @@ maybe_replace_var_1(E, #sub{t=Tdb}) -> E end end; - error -> + _ -> E end. @@ -2147,8 +1981,9 @@ case_opt_nomatch(_, [], _) -> []. %% will match, and we can remove the corresponding pattern from %% each clause. %% -%% The only complication is if the literal is a binary. Binary -%% pattern matching is tricky, so we will give up in that case. +%% The only complication is if the literal is a binary or map. +%% In general, it is difficult to know whether a binary or +%% map pattern will match, so we give up in that case. case_opt_lit(Lit, Cs0) -> try case_opt_lit_1(Lit, Cs0) of @@ -2175,6 +2010,10 @@ case_opt_lit_1(E, [{[P|Ps],C,PsAcc,Bs0}|Cs]) -> case_opt_lit_1(_, []) -> []. %% case_opt_data(Expr, Clauses0, LitExpr) -> {ok,Exprs,Clauses} +%% The case expression is a non-atomic data constructor (cons +%% or tuple). We can know at compile time whether each clause +%% will match, and we can delay the building of the data to +%% the clauses where it is actually needed. case_opt_data(E, Cs0) -> Es = cerl:data_es(E), @@ -2184,45 +2023,48 @@ case_opt_data(E, Cs0) -> {ok,Es,Cs} catch throw:impossible -> + %% The pattern contained a binary or map. {error,Cs0} end. -case_opt_data_1([{[P|Ps0],C,PsAcc,Bs0}|Cs], Es, TypeSig) -> - {ok,Ps1,Bs1} = case_data_pat(P, TypeSig), - [{Ps1++Ps0,C,PsAcc,Bs1++Bs0}| - case_opt_data_1(Cs, Es, TypeSig)]; +case_opt_data_1([{[P0|Ps0],C,PsAcc,Bs0}|Cs], Es, TypeSig) -> + P = case_opt_compiler_generated(P0), + BindTo = #c_var{name=dummy}, + {Ps1,[{BindTo,_}|Bs1]} = case_data_pat_alias(P, BindTo, TypeSig, []), + [{Ps1++Ps0,C,PsAcc,Bs1++Bs0}|case_opt_data_1(Cs, Es, TypeSig)]; case_opt_data_1([], _, _) -> []. -%% case_data_pat(Pattern, Type, Arity) -> {ok,[Pattern],[{AliasVar,Pat}]} | error. - -case_data_pat(P, TypeSig) -> - case cerl:is_data(P) of - false -> - case_data_pat_var(P, TypeSig); - true -> - {ok,cerl:data_es(P),[]} - end. - -%% case_data_pat_var(Pattern, {DataType,ArityType}) -> -%% {ok,[Pattern],[{AliasVar,Pat}]} - -case_data_pat_var(P, {Type,Arity}=TypeSig) -> - %% If the entire case statement is evaluated in an effect - %% context (e.g. "case {A,B} of ... end, ok"), there will - %% be a warning that a term is constructed but never used. - %% To avoid that warning, we must annotate the data - %% constructor as compiler generated. - Ann = [compiler_generated|cerl:get_ann(P)], +case_data_pat_alias(P, BindTo0, TypeSig, Bs0) -> case cerl:type(P) of - var -> - Vars = make_vars(cerl:get_ann(P), Arity), - {ok,Vars,[{P,cerl:ann_make_data(Ann, Type, Vars)}]}; alias -> - V = cerl:alias_var(P), - Apat = cerl:alias_pat(P), - {ok,Ps,Bs} = case_data_pat(Apat, TypeSig), - {ok,Ps,[{V,cerl:ann_make_data(Ann, Type, - pat_to_expr_list(Ps))}|Bs]} + %% Recursively handle the pattern and bind to + %% the alias variable. + BindTo = cerl:alias_var(P), + Apat0 = cerl:alias_pat(P), + Ann = [compiler_generated], + Apat = cerl:set_ann(Apat0, Ann), + {Ps,Bs} = case_data_pat_alias(Apat, BindTo, TypeSig, Bs0), + {Ps,[{BindTo0,BindTo}|Bs]}; + var -> + %% Here we will need to actually build the data and bind + %% it to the variable. + {Type,Arity} = TypeSig, + Ann = [compiler_generated], + Vars = make_vars(Ann, Arity), + Data = cerl:ann_make_data(Ann, Type, Vars), + Bs = [{BindTo0,P},{P,Data}|Bs0], + {Vars,Bs}; + _ -> + %% Since case_opt_nomatch/3 has removed all clauses that + %% cannot match, we KNOW that this clause must match and + %% that the pattern must be a data constructor. + %% Here we must build the data and bind it to the variable. + {Type,_} = TypeSig, + DataEs = cerl:data_es(P), + Vars = pat_to_expr_list(DataEs), + Ann = [compiler_generated], + Data = cerl:ann_make_data(Ann, Type, Vars), + {DataEs,[{BindTo0,Data}]} end. %% pat_to_expr(Pattern) -> Expression. @@ -2269,58 +2111,128 @@ make_var_name() -> list_to_atom("fol"++integer_to_list(N)). letify(Bs, Body) -> + Ann = cerl:get_ann(Body), foldr(fun({V,Val}, B) -> - letify(V, Val, B) + cerl:ann_c_let(Ann, [V], Val, B) end, Body, Bs). -letify(#c_var{name=Vname}=Var, Val, Body) -> - case core_lib:is_var_used(Vname, Body) of - true -> - A = element(2, Body), - #c_let{anno=A,vars=[Var],arg=Val,body=Body}; - false -> Body - end. - -%% opt_case_in_let(LetExpr) -> LetExpr' +%% opt_not_in_let(Let) -> Cerl +%% Try to optimize away a 'not' operator in a 'let'. -opt_case_in_let(#c_let{vars=Vs,arg=Arg,body=B}=Let, Sub) -> - opt_case_in_let_0(Vs, Arg, B, Let, Sub). +-spec opt_not_in_let(cerl:c_let()) -> cerl:cerl(). -opt_case_in_let_0([#c_var{name=V}], Arg, - #c_case{arg=#c_var{name=V},clauses=Cs}=Case, Let, Sub) -> - case opt_case_in_let_1(V, Arg, Cs) of - impossible -> - case is_simple_case_arg(Arg) andalso - not core_lib:is_var_used(V, Case#c_case{arg=#c_literal{val=nil}}) of - true -> - expr(opt_bool_case(Case#c_case{arg=Arg,clauses=Cs}), sub_new(Sub)); - false -> - Let +opt_not_in_let(#c_let{vars=[_]=Vs0,arg=Arg0,body=Body0}=Let) -> + case opt_not_in_let(Vs0, Arg0, Body0) of + {[],#c_values{es=[]},Body} -> + Body; + {Vs,Arg,Body} -> + Let#c_let{vars=Vs,arg=Arg,body=Body} + end; +opt_not_in_let(Let) -> Let. + +%% opt_not_in_let(Vs, Arg, Body) -> {Vs',Arg',Body'} +%% Try to optimize away a 'not' operator in a 'let'. + +-spec opt_not_in_let([cerl:c_var()], cerl:cerl(), cerl:cerl()) -> + {[cerl:c_var()],cerl:cerl(),cerl:cerl()}. + +opt_not_in_let([#c_var{name=V}]=Vs0, Arg0, Body0) -> + case cerl:type(Body0) of + call -> + %% let <V> = Expr in not V ==> + %% let <> = <> in notExpr + case opt_not_in_let_1(V, Body0, Arg0) of + no -> + {Vs0,Arg0,Body0}; + {yes,Body} -> + {[],#c_values{es=[]},Body} + end; + 'let' -> + %% let <V> = Expr in let <Var> = not V in Body ==> + %% let <Var> = notExpr in Body + %% V must not be used in Body. + LetArg = cerl:let_arg(Body0), + case opt_not_in_let_1(V, LetArg, Arg0) of + no -> + {Vs0,Arg0,Body0}; + {yes,Arg} -> + LetBody = cerl:let_body(Body0), + case core_lib:is_var_used(V, LetBody) of + true -> + {Vs0,Arg0,Body0}; + false -> + LetVars = cerl:let_vars(Body0), + {LetVars,Arg,LetBody} + end end; - Expr -> Expr + _ -> + {Vs0,Arg0,Body0} end; -opt_case_in_let_0(_, _, _, Let, _) -> Let. +opt_not_in_let(Vs, Arg, Body) -> + {Vs,Arg,Body}. -opt_case_in_let_1(V, Arg, Cs) -> - try - opt_case_in_let_2(V, Arg, Cs) - catch - _:_ -> impossible +opt_not_in_let_1(V, Call, Body) -> + case Call of + #c_call{module=#c_literal{val=erlang}, + name=#c_literal{val='not'}, + args=[#c_var{name=V}]} -> + opt_not_in_let_2(Body, Call); + _ -> + no end. -opt_case_in_let_2(V, Arg0, - [#c_clause{pats=[#c_tuple{es=Es}], - guard=#c_literal{val=true},body=B}|_]) -> - - %% In {V1,V2,...} = case E of P -> ... {Val1,Val2,...}; ... end. - %% avoid building tuples, by converting tuples to multiple values. - %% (The optimisation is not done if the built tuple is used or returned.) - - true = all(fun (#c_var{}) -> true; - (_) -> false end, Es), %Only variables in tuple - false = core_lib:is_var_used(V, B), %Built tuple must not be used. - Arg1 = tuple_to_values(Arg0, length(Es)), %Might fail. - #c_let{vars=Es,arg=Arg1,body=B}. +opt_not_in_let_2(#c_case{clauses=Cs0}=Case, NotCall) -> + Vars = make_vars([], 1), + Body = NotCall#c_call{args=Vars}, + Cs = [begin + Let = #c_let{vars=Vars,arg=B,body=Body}, + C#c_clause{body=opt_not_in_let(Let)} + end || #c_clause{body=B}=C <- Cs0], + {yes,Case#c_case{clauses=Cs}}; +opt_not_in_let_2(#c_call{}=Call0, _NotCall) -> + invert_call(Call0); +opt_not_in_let_2(_, _) -> no. + +invert_call(#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=Name0}, + args=[_,_]}=Call) -> + case inverse_rel_op(Name0) of + no -> no; + Name -> {yes,Call#c_call{name=#c_literal{val=Name}}} + end; +invert_call(#c_call{}) -> no. + +%% inverse_rel_op(Op) -> no | RevOp + +inverse_rel_op('=:=') -> '=/='; +inverse_rel_op('=/=') -> '=:='; +inverse_rel_op('==') -> '/='; +inverse_rel_op('/=') -> '=='; +inverse_rel_op('>') -> '=<'; +inverse_rel_op('<') -> '>='; +inverse_rel_op('>=') -> '<'; +inverse_rel_op('=<') -> '>'; +inverse_rel_op(_) -> no. + + +%% opt_bool_case_in_let(LetExpr, Sub) -> Core + +opt_bool_case_in_let(#c_let{vars=Vs,arg=Arg,body=B}=Let, Sub) -> + opt_case_in_let_1(Vs, Arg, B, Let, Sub). + +opt_case_in_let_1([#c_var{name=V}], Arg, + #c_case{arg=#c_var{name=V}}=Case0, Let, Sub) -> + case is_simple_case_arg(Arg) of + true -> + Case = opt_bool_case(Case0#c_case{arg=Arg}), + case core_lib:is_var_used(V, Case) of + false -> expr(Case, sub_new(Sub)); + true -> Let + end; + false -> + Let + end; +opt_case_in_let_1(_, _, _, Let, _) -> Let. %% is_simple_case_arg(Expr) -> true|false %% Determine whether the Expr is simple enough to be worth @@ -2362,18 +2274,15 @@ is_bool_expr(#c_clause{body=B}, Sub) -> is_bool_expr(B, Sub); is_bool_expr(#c_let{vars=[V],arg=Arg,body=B}, Sub0) -> Sub = case is_bool_expr(Arg, Sub0) of - true -> update_types(V, [#c_literal{val=true}], Sub0); + true -> update_types(V, [bool], Sub0); false -> Sub0 end, is_bool_expr(B, Sub); is_bool_expr(#c_let{body=B}, Sub) -> %% Binding of multiple variables. is_bool_expr(B, Sub); -is_bool_expr(#c_literal{val=Bool}, _) when is_boolean(Bool) -> - true; -is_bool_expr(#c_var{name=V}, Sub) -> - is_boolean_type(V, Sub); -is_bool_expr(_, _) -> false. +is_bool_expr(C, Sub) -> + is_boolean_type(C, Sub) =:= yes. is_bool_expr_list([C|Cs], Sub) -> is_bool_expr(C, Sub) andalso is_bool_expr_list(Cs, Sub); @@ -2386,7 +2295,7 @@ is_bool_expr_list([], _) -> true. %% functions, or is_record/2). %% is_safe_bool_expr(Core, Sub) -> - is_safe_bool_expr_1(Core, Sub, gb_sets:empty()). + is_safe_bool_expr_1(Core, Sub, cerl_sets:new()). is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang}, name=#c_literal{val=is_record}, @@ -2432,7 +2341,7 @@ is_safe_bool_expr_1(#c_let{vars=Vars,arg=Arg,body=B}, Sub, BoolVars) -> true -> case {is_safe_bool_expr_1(Arg, Sub, BoolVars),Vars} of {true,[#c_var{name=V}]} -> - is_safe_bool_expr_1(B, Sub, gb_sets:add(V, BoolVars)); + is_safe_bool_expr_1(B, Sub, cerl_sets:add_element(V, BoolVars)); {false,_} -> is_safe_bool_expr_1(B, Sub, BoolVars) end; @@ -2441,7 +2350,7 @@ is_safe_bool_expr_1(#c_let{vars=Vars,arg=Arg,body=B}, Sub, BoolVars) -> is_safe_bool_expr_1(#c_literal{val=Val}, _Sub, _) -> is_boolean(Val); is_safe_bool_expr_1(#c_var{name=V}, _Sub, BoolVars) -> - gb_sets:is_element(V, BoolVars); + cerl_sets:is_element(V, BoolVars); is_safe_bool_expr_1(_, _, _) -> false. is_safe_bool_expr_list([C|Cs], Sub, BoolVars) -> @@ -2451,38 +2360,6 @@ is_safe_bool_expr_list([C|Cs], Sub, BoolVars) -> end; is_safe_bool_expr_list([], _, _) -> true. -%% tuple_to_values(Expr, TupleArity) -> Expr' -%% Convert tuples in return position of arity TupleArity to values. -%% Throws an exception for constructs that are not handled. - -tuple_to_values(#c_tuple{es=Es}, Arity) when length(Es) =:= Arity -> - core_lib:make_values(Es); -tuple_to_values(#c_literal{val=Tuple}=Lit, Arity) when tuple_size(Tuple) =:= Arity -> - Es = [Lit#c_literal{val=E} || E <- tuple_to_list(Tuple)], - core_lib:make_values(Es); -tuple_to_values(#c_case{clauses=Cs0}=Case, Arity) -> - Cs1 = [tuple_to_values(E, Arity) || E <- Cs0], - Case#c_case{clauses=Cs1}; -tuple_to_values(#c_seq{body=B0}=Seq, Arity) -> - Seq#c_seq{body=tuple_to_values(B0, Arity)}; -tuple_to_values(#c_let{body=B0}=Let, Arity) -> - Let#c_let{body=tuple_to_values(B0, Arity)}; -tuple_to_values(#c_receive{clauses=Cs0,timeout=Timeout,action=A0}=Rec, Arity) -> - Cs = [tuple_to_values(E, Arity) || E <- Cs0], - A = case Timeout of - #c_literal{val=infinity} -> A0; - _ -> tuple_to_values(A0, Arity) - end, - Rec#c_receive{clauses=Cs,action=A}; -tuple_to_values(#c_clause{body=B0}=Clause, Arity) -> - B = tuple_to_values(B0, Arity), - Clause#c_clause{body=B}; -tuple_to_values(Expr, _) -> - case will_fail(Expr) of - true -> Expr; - false -> erlang:error({not_handled,Expr}) - end. - %% simplify_let(Let, Sub) -> Expr | impossible %% If the argument part of an let contains a complex expression, such %% as a let or a sequence, move the original let body into the complex @@ -2507,12 +2384,12 @@ move_let_into_expr(#c_let{vars=InnerVs0,body=InnerBody0}=Inner, %% in <InnerBody> %% Arg = body(Arg0, Sub0), - ScopeSub0 = sub_subst_scope(Sub0#sub{t=[]}), - {OuterVs,ScopeSub} = pattern_list(OuterVs0, ScopeSub0), - + ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}), + {OuterVs,ScopeSub} = var_list(OuterVs0, ScopeSub0), + OuterBody = body(OuterBody0, ScopeSub), - {InnerVs,Sub} = pattern_list(InnerVs0, Sub0), + {InnerVs,Sub} = var_list(InnerVs0, Sub0), InnerBody = body(InnerBody0, Sub), Outer#c_let{vars=OuterVs,arg=Arg, body=Inner#c_let{vars=InnerVs,arg=OuterBody,body=InnerBody}}; @@ -2528,39 +2405,49 @@ move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let, case {TwoClauses,is_failing_clause(Ca0),is_failing_clause(Cb0)} of {true,false,true} -> %% let <Lvars> = case <Case-expr> of - %% <Cvars> -> <Clause-body>; - %% <OtherCvars> -> erlang:error(...) + %% <Cpats> -> <Clause-body>; + %% <OtherCpats> -> erlang:error(...) %% end %% in <Let-body> %% %% ==> %% %% case <Case-expr> of - %% <Cvars> -> + %% <Cpats> -> %% let <Lvars> = <Clause-body> %% in <Let-body>; - %% <OtherCvars> -> erlang:error(...) + %% <OtherCpats> -> erlang:error(...) %% end Cexpr = body(Cexpr0, Sub0), - CaVars0 = Ca0#c_clause.pats, + CaPats0 = Ca0#c_clause.pats, G0 = Ca0#c_clause.guard, B0 = Ca0#c_clause.body, - ScopeSub0 = sub_subst_scope(Sub0#sub{t=[]}), - {CaVars,ScopeSub} = pattern_list(CaVars0, ScopeSub0), - G = guard(G0, ScopeSub), - - B1 = body(B0, ScopeSub), - - {Lvs,B2,Sub1} = let_substs(Lvs0, B1, Sub0), - Sub2 = Sub1#sub{s=gb_sets:union(ScopeSub#sub.s, - Sub1#sub.s)}, - Lbody = body(Lbody0, Sub2), - B = Let#c_let{vars=Lvs,arg=core_lib:make_values(B2),body=Lbody}, - - Ca = Ca0#c_clause{pats=CaVars,guard=G,body=B}, - Cb = clause(Cb0, Cexpr, value, Sub0), - Case#c_case{arg=Cexpr,clauses=[Ca,Cb]}; + ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}), + try pattern_list(CaPats0, ScopeSub0) of + {CaPats,ScopeSub} -> + G = guard(G0, ScopeSub), + + B1 = body(B0, ScopeSub), + + {Lvs,B2,Sub1} = let_substs(Lvs0, B1, Sub0), + Sub2 = Sub1#sub{s=cerl_sets:union(ScopeSub#sub.s, + Sub1#sub.s)}, + Lbody = body(Lbody0, Sub2), + B = Let#c_let{vars=Lvs, + arg=core_lib:make_values(B2), + body=Lbody}, + + Ca = Ca0#c_clause{pats=CaPats,guard=G,body=B}, + Cb = clause(Cb0, Cexpr, value, Sub0), + Case#c_case{arg=Cexpr,clauses=[Ca,Cb]} + catch + nomatch -> + %% This is not a defeat. The code will eventually + %% be optimized to erlang:error(...) by the other + %% optimizations done in this module. + impossible + end; {_,_,_} -> impossible end; move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let, @@ -2587,88 +2474,257 @@ move_let_into_expr(_Let, _Expr, _Sub) -> impossible. is_failing_clause(#c_clause{body=B}) -> will_fail(B). -scope_add(Vs, #sub{s=Scope0}=Sub) -> - Scope = foldl(fun(V, S) when is_integer(V); is_atom(V) -> - gb_sets:add(V, S) - end, Scope0, Vs), - Sub#sub{s=Scope}. +%% opt_case_in_let(Let) -> Let' +%% Try to avoid building tuples that are immediately matched. +%% A common pattern is: +%% +%% {V1,V2,...} = case E of P -> ... {Val1,Val2,...}; ... end +%% +%% In Core Erlang the pattern would look like this: +%% +%% let <V> = case E of +%% ... -> ... {Val1,Val2} +%% ... +%% end, +%% in case V of +%% {A,B} -> ... <use A and B> ... +%% end +%% +%% Rewrite this to: +%% +%% let <V1,V2> = case E of +%% ... -> ... <Val1,Val2> +%% ... +%% end, +%% in +%% let <V> = {V1,V2} +%% in case V of +%% {A,B} -> ... <use A and B> ... +%% end +%% +%% Note that the second 'case' is unchanged. The other optimizations +%% in this module will eliminate the building of the tuple and +%% rewrite the second case to: +%% +%% case <V1,V2> of +%% <A,B> -> ... <use A and B> ... +%% end +%% + +opt_case_in_let(#c_let{vars=Vs,arg=Arg0,body=B}=Let0) -> + case matches_data(Vs, B) of + {yes,TypeSig} -> + case delay_build(Arg0, TypeSig) of + no -> + Let0; + {yes,Vars,Arg,Data} -> + InnerLet = Let0#c_let{arg=Data}, + Let0#c_let{vars=Vars,arg=Arg,body=InnerLet} + end; + no -> + Let0 + end. + +matches_data([#c_var{name=V}], #c_case{arg=#c_var{name=V}, + clauses=[#c_clause{pats=[P]}|_]}) -> + case cerl:is_data(P) of + false -> + no; + true -> + case cerl:data_type(P) of + {atomic,_} -> + no; + Type -> + {yes,{Type,cerl:data_arity(P)}} + end + end; +matches_data(_, _) -> no. + +delay_build(Core, TypeSig) -> + case cerl:is_data(Core) of + true -> no; + false -> delay_build_1(Core, TypeSig) + end. + +delay_build_1(Core0, TypeSig) -> + try delay_build_expr(Core0, TypeSig) of + Core -> + {Type,Arity} = TypeSig, + Ann = [compiler_generated], + Vars = make_vars(Ann, Arity), + Data = cerl:ann_make_data(Ann, Type, Vars), + {yes,Vars,Core,Data} + catch + throw:impossible -> + no + end. + +delay_build_cs([#c_clause{body=B0}=C0|Cs], TypeSig) -> + B = delay_build_expr(B0, TypeSig), + C = C0#c_clause{body=B}, + [C|delay_build_cs(Cs, TypeSig)]; +delay_build_cs([], _) -> []. + +delay_build_expr(Core, {Type,Arity}=TypeSig) -> + case cerl:is_data(Core) of + false -> + delay_build_expr_1(Core, TypeSig); + true -> + case {cerl:data_type(Core),cerl:data_arity(Core)} of + {Type,Arity} -> + core_lib:make_values(cerl:data_es(Core)); + {_,_} -> + throw(impossible) + end + end. + +delay_build_expr_1(#c_case{clauses=Cs0}=Case, TypeSig) -> + Cs = delay_build_cs(Cs0, TypeSig), + Case#c_case{clauses=Cs}; +delay_build_expr_1(#c_let{body=B0}=Let, TypeSig) -> + B = delay_build_expr(B0, TypeSig), + Let#c_let{body=B}; +delay_build_expr_1(#c_receive{clauses=Cs0, + timeout=Timeout, + action=A0}=Rec, TypeSig) -> + Cs = delay_build_cs(Cs0, TypeSig), + A = case Timeout of + #c_literal{val=infinity} -> A0; + _ -> delay_build_expr(A0, TypeSig) + end, + Rec#c_receive{clauses=Cs,action=A}; +delay_build_expr_1(#c_seq{body=B0}=Seq, TypeSig) -> + B = delay_build_expr(B0, TypeSig), + Seq#c_seq{body=B}; +delay_build_expr_1(Core, _TypeSig) -> + case will_fail(Core) of + true -> Core; + false -> throw(impossible) + end. %% opt_simple_let(#c_let{}, Context, Sub) -> CoreTerm %% Optimize a let construct that does not contain any lets in %% in its argument. -opt_simple_let(#c_let{arg=Arg0}=Let, Ctxt, Sub0) -> - Arg = body(Arg0, value, Sub0), %This is a body +opt_simple_let(Let0, Ctxt, Sub) -> + case opt_not_in_let(Let0) of + #c_let{}=Let -> + opt_simple_let_0(Let, Ctxt, Sub); + Expr -> + expr(Expr, Ctxt, Sub) + end. + +opt_simple_let_0(#c_let{arg=Arg0}=Let, Ctxt, Sub) -> + Arg = body(Arg0, value, Sub), %This is a body case will_fail(Arg) of true -> Arg; - false -> opt_simple_let_1(Let, Arg, Ctxt, Sub0) + false -> opt_simple_let_1(Let, Arg, Ctxt, Sub) end. opt_simple_let_1(#c_let{vars=Vs0,body=B0}=Let, Arg0, Ctxt, Sub0) -> %% Optimise let and add new substitutions. - {Vs,Args,Sub1} = let_substs(Vs0, Arg0, Sub0), - BodySub = case {Vs,Args} of - {[V],[A]} -> - case is_bool_expr(A, Sub0) of - true -> - update_types(V, [#c_literal{val=true}], Sub1); - false -> - Sub1 - end; - {_,_} -> Sub1 - end, - B = body(B0, Ctxt, BodySub), - Arg = core_lib:make_values(Args), - opt_simple_let_2(Let, Vs, Arg, B, Ctxt, Sub1). - -opt_simple_let_2(Let0, Vs0, Arg0, Body0, effect, Sub) -> - case {Vs0,Arg0,Body0} of - {[],#c_values{es=[]},Body} -> - %% No variables left (because of substitutions). - Body; - {[_|_],Arg,#c_literal{}} -> - %% The body is a literal. That means that we can ignore - %% it and that the return value is Arg revisited in - %% effect context. - body(Arg, effect, sub_new_preserve_types(Sub)); - {Vs,Arg,Body} -> - %% Since we are in effect context, there is a chance - %% that the body no longer references the variables. - %% In that case we can construct a sequence and visit - %% that in effect context: - %% let <Var> = Arg in BodyWithoutVar ==> seq Arg BodyWithoutVar - case is_any_var_used(Vs, Body) of - false -> - expr(#c_seq{arg=Arg,body=Body}, effect, sub_new_preserve_types(Sub)); - true -> - Let = Let0#c_let{vars=Vs,arg=Arg,body=Body}, - opt_case_in_let_arg(opt_case_in_let(Let, Sub), effect, Sub) - end - end; -opt_simple_let_2(Let, Vs0, Arg0, Body, value, Sub) -> + {Vs1,Args,Sub1} = let_substs(Vs0, Arg0, Sub0), + BodySub = update_let_types(Vs1, Args, Sub1), + B1 = body(B0, Ctxt, BodySub), + Arg1 = core_lib:make_values(Args), + {Vs,Arg,B} = opt_not_in_let(Vs1, Arg1, B1), + opt_simple_let_2(Let, Vs, Arg, B, B0, Ctxt, Sub1). + +opt_simple_let_2(Let0, Vs0, Arg0, Body, PrevBody, Ctxt, Sub) -> case {Vs0,Arg0,Body} of - {[#c_var{name=N1}],Arg,#c_var{name=N2}} -> + {[#c_var{name=N1}],Arg1,#c_var{name=N2}} -> case N1 =:= N2 of true -> %% let <Var> = Arg in <Var> ==> Arg - Arg; + Arg1; false -> %% let <Var> = Arg in <OtherVar> ==> seq Arg OtherVar - expr(#c_seq{arg=Arg,body=Body}, value, sub_new_preserve_types(Sub)) + Arg = maybe_suppress_warnings(Arg1, Vs0, PrevBody), + expr(#c_seq{arg=Arg,body=Body}, Ctxt, + sub_new_preserve_types(Sub)) end; {[],#c_values{es=[]},_} -> %% No variables left. Body; - {_,Arg,#c_literal{}} -> - %% The variable is not used in the body. The argument - %% can be evaluated in effect context to simplify it. - expr(#c_seq{arg=Arg,body=Body}, value, sub_new_preserve_types(Sub)); - {Vs,Arg,Body} -> - opt_case_in_let_arg( - opt_case_in_let(Let#c_let{vars=Vs,arg=Arg,body=Body}, Sub), - value, Sub) + {Vs,Arg1,#c_literal{}} -> + Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody), + E = case Ctxt of + effect -> + %% Throw away the literal body. + Arg; + value -> + %% Since the variable is not used in the body, we + %% can rewrite the let to a sequence. + %% let <Var> = Arg in Literal ==> seq Arg Literal + #c_seq{arg=Arg,body=Body} + end, + expr(E, Ctxt, sub_new_preserve_types(Sub)); + {Vs,Arg1,Body} -> + %% If none of the variables are used in the body, we can + %% rewrite the let to a sequence: + %% let <Var> = Arg in BodyWithoutVar ==> + %% seq Arg BodyWithoutVar + case is_any_var_used(Vs, Body) of + false -> + Arg = maybe_suppress_warnings(Arg1, Vs, PrevBody), + expr(#c_seq{arg=Arg,body=Body}, Ctxt, + sub_new_preserve_types(Sub)); + true -> + Let1 = Let0#c_let{vars=Vs,arg=Arg1,body=Body}, + Let2 = opt_bool_case_in_let(Let1, Sub), + opt_case_in_let_arg(Let2, Ctxt, Sub) + end end. +%% maybe_suppress_warnings(Arg, [#c_var{}], PreviousBody) -> Arg' +%% Try to suppress false warnings when a variable is not used. +%% For instance, we don't expect a warning for useless building in: +%% +%% R = #r{}, %No warning expected. +%% R#r.f %Optimization would remove the reference to R. +%% +%% To avoid false warnings, we will check whether the variables were +%% referenced in the original unoptimized code. If they were, we will +%% consider the warning false and suppress it. + +maybe_suppress_warnings(Arg, Vs, PrevBody) -> + case should_suppress_warning(Arg) of + true -> + Arg; %Already suppressed. + false -> + case is_any_var_used(Vs, PrevBody) of + true -> + suppress_warning([Arg]); + false -> + Arg + end + end. + +%% Suppress warnings for a Core Erlang expression whose value will +%% be ignored. +suppress_warning([H|T]) -> + case cerl:is_literal(H) of + true -> + suppress_warning(T); + false -> + case cerl:is_data(H) of + true -> + suppress_warning(cerl:data_es(H) ++ T); + false -> + %% Some other thing, such as a function call. + %% This cannot be the compiler's fault, so the + %% warning should not be suppressed. We must + %% be careful not to destroy tail-recursion. + case T of + [] -> + H; + [_|_] -> + cerl:c_seq(H, suppress_warning(T)) + end + end + end; +suppress_warning([]) -> void(). + move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg, body=InnerArg0}=Outer, clauses=InnerClauses}=Inner, Sub) -> @@ -2682,8 +2738,8 @@ move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg, %% let <OuterVars> = <OuterArg> %% in case <InnerArg> of <InnerClauses> end %% - ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}), - {OuterVars,ScopeSub} = pattern_list(OuterVars0, ScopeSub0), + ScopeSub0 = sub_subst_scope(Sub#sub{t=#{}}), + {OuterVars,ScopeSub} = var_list(OuterVars0, ScopeSub0), InnerArg = body(InnerArg0, ScopeSub), Outer#c_let{vars=OuterVars,arg=OuterArg, body=Inner#c_case{arg=InnerArg,clauses=InnerClauses}}; @@ -2711,15 +2767,19 @@ move_case_into_arg(#c_case{arg=#c_case{arg=OuterArg, %% <OuterCb> %% end %% - ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}), - {OuterPats,ScopeSub} = pattern_list(OuterPats0, ScopeSub0), - OuterGuard = guard(OuterGuard0, ScopeSub), - InnerArg = body(InnerArg0, ScopeSub), - Inner = Inner0#c_case{arg=InnerArg,clauses=InnerClauses}, - OuterCa = OuterCa0#c_clause{pats=OuterPats,guard=OuterGuard, - body=Inner}, - Outer#c_case{arg=OuterArg, - clauses=[OuterCa,OuterCb]}; + ScopeSub0 = sub_subst_scope(Sub#sub{t=#{}}), + + %% We KNOW that pattern_list/2 has already been called for OuterPats0; + %% therefore, it cannot throw an exception. + {OuterPats,ScopeSub} = pattern_list(OuterPats0, ScopeSub0), + OuterGuard = guard(OuterGuard0, ScopeSub), + InnerArg = body(InnerArg0, ScopeSub), + Inner = Inner0#c_case{arg=InnerArg,clauses=InnerClauses}, + OuterCa = OuterCa0#c_clause{pats=OuterPats, + guard=OuterGuard, + body=Inner}, + Outer#c_case{arg=OuterArg, + clauses=[OuterCa,OuterCb]}; false -> impossible end; @@ -2756,7 +2816,7 @@ move_case_into_arg(_, _) -> %% <> when 'true' -> %% let <Var> = Literal2 in LetBody %% end -%% +%% %% In the worst case, the size of the code could increase. %% In practice, though, substituting the literals into %% LetBody and doing constant folding will decrease the code @@ -2789,14 +2849,120 @@ is_any_var_used([#c_var{name=V}|Vs], Expr) -> end; is_any_var_used([], _) -> false. -is_boolean_type(V, #sub{t=Tdb}) -> - case orddict:find(V, Tdb) of - {ok,bool} -> true; - _ -> false +%%% +%%% Retrieving information about types. +%%% + +-spec get_type(cerl:cerl(), #sub{}) -> type_info() | 'none'. + +get_type(#c_var{name=V}, #sub{t=Tdb}) -> + case Tdb of + #{V:=Type} -> Type; + _ -> none + end; +get_type(C, _) -> + case cerl:type(C) of + binary -> C; + map -> C; + _ -> + case cerl:is_data(C) of + true -> C; + false -> none + end + end. + +-spec is_boolean_type(cerl:cerl(), sub()) -> yes_no_maybe(). + +is_boolean_type(Var, Sub) -> + case get_type(Var, Sub) of + none -> + maybe; + bool -> + yes; + C -> + B = cerl:is_c_atom(C) andalso + is_boolean(cerl:atom_val(C)), + yes_no(B) end. +-spec is_int_type(cerl:cerl(), sub()) -> yes_no_maybe(). + +is_int_type(Var, Sub) -> + case get_type(Var, Sub) of + none -> maybe; + integer -> yes; + C -> yes_no(cerl:is_c_int(C)) + end. + +-spec is_tuple_type(cerl:cerl(), sub()) -> yes_no_maybe(). + +is_tuple_type(Var, Sub) -> + case get_type(Var, Sub) of + none -> maybe; + C -> yes_no(cerl:is_c_tuple(C)) + end. + +yes_no(true) -> yes; +yes_no(false) -> no. + +%%% +%%% Update type information. +%%% + +update_let_types(Vs, Args, Sub) when is_list(Args) -> + update_let_types_1(Vs, Args, Sub); +update_let_types(_Vs, _Arg, Sub) -> + %% The argument is a complex expression (such as a 'case') + %% that returns multiple values. + Sub. + +update_let_types_1([#c_var{}=V|Vs], [A|As], Sub0) -> + Sub = update_types_from_expr(V, A, Sub0), + update_let_types_1(Vs, As, Sub); +update_let_types_1([], [], Sub) -> Sub. + +update_types_from_expr(V, Expr, Sub) -> + Type = extract_type(Expr, Sub), + update_types(V, [Type], Sub). + +extract_type(#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=Name}, + args=Args}=Call, Sub) -> + case returns_integer(Name, Args) of + true -> integer; + false -> extract_type_1(Call, Sub) + end; +extract_type(Expr, Sub) -> + extract_type_1(Expr, Sub). + +extract_type_1(Expr, Sub) -> + case is_bool_expr(Expr, Sub) of + false -> Expr; + true -> bool + end. + +returns_integer('band', [_,_]) -> true; +returns_integer('bnot', [_]) -> true; +returns_integer('bor', [_,_]) -> true; +returns_integer('bxor', [_,_]) -> true; +returns_integer(bit_size, [_]) -> true; +returns_integer('bsl', [_,_]) -> true; +returns_integer('bsr', [_,_]) -> true; +returns_integer(byte_size, [_]) -> true; +returns_integer('div', [_,_]) -> true; +returns_integer(length, [_]) -> true; +returns_integer('rem', [_,_]) -> true; +returns_integer('round', [_]) -> true; +returns_integer(size, [_]) -> true; +returns_integer(tuple_size, [_]) -> true; +returns_integer(trunc, [_]) -> true; +returns_integer(_, _) -> false. + %% update_types(Expr, Pattern, Sub) -> Sub' %% Update the type database. + +-spec update_types(cerl:cerl(), [type_info()], sub()) -> sub(). + update_types(Expr, Pat, #sub{t=Tdb0}=Sub) -> Tdb = update_types_1(Expr, Pat, Tdb0), Sub#sub{t=Tdb}. @@ -2813,33 +2979,38 @@ update_types_1(#c_var{name=V,anno=Anno}, Pat, Types) -> update_types_1(_, _, Types) -> Types. update_types_2(V, [#c_tuple{}=P], Types) -> - orddict:store(V, P, Types); + Types#{V=>P}; update_types_2(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) -> - orddict:store(V, bool, Types); + Types#{V=>bool}; +update_types_2(V, [Type], Types) when is_atom(Type) -> + Types#{V=>Type}; update_types_2(_, _, Types) -> Types. %% kill_types(V, Tdb) -> Tdb' %% Kill any entries that references the variable, %% either in the key or in the value. -kill_types(V, [{V,_}|Tdb]) -> - kill_types(V, Tdb); -kill_types(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) -> +kill_types(V, Tdb) -> + maps:from_list(kill_types2(V,maps:to_list(Tdb))). + +kill_types2(V, [{V,_}|Tdb]) -> + kill_types2(V, Tdb); +kill_types2(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) -> case core_lib:is_var_used(V, Tuple) of - false -> [Entry|kill_types(V, Tdb)]; - true -> kill_types(V, Tdb) + false -> [Entry|kill_types2(V, Tdb)]; + true -> kill_types2(V, Tdb) end; -kill_types(V, [{_,Atom}=Entry|Tdb]) when is_atom(Atom) -> - [Entry|kill_types(V, Tdb)]; -kill_types(_, []) -> []. +kill_types2(V, [{_,Atom}=Entry|Tdb]) when is_atom(Atom) -> + [Entry|kill_types2(V, Tdb)]; +kill_types2(_, []) -> []. %% copy_type(DestVar, SrcVar, Tdb) -> Tdb' %% If the SrcVar has a type, assign it to DestVar. %% copy_type(V, #c_var{name=Src}, Tdb) -> - case orddict:find(Src, Tdb) of - {ok,Type} -> orddict:store(V, Type, Tdb); - error -> Tdb + case Tdb of + #{Src:=Type} -> Tdb#{V=>Type}; + _ -> Tdb end; copy_type(_, _, Tdb) -> Tdb. @@ -3074,15 +3245,15 @@ bsm_ensure_no_partition_2([#c_var{name=V}|Ps], N, G, Vstate, S) -> bsm_ensure_no_partition_2([_|Ps], N, G, _, S) -> bsm_ensure_no_partition_2(Ps, N-1, G, bin_argument_order, S). -bsm_ensure_no_partition_after([#c_clause{pats=Ps}|Cs], Pos) -> +bsm_ensure_no_partition_after([#c_clause{pats=Ps}=C|Cs], Pos) -> case nth(Pos, Ps) of #c_var{} -> bsm_ensure_no_partition_after(Cs, Pos); - P -> - bsm_problem(P, bin_partition) + _ -> + bsm_problem(C, bin_partition) end; bsm_ensure_no_partition_after([], _) -> ok. - + bsm_could_match_binary(#c_alias{pat=P}) -> bsm_could_match_binary(P); bsm_could_match_binary(#c_cons{}) -> false; bsm_could_match_binary(#c_tuple{}) -> false; @@ -3116,11 +3287,11 @@ add_bin_opt_info(Core, Term) -> end. add_warning(Core, Term) -> - case is_compiler_generated(Core) of + case should_suppress_warning(Core) of true -> ok; false -> - Anno = core_lib:get_anno(Core), + Anno = cerl:get_ann(Core), Line = get_line(Anno), File = get_file(Anno), Key = {?MODULE,warnings}, @@ -3141,9 +3312,17 @@ get_file([{file,File}|_]) -> File; get_file([_|T]) -> get_file(T); get_file([]) -> "no_file". % should not happen +should_suppress_warning(Core) -> + is_compiler_generated(Core) orelse + is_result_unwanted(Core). + is_compiler_generated(Core) -> - Anno = core_lib:get_anno(Core), - member(compiler_generated, Anno). + Ann = cerl:get_ann(Core), + member(compiler_generated, Ann). + +is_result_unwanted(Core) -> + Ann = cerl:get_ann(Core), + member(result_not_wanted, Ann). get_warnings() -> ordsets:from_list((erase({?MODULE,warnings}))). @@ -3182,6 +3361,29 @@ format_error(nomatch_shadow) -> "this clause cannot match because a previous clause always matches"; format_error(nomatch_guard) -> "the guard for this clause evaluates to 'false'"; +format_error({nomatch_bit_syntax_truncated,Signess,Val,Sz}) -> + S = case Signess of + signed -> "a 'signed'"; + unsigned -> "an 'unsigned'" + end, + F = "this clause cannot match because the value ~P" + " will not fit in ~s binary segment of size ~p", + flatten(io_lib:format(F, [Val,10,S,Sz])); +format_error({nomatch_bit_syntax_unsigned,Val}) -> + F = "this clause cannot match because the negative value ~P" + " will never match the value of an 'unsigned' binary segment", + flatten(io_lib:format(F, [Val,10])); +format_error({nomatch_bit_syntax_size,Sz}) -> + F = "this clause cannot match because '~P' is not a valid size for a binary segment", + flatten(io_lib:format(F, [Sz,10])); +format_error({nomatch_bit_syntax_type,Val,Type}) -> + F = "this clause cannot match because '~P' is not of the" + " expected type '~p'", + flatten(io_lib:format(F, [Val,10,Type])); +format_error({nomatch_bit_syntax_unicode,Val}) -> + F = "this clause cannot match because the value ~p" + " is not a valid Unicode code point", + flatten(io_lib:format(F, [Val])); format_error(no_clause_match) -> "no clause will ever match"; format_error(nomatch_clause_type) -> @@ -3202,6 +3404,8 @@ format_error({no_effect,{erlang,F,A}}) -> format_error(result_ignored) -> "the result of the expression is ignored " "(suppress the warning by assigning the expression to the _ variable)"; +format_error(invalid_call) -> + "invalid function call"; format_error(useless_building) -> "a term is constructed, but never used"; format_error(bin_opt_alias) -> @@ -3235,12 +3439,12 @@ format_error(bin_var_used_in_guard) -> verify_scope(E, #sub{s=Scope}) -> Free0 = cerl_trees:free_variables(E), Free = [V || V <- Free0, not is_tuple(V)], %Ignore function names. - case ordsets:is_subset(Free, gb_sets:to_list(Scope)) of + case ordsets:is_subset(Free, cerl_sets:to_list(Scope)) of true -> true; false -> io:format("~p\n", [E]), io:format("~p\n", [Free]), - io:format("~p\n", [gb_sets:to_list(Scope)]), + io:format("~p\n", [cerl_sets:to_list(Scope)]), false end. -endif. diff --git a/lib/compiler/src/sys_core_fold_lists.erl b/lib/compiler/src/sys_core_fold_lists.erl new file mode 100644 index 0000000000..9867fab46a --- /dev/null +++ b/lib/compiler/src/sys_core_fold_lists.erl @@ -0,0 +1,387 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. 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% +%% +%% Purpose : Inline high order lists functions from the lists module. + +-module(sys_core_fold_lists). + +-export([call/4]). + +-include("core_parse.hrl"). + +%% We inline some very common higher order list operations. +%% We use the same evaluation order as the library function. + +-spec call(cerl:c_call(), atom(), atom(), [cerl:cerl()]) -> + 'none' | cerl:cerl(). + +call(#c_call{anno=Anno}, lists, all, [Arg1,Arg2]) -> + Loop = #c_var{name={'lists^all',1}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]}, + CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true}, + body=#c_apply{anno=Anno, op=Loop, args=[Xs]}}, + CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true}, + body=#c_literal{val=false}}, + CC3 = #c_clause{pats=[X], guard=#c_literal{val=true}, + body=match_fail(Anno, Err1)}, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, + body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]}, + clauses = [CC1, CC2, CC3]}}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, + body=#c_literal{val=true}}, + Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^all',1}}|Anno], Err2)}, + Fun = #c_fun{vars=[Xs], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, + body=#c_letrec{defs=[{Loop,Fun}], + body=#c_apply{anno=Anno, op=Loop, args=[L]}}}; +call(#c_call{anno=Anno}, lists, any, [Arg1,Arg2]) -> + Loop = #c_var{name={'lists^any',1}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]}, + CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true}, + body=#c_literal{val=true}}, + CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true}, + body=#c_apply{anno=Anno, op=Loop, args=[Xs]}}, + CC3 = #c_clause{pats=[X], guard=#c_literal{val=true}, + body=match_fail(Anno, Err1)}, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, + body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]}, + clauses = [CC1, CC2, CC3]}}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, + body=#c_literal{val=false}}, + Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^any',1}}|Anno], Err2)}, + Fun = #c_fun{vars=[Xs], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, + body=#c_letrec{defs=[{Loop,Fun}], + body=#c_apply{anno=Anno, op=Loop, args=[L]}}}; +call(#c_call{anno=Anno}, lists, foreach, [Arg1,Arg2]) -> + Loop = #c_var{name={'lists^foreach',1}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, + body=#c_seq{arg=#c_apply{anno=Anno, op=F, args=[X]}, + body=#c_apply{anno=Anno, op=Loop, args=[Xs]}}}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, + body=#c_literal{val=ok}}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^foreach',1}}|Anno], Err)}, + Fun = #c_fun{vars=[Xs], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, + body=#c_letrec{defs=[{Loop,Fun}], + body=#c_apply{anno=Anno, op=Loop, args=[L]}}}; +call(#c_call{anno=Anno}, lists, map, [Arg1,Arg2]) -> + Loop = #c_var{name={'lists^map',1}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + H = #c_var{name='H'}, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, + body=#c_let{vars=[H], arg=#c_apply{anno=Anno, + op=F, + args=[X]}, + body=#c_cons{hd=H, + anno=[compiler_generated], + tl=#c_apply{anno=Anno, + op=Loop, + args=[Xs]}}}}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, + body=#c_literal{val=[]}}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^map',1}}|Anno], Err)}, + Fun = #c_fun{vars=[Xs], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, + body=#c_letrec{defs=[{Loop,Fun}], + body=#c_apply{anno=Anno, op=Loop, args=[L]}}}; +call(#c_call{anno=Anno}, lists, flatmap, [Arg1,Arg2]) -> + Loop = #c_var{name={'lists^flatmap',1}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + H = #c_var{name='H'}, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, + body=#c_let{vars=[H], + arg=#c_apply{anno=Anno, op=F, args=[X]}, + body=#c_call{anno=[compiler_generated|Anno], + module=#c_literal{val=erlang}, + name=#c_literal{val='++'}, + args=[H, + #c_apply{anno=Anno, + op=Loop, + args=[Xs]}]}}}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, + body=#c_literal{val=[]}}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^flatmap',1}}|Anno], Err)}, + Fun = #c_fun{vars=[Xs], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, + body=#c_letrec{defs=[{Loop,Fun}], + body=#c_apply{anno=Anno, op=Loop, args=[L]}}}; +call(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2]) -> + Loop = #c_var{name={'lists^filter',1}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + B = #c_var{name='B'}, + Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]}, + CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true}, + body=#c_cons{anno=[compiler_generated], hd=X, tl=Xs}}, + CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true}, + body=Xs}, + CC3 = #c_clause{pats=[X], guard=#c_literal{val=true}, + body=match_fail(Anno, Err1)}, + Case = #c_case{arg=B, clauses = [CC1, CC2, CC3]}, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, + body=#c_let{vars=[B], + arg=#c_apply{anno=Anno, op=F, args=[X]}, + body=#c_let{vars=[Xs], + arg=#c_apply{anno=Anno, + op=Loop, + args=[Xs]}, + body=Case}}}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=1}]}, + body=#c_literal{val=[]}}, + Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, F, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^filter',1}}|Anno], Err2)}, + Fun = #c_fun{vars=[Xs], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]}, + body=#c_letrec{defs=[{Loop,Fun}], + body=#c_apply{anno=Anno, op=Loop, args=[L]}}}; +call(#c_call{anno=Anno}, lists, foldl, [Arg1,Arg2,Arg3]) -> + Loop = #c_var{name={'lists^foldl',2}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + A = #c_var{name='A'}, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, + body=#c_apply{anno=Anno, + op=Loop, + args=[Xs, #c_apply{anno=Anno, + op=F, + args=[X, A]}]}}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=2}]}, + body=A}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^foldl',2}}|Anno], Err)}, + Fun = #c_fun{vars=[Xs, A], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, A, L], arg=#c_values{es=[Arg1, Arg2, Arg3]}, + body=#c_letrec{defs=[{Loop,Fun}], + body=#c_apply{anno=Anno, op=Loop, args=[L, A]}}}; +call(#c_call{anno=Anno}, lists, foldr, [Arg1,Arg2,Arg3]) -> + Loop = #c_var{name={'lists^foldr',2}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + A = #c_var{name='A'}, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, + body=#c_apply{anno=Anno, + op=F, + args=[X, #c_apply{anno=Anno, + op=Loop, + args=[Xs, A]}]}}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=2}]}, + body=A}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, A, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^foldr',2}}|Anno], Err)}, + Fun = #c_fun{vars=[Xs, A], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, A, L], arg=#c_values{es=[Arg1, Arg2, Arg3]}, + body=#c_letrec{defs=[{Loop,Fun}], + body=#c_apply{anno=Anno, op=Loop, args=[L, A]}}}; +call(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3]) -> + Loop = #c_var{name={'lists^mapfoldl',2}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + Avar = #c_var{name='A'}, + Match = + fun (A, P, E) -> + C1 = #c_clause{pats=[P], guard=#c_literal{val=true}, body=E}, + Err = #c_tuple{es=[#c_literal{val='badmatch'}, X]}, + C2 = #c_clause{pats=[X], guard=#c_literal{val=true}, + body=match_fail(Anno, Err)}, + #c_case{arg=A, clauses=[C1, C2]} + end, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, + body=Match(#c_apply{anno=Anno, op=F, args=[X, Avar]}, + #c_tuple{es=[X, Avar]}, +%%% Tuple passing version + Match(#c_apply{anno=Anno, + op=Loop, + args=[Xs, Avar]}, + #c_tuple{es=[Xs, Avar]}, + #c_tuple{anno=[compiler_generated], + es=[#c_cons{anno=[compiler_generated], + hd=X, tl=Xs}, + Avar]}) +%%% Multiple-value version +%%% #c_let{vars=[Xs,A], +%%% %% The tuple here will be optimised +%%% %% away later; no worries. +%%% arg=#c_apply{op=Loop, args=[Xs, A]}, +%%% body=#c_values{es=[#c_cons{hd=X, tl=Xs}, +%%% A]}} + )}, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=2}]}, +%%% Tuple passing version + body=#c_tuple{anno=[compiler_generated], + es=[#c_literal{val=[]}, Avar]}}, +%%% Multiple-value version +%%% body=#c_values{es=[#c_literal{val=[]}, A]}}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^mapfoldl',2}}|Anno], Err)}, + Fun = #c_fun{vars=[Xs, Avar], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, Avar, L], arg=#c_values{es=[Arg1, Arg2, Arg3]}, + body=#c_letrec{defs=[{Loop,Fun}], +%%% Tuple passing version + body=#c_apply{anno=Anno, + op=Loop, + args=[L, Avar]}}}; +%%% Multiple-value version +%%% body=#c_let{vars=[Xs, A], +%%% arg=#c_apply{op=Loop, +%%% args=[L, A]}, +%%% body=#c_tuple{es=[Xs, A]}}}}; +call(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3]) -> + Loop = #c_var{name={'lists^mapfoldr',2}}, + F = #c_var{name='F'}, + Xs = #c_var{name='Xs'}, + X = #c_var{name='X'}, + Avar = #c_var{name='A'}, + Match = + fun (A, P, E) -> + C1 = #c_clause{pats=[P], guard=#c_literal{val=true}, body=E}, + Err = #c_tuple{es=[#c_literal{val='badmatch'}, X]}, + C2 = #c_clause{pats=[X], guard=#c_literal{val=true}, + body=match_fail(Anno, Err)}, + #c_case{arg=A, clauses=[C1, C2]} + end, + C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true}, +%%% Tuple passing version + body=Match(#c_apply{anno=Anno, + op=Loop, + args=[Xs, Avar]}, + #c_tuple{es=[Xs, Avar]}, + Match(#c_apply{anno=Anno, op=F, args=[X, Avar]}, + #c_tuple{es=[X, Avar]}, + #c_tuple{anno=[compiler_generated], + es=[#c_cons{anno=[compiler_generated], + hd=X, tl=Xs}, Avar]})) +%%% Multiple-value version +%%% body=#c_let{vars=[Xs,A], +%%% %% The tuple will be optimised away +%%% arg=#c_apply{op=Loop, args=[Xs, A]}, +%%% body=Match(#c_apply{op=F, args=[X, A]}, +%%% #c_tuple{es=[X, A]}, +%%% #c_values{es=[#c_cons{hd=X, tl=Xs}, +%%% A]})} + }, + C2 = #c_clause{pats=[#c_literal{val=[]}], + guard=#c_call{module=#c_literal{val=erlang}, + name=#c_literal{val=is_function}, + args=[F, #c_literal{val=2}]}, +%%% Tuple passing version + body=#c_tuple{anno=[compiler_generated], + es=[#c_literal{val=[]}, Avar]}}, +%%% Multiple-value version +%%% body=#c_values{es=[#c_literal{val=[]}, A]}}, + Err = #c_tuple{es=[#c_literal{val='function_clause'}, F, Avar, Xs]}, + C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true}, + body=match_fail([{function_name,{'lists^mapfoldr',2}}|Anno], Err)}, + Fun = #c_fun{vars=[Xs, Avar], + body=#c_case{arg=Xs, clauses=[C1, C2, C3]}}, + L = #c_var{name='L'}, + #c_let{vars=[F, Avar, L], arg=#c_values{es=[Arg1, Arg2, Arg3]}, + body=#c_letrec{defs=[{Loop,Fun}], +%%% Tuple passing version + body=#c_apply{anno=Anno, + op=Loop, + args=[L, Avar]}}}; +%%% Multiple-value version +%%% body=#c_let{vars=[Xs, A], +%%% arg=#c_apply{op=Loop, +%%% args=[L, A]}, +%%% body=#c_tuple{es=[Xs, A]}}}}; +call(_, _, _, _) -> + none. + +match_fail(Ann, Arg) -> + Name = cerl:abstract(match_fail), + Args = [Arg], + cerl:ann_c_primop(Ann, Name, Args). diff --git a/lib/compiler/src/sys_core_inline.erl b/lib/compiler/src/sys_core_inline.erl index 9f93acb666..8c1f69d5de 100644 --- a/lib/compiler/src/sys_core_inline.erl +++ b/lib/compiler/src/sys_core_inline.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -195,10 +196,10 @@ kill_id_anns(Body) -> A = kill_id_anns_1(A0), CFun#c_fun{anno=A}; (Expr) -> - %% Mark everything as compiler generated to suppress - %% bogus warnings. - A = compiler_generated(core_lib:get_anno(Expr)), - core_lib:set_anno(Expr, A) + %% Mark everything as compiler generated to + %% suppress bogus warnings. + A = compiler_generated(cerl:get_ann(Expr)), + cerl:set_ann(Expr, A) end, Body). kill_id_anns_1([{'id',_}|As]) -> diff --git a/lib/compiler/src/sys_pre_attributes.erl b/lib/compiler/src/sys_pre_attributes.erl index a6b7274b07..bc93c85989 100644 --- a/lib/compiler/src/sys_pre_attributes.erl +++ b/lib/compiler/src/sys_pre_attributes.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index 761ae8409c..7ab4e1845c 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2015. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -28,30 +29,26 @@ %% Main entry point. -export([module/2]). --import(ordsets, [from_list/1,union/2]). -import(lists, [member/2,foldl/3,foldr/3]). --include("../include/erl_bits.hrl"). +-type fa() :: {atom(), arity()}. -record(expand, {module=[], %Module name exports=[], %Exports - imports=[], %Imports - compile=[], %Compile flags attributes=[], %Attributes callbacks=[], %Callbacks - defined, %Defined functions (gb_set) + optional_callbacks=[] :: [fa()], %Optional callbacks vcount=0, %Variable counter func=[], %Current function arity=[], %Arity for current function - fcount=0, %Local fun count - bitdefault, - bittypes + fcount=0, %Local fun count + ctype %Call type map }). %% module(Forms, CompileOptions) %% {ModuleName,Exports,TransformedForms,CompileOptions'} -%% Expand the forms in one module. N.B.: the lists of predefined -%% exports and imports are really ordsets! +%% Expand the forms in one module. +%% %% CompileOptions is augmented with options from -compile attributes. module(Fs0, Opts0) -> @@ -64,63 +61,87 @@ module(Fs0, Opts0) -> %% Set pre-defined exported functions. PreExp = [{module_info,0},{module_info,1}], + %% Build the set of defined functions and the initial call + %% type map. + Defined = defined_functions(Fs, PreExp), + Ctype = maps:from_list([{K,local} || K <- Defined]), + %% Build initial expand record. St0 = #expand{exports=PreExp, - compile=Opts, - defined=PreExp, - bitdefault = erl_bits:system_bitdefault(), - bittypes = erl_bits:system_bittypes() + ctype=Ctype }, + %% Expand the functions. - {Tfs,St1} = forms(Fs, define_functions(Fs, St0)), + {Tfs,St1} = forms(Fs, St0), + %% Get the correct list of exported functions. - Exports = case member(export_all, St1#expand.compile) of - true -> gb_sets:to_list(St1#expand.defined); + Exports = case member(export_all, Opts) of + true -> Defined; false -> St1#expand.exports end, + St2 = St1#expand{exports=Exports,ctype=undefined}, + %% Generate all functions from stored info. - {Ats,St3} = module_attrs(St1#expand{exports = Exports}), + {Ats,St3} = module_attrs(St2), {Mfs,St4} = module_predef_funcs(St3), {St4#expand.module, St4#expand.exports, Ats ++ Tfs ++ Mfs, - St4#expand.compile}. + Opts}. compiler_options(Forms) -> lists:flatten([C || {attribute,_,compile,C} <- Forms]). -%% define_function(Form, State) -> State. +%% defined_function(Forms, Predef) -> Functions. %% Add function to defined if form is a function. -define_functions(Forms, #expand{defined=Predef}=St) -> +defined_functions(Forms, Predef) -> Fs = foldl(fun({function,_,N,A,_Cs}, Acc) -> [{N,A}|Acc]; (_, Acc) -> Acc end, Predef, Forms), - St#expand{defined=gb_sets:from_list(Fs)}. + ordsets:from_list(Fs). module_attrs(#expand{attributes=Attributes}=St) -> Attrs = [{attribute,Line,Name,Val} || {Name,Line,Val} <- Attributes], Callbacks = [Callback || {_,_,callback,_}=Callback <- Attrs], - {Attrs,St#expand{callbacks=Callbacks}}. - -module_predef_funcs(St) -> - {Mpf1,St1}=module_predef_func_beh_info(St), - {Mpf2,St2}=module_predef_funcs_mod_info(St1), - {Mpf1++Mpf2,St2}. + OptionalCallbacks = get_optional_callbacks(Attrs), + {Attrs,St#expand{callbacks=Callbacks, + optional_callbacks=OptionalCallbacks}}. + +get_optional_callbacks(Attrs) -> + L = [O || + {attribute, _, optional_callbacks, O} <- Attrs, + is_fa_list(O)], + lists:append(L). + +is_fa_list([{FuncName, Arity}|L]) + when is_atom(FuncName), is_integer(Arity), Arity >= 0 -> + is_fa_list(L); +is_fa_list([]) -> true; +is_fa_list(_) -> false. + +module_predef_funcs(St0) -> + {Mpf1,St1} = module_predef_func_beh_info(St0), + Mpf2 = module_predef_funcs_mod_info(St1), + Mpf = [erl_parse:new_anno(F) || F <- Mpf1++Mpf2], + {Mpf,St1}. module_predef_func_beh_info(#expand{callbacks=[]}=St) -> {[], St}; -module_predef_func_beh_info(#expand{callbacks=Callbacks,defined=Defined, +module_predef_func_beh_info(#expand{callbacks=Callbacks, + optional_callbacks=OptionalCallbacks, exports=Exports}=St) -> - PreDef=[{behaviour_info,1}], - PreExp=PreDef, - {[gen_beh_info(Callbacks)], - St#expand{defined=gb_sets:union(gb_sets:from_list(PreDef), Defined), - exports=union(from_list(PreExp), Exports)}}. + PreDef0 = [{behaviour_info,1}], + PreDef = ordsets:from_list(PreDef0), + {[gen_beh_info(Callbacks, OptionalCallbacks)], + St#expand{exports=ordsets:union(PreDef, Exports)}}. -gen_beh_info(Callbacks) -> +gen_beh_info(Callbacks, OptionalCallbacks) -> List = make_list(Callbacks), + OptionalList = make_optional_list(OptionalCallbacks), {function,0,behaviour_info,1, [{clause,0,[{atom,0,callbacks}],[], - [List]}]}. + [List]}, + {clause,0,[{atom,0,optional_callbacks}],[], + [OptionalList]}]}. make_list([]) -> {nil,0}; make_list([{_,_,_,[{{Name,Arity},_}]}|Rest]) -> @@ -130,20 +151,24 @@ make_list([{_,_,_,[{{Name,Arity},_}]}|Rest]) -> {integer,0,Arity}]}, make_list(Rest)}. -module_predef_funcs_mod_info(St) -> - PreDef = [{module_info,0},{module_info,1}], - PreExp = PreDef, - {[{function,0,module_info,0, - [{clause,0,[],[], +make_optional_list([]) -> {nil,0}; +make_optional_list([{Name,Arity}|Rest]) -> + {cons,0, + {tuple,0, + [{atom,0,Name}, + {integer,0,Arity}]}, + make_optional_list(Rest)}. + +module_predef_funcs_mod_info(#expand{module=Mod}) -> + ModAtom = {atom,0,Mod}, + [{function,0,module_info,0, + [{clause,0,[],[], [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}}, - [{atom,0,St#expand.module}]}]}]}, - {function,0,module_info,1, - [{clause,0,[{var,0,'X'}],[], + [ModAtom]}]}]}, + {function,0,module_info,1, + [{clause,0,[{var,0,'X'}],[], [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}}, - [{atom,0,St#expand.module},{var,0,'X'}]}]}]}], - St#expand{defined=gb_sets:union(gb_sets:from_list(PreDef), - St#expand.defined), - exports=union(from_list(PreExp), St#expand.exports)}}. + [ModAtom,{var,0,'X'}]}]}]}]. %% forms(Forms, State) -> %% {TransformedForms,State'} @@ -170,7 +195,8 @@ attribute(module, Module, _L, St) -> true = is_atom(Module), St#expand{module=Module}; attribute(export, Es, _L, St) -> - St#expand{exports=union(from_list(Es), St#expand.exports)}; + St#expand{exports=ordsets:union(ordsets:from_list(Es), + St#expand.exports)}; attribute(import, Is, _L, St) -> import(Is, St); attribute(compile, _C, _L, St) -> @@ -205,8 +231,6 @@ head(As, St) -> pattern_list(As, St). %% {TransformedPattern,State'} %% -pattern({var,_,'_'}=Var, St) -> %Ignore anonymous variable. - {Var,St}; pattern({var,_,_}=Var, St) -> {Var,St}; pattern({char,_,_}=Char, St) -> @@ -232,9 +256,18 @@ pattern({map,Line,Ps}, St0) -> {TPs,St1} = pattern_list(Ps, St0), {{map,Line,TPs},St1}; pattern({map_field_exact,Line,K0,V0}, St0) -> - {K,St1} = expr(K0, St0), + %% Key should be treated as an expression + %% but since expressions are not allowed yet, + %% process it through pattern .. and handle assoc + %% (normalise unary op integer -> integer) + {K,St1} = pattern(K0, St0), {V,St2} = pattern(V0, St1), {{map_field_exact,Line,K,V},St2}; +pattern({map_field_assoc,Line,K0,V0}, St0) -> + %% when keys are Maps + {K,St1} = pattern(K0, St0), + {V,St2} = pattern(V0, St1), + {{map_field_assoc,Line,K,V},St2}; %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1}; @@ -350,19 +383,19 @@ expr({block,Line,Es0}, St0) -> {Es,St1} = exprs(Es0, St0), {{block,Line,Es},St1}; expr({'if',Line,Cs0}, St0) -> - {Cs,St1} = icr_clauses(Cs0, St0), + {Cs,St1} = clauses(Cs0, St0), {{'if',Line,Cs},St1}; expr({'case',Line,E0,Cs0}, St0) -> {E,St1} = expr(E0, St0), - {Cs,St2} = icr_clauses(Cs0, St1), + {Cs,St2} = clauses(Cs0, St1), {{'case',Line,E,Cs},St2}; expr({'receive',Line,Cs0}, St0) -> - {Cs,St1} = icr_clauses(Cs0, St0), + {Cs,St1} = clauses(Cs0, St0), {{'receive',Line,Cs},St1}; expr({'receive',Line,Cs0,To0,ToEs0}, St0) -> {To,St1} = expr(To0, St0), {ToEs,St2} = exprs(ToEs0, St1), - {Cs,St3} = icr_clauses(Cs0, St2), + {Cs,St3} = clauses(Cs0, St2), {{'receive',Line,Cs,To,ToEs},St3}; expr({'fun',Line,Body}, St) -> fun_tq(Line, Body, St); @@ -371,21 +404,15 @@ expr({named_fun,Line,Name,Cs}, St) -> expr({call,Line,{atom,La,N}=Atom,As0}, St0) -> {As,St1} = expr_list(As0, St0), Ar = length(As), - case defined(N,Ar,St1) of - true -> + Key = {N,Ar}, + case St1#expand.ctype of + #{Key:=local} -> {{call,Line,Atom,As},St1}; + #{Key:={imported,Mod}} -> + {{call,Line,{remote,La,{atom,La,Mod},Atom},As},St1}; _ -> - case imported(N, Ar, St1) of - {yes,Mod} -> - {{call,Line,{remote,La,{atom,La,Mod},Atom},As},St1}; - no -> - case erl_internal:bif(N, Ar) of - true -> - {{call,Line,{remote,La,{atom,La,erlang},Atom},As},St1}; - false -> %% This should have been handled by erl_lint - {{call,Line,Atom,As},St1} - end - end + true = erl_internal:bif(N, Ar), + {{call,Line,{remote,La,{atom,La,erlang},Atom},As},St1} end; expr({call,Line,{remote,Lr,M0,F},As0}, St0) -> {[M1,F1|As1],St1} = expr_list([M0,F|As0], St0), @@ -395,12 +422,11 @@ expr({call,Line,F,As0}, St0) -> {{call,Line,Fun1,As1},St1}; expr({'try',Line,Es0,Scs0,Ccs0,As0}, St0) -> {Es1,St1} = exprs(Es0, St0), - {Scs1,St2} = icr_clauses(Scs0, St1), - {Ccs1,St3} = icr_clauses(Ccs0, St2), + {Scs1,St2} = clauses(Scs0, St1), + {Ccs1,St3} = clauses(Ccs0, St2), {As1,St4} = exprs(As0, St3), {{'try',Line,Es1,Scs1,Ccs1,As1},St4}; expr({'catch',Line,E0}, St0) -> - %% Catch exports no new variables. {E,St1} = expr(E0, St0), {{'catch',Line,E},St1}; expr({match,Line,P0,E0}, St0) -> @@ -421,21 +447,6 @@ expr_list([E0|Es0], St0) -> {[E|Es],St2}; expr_list([], St) -> {[],St}. -%% icr_clauses([Clause], State) -> {[TransformedClause],State'} -%% Be very careful here to return the variables that are really used -%% and really new. - -icr_clauses([], St) -> {[],St}; -icr_clauses(Clauses, St) -> icr_clauses2(Clauses, St). - -icr_clauses2([{clause,Line,H0,G0,B0}|Cs0], St0) -> - {H,St1} = head(H0, St0), - {G,St2} = guard(G0, St1), - {B,St3} = exprs(B0, St2), - {Cs,St4} = icr_clauses2(Cs0, St3), - {[{clause,Line,H,G,B}|Cs],St4}; -icr_clauses2([], St) -> {[],St}. - %% lc_tq(Line, Qualifiers, State) -> %% {[TransQual],State'} @@ -451,16 +462,9 @@ lc_tq(Line, [{b_generate,Lg,P0,G0}|Qs0], St0) -> {Qs1,St3} = lc_tq(Line, Qs0, St2), {[{b_generate,Lg,P1,G1}|Qs1],St3}; lc_tq(Line, [F0 | Qs0], St0) -> - case erl_lint:is_guard_test(F0) of - true -> - {F1,St1} = guard_test(F0, St0), - {Qs1,St2} = lc_tq(Line, Qs0, St1), - {[F1|Qs1],St2}; - false -> - {F1,St1} = expr(F0, St0), - {Qs1,St2} = lc_tq(Line, Qs0, St1), - {[F1 | Qs1],St2} - end; + {F1,St1} = expr(F0, St0), + {Qs1,St2} = lc_tq(Line, Qs0, St1), + {[F1|Qs1],St2}; lc_tq(_Line, [], St0) -> {[],St0}. @@ -492,7 +496,7 @@ fun_tq(L, {function,M,F,A}, St) when is_atom(M), is_atom(F), is_integer(A) -> fun_tq(Lf, {function,_,_,_}=ExtFun, St) -> {{'fun',Lf,ExtFun},St}; fun_tq(Lf, {clauses,Cs0}, St0) -> - {Cs1,St1} = fun_clauses(Cs0, St0), + {Cs1,St1} = clauses(Cs0, St0), {Fname,St2} = new_fun_name(St1), %% Set dummy values for Index and Uniq -- the real values will %% be assigned by beam_asm. @@ -500,18 +504,10 @@ fun_tq(Lf, {clauses,Cs0}, St0) -> {{'fun',Lf,{clauses,Cs1},{Index,Uniq,Fname}},St2}. fun_tq(Line, Cs0, St0, Name) -> - {Cs1,St1} = fun_clauses(Cs0, St0), + {Cs1,St1} = clauses(Cs0, St0), {Fname,St2} = new_fun_name(St1, Name), {{named_fun,Line,Name,Cs1,{0,0,Fname}},St2}. -fun_clauses([{clause,L,H0,G0,B0}|Cs0], St0) -> - {H,St1} = head(H0, St0), - {G,St2} = guard(G0, St1), - {B,St3} = exprs(B0, St2), - {Cs,St4} = fun_clauses(Cs0, St3), - {[{clause,L,H,G,B}|Cs],St4}; -fun_clauses([], St) -> {[],St}. - %% new_fun_name(State) -> {FunName,State}. new_fun_name(St) -> @@ -536,7 +532,6 @@ pattern_element({bin_element,Line,Expr0,Size0,Type0}, {Es,St0}) -> {[{bin_element,Line,Expr,Size,Type}|Es],St2}. pat_bit_size(default, St) -> {default,St}; -pat_bit_size({atom,_La,all}=All, St) -> {All,St}; pat_bit_size({var,_Lv,_V}=Var, St) -> {Var,St}; pat_bit_size(Size, St) -> Line = element(2, Size), @@ -557,8 +552,7 @@ coerce_to_float({integer,L,I}=E, [float|_]) -> try {float,L,float(I)} catch - error:badarg -> E; - error:badarith -> E + error:badarg -> E end; coerce_to_float(E, _) -> E. @@ -612,25 +606,11 @@ string_to_conses(Line, Cs, Tail) -> %% import(Line, Imports, State) -> %% State' -%% imported(Name, Arity, State) -> -%% {yes,Module} | no -%% Handle import declarations and test for imported functions. No need to -%% check when building imports as code is correct. +%% Handle import declarations. -import({Mod,Fs}, St) -> +import({Mod,Fs}, #expand{ctype=Ctype0}=St) -> true = is_atom(Mod), - Mfs = from_list(Fs), - St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)}. - -add_imports(Mod, [F|Fs], Is) -> - add_imports(Mod, Fs, orddict:store(F, Mod, Is)); -add_imports(_, [], Is) -> Is. - -imported(F, A, St) -> - case orddict:find({F,A}, St#expand.imports) of - {ok,Mod} -> {yes,Mod}; - error -> no - end. - -defined(F, A, St) -> - gb_sets:is_element({F,A}, St#expand.defined). + Ctype = foldl(fun(F, A) -> + A#{F=>{imported,Mod}} + end, Ctype0, Fs), + St#expand{ctype=Ctype}. diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index 47a357c23d..4df1aadd0a 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -43,7 +44,7 @@ -export([module/2]). -import(lists, [member/2,keymember/3,keysort/2,keydelete/3, - append/1,map/2,flatmap/2,filter/2,foldl/3,foldr/3,mapfoldl/3, + append/1,flatmap/2,filter/2,foldl/3,foldr/3,mapfoldl/3, sort/1,reverse/1,reverse/2]). -import(v3_life, [vdb_find/2]). @@ -57,8 +58,7 @@ break, %Break label recv, %Receive label is_top_block, %Boolean: top block or not - functable=gb_trees:empty(), %Gb tree of local functions: - % {{Name,Arity},Label} + functable=#{}, %Map of local functions: {Name,Arity}=>Label in_catch=false, %Inside a catch or not. need_frame, %Need a stack frame. ultimate_failure %Label for ultimate match failure. @@ -69,10 +69,8 @@ stk=[], %Stack table res=[]}). %Reserved regs: [{reserved,I,V}] -module({Mod,Exp,Attr,Forms}, Options) -> - put(?MODULE, Options), +module({Mod,Exp,Attr,Forms}, _Options) -> {Fs,St} = functions(Forms, {atom,Mod}), - erase(?MODULE), {ok,{Mod,Exp,Attr,Fs,St#cg.lcount}}. functions(Forms, AtomMod) -> @@ -123,24 +121,15 @@ cg_fun(Les, Hvs, Vdb, AtomMod, NameArity, Anno, St0) -> put_reg(V, Reg) end, [], Hvs), stk=[]}, 0, Vdb), - {B0,_Aft,St} = cg_list(Les, 0, Vdb, Bef, + {B,_Aft,St} = cg_list(Les, 0, Vdb, Bef, St3#cg{bfail=0, ultimate_failure=UltimateMatchFail, is_top_block=true}), - B = fix_bs_match_strings(B0), {Name,Arity} = NameArity, Asm = [{label,Fi},line(Anno),{func_info,AtomMod,{atom,Name},Arity}, {label,Fl}|B++[{label,UltimateMatchFail},if_end]], {Asm,Fl,St}. -fix_bs_match_strings([{test,bs_match_string,F,[Ctx,BinList]}|Is]) - when is_list(BinList) -> - I = {test,bs_match_string,F,[Ctx,list_to_bitstring(BinList)]}, - [I|fix_bs_match_strings(Is)]; -fix_bs_match_strings([I|Is]) -> - [I|fix_bs_match_strings(Is)]; -fix_bs_match_strings([]) -> []. - %% cg(Lkexpr, Vdb, StackReg, State) -> {[Ainstr],StackReg,State}. %% Generate code for a kexpr. %% Split function into two steps for clarity, not efficiency. @@ -210,7 +199,7 @@ need_heap_0([], H, Acc) -> need_heap_1(#l{ke={set,_,{binary,_}},i=I}, H) -> {need_heap_need(I, H),0}; -need_heap_1(#l{ke={set,_,{map,_,_}},i=I}, H) -> +need_heap_1(#l{ke={set,_,{map,_,_,_}},i=I}, H) -> {need_heap_need(I, H),0}; need_heap_1(#l{ke={set,_,Val}}, H) -> %% Just pass through adding to needed heap. @@ -586,7 +575,7 @@ top_level_block(Keis, Bef, MaxRegs, _St) -> (return) -> [{deallocate,FrameSz},return]; (Tuple) when is_tuple(Tuple) -> - [turn_yregs(tuple_size(Tuple), Tuple, MaxY)]; + [turn_yregs(Tuple, MaxY)]; (Other) -> [Other] end, Keis), @@ -598,14 +587,49 @@ top_level_block(Keis, Bef, MaxRegs, _St) -> %% catches work. The code generation algorithm gives a lower register %% number to the outer catch, which is wrong. -turn_yregs(0, Tp, _) -> Tp; -turn_yregs(El, Tp, MaxY) -> - turn_yregs(El-1,setelement(El,Tp,turn_yreg(element(El,Tp),MaxY)),MaxY). - -turn_yreg({yy,YY},MaxY) -> {y,MaxY-YY}; -turn_yreg({list,Ls},MaxY) -> {list, turn_yreg(Ls,MaxY)}; -turn_yreg(Ts,MaxY) when is_list(Ts) -> [turn_yreg(T,MaxY)||T<-Ts]; -turn_yreg(Other,_MaxY) -> Other. +turn_yregs({call,_,_}=I, _MaxY) -> I; +turn_yregs({call_ext,_,_}=I, _MaxY) -> I; +turn_yregs({jump,_}=I, _MaxY) -> I; +turn_yregs({label,_}=I, _MaxY) -> I; +turn_yregs({line,_}=I, _MaxY) -> I; +turn_yregs({test_heap,_,_}=I, _MaxY) -> I; +turn_yregs({bif,Op,F,A,B}, MaxY) -> + {bif,Op,F,turn_yreg(A, MaxY),turn_yreg(B, MaxY)}; +turn_yregs({gc_bif,Op,F,Live,A,B}, MaxY) when is_integer(Live) -> + {gc_bif,Op,F,Live,turn_yreg(A, MaxY),turn_yreg(B, MaxY)}; +turn_yregs({get_tuple_element,S,N,D}, MaxY) -> + {get_tuple_element,turn_yreg(S, MaxY),N,turn_yreg(D, MaxY)}; +turn_yregs({put_tuple,Arity,D}, MaxY) -> + {put_tuple,Arity,turn_yreg(D, MaxY)}; +turn_yregs({select_val,R,F,L}, MaxY) -> + {select_val,turn_yreg(R, MaxY),F,L}; +turn_yregs({test,Op,F,L}, MaxY) -> + {test,Op,F,turn_yreg(L, MaxY)}; +turn_yregs({test,Op,F,Live,A,B}, MaxY) when is_integer(Live) -> + {test,Op,F,Live,turn_yreg(A, MaxY),turn_yreg(B, MaxY)}; +turn_yregs({Op,A}, MaxY) -> + {Op,turn_yreg(A, MaxY)}; +turn_yregs({Op,A,B}, MaxY) -> + {Op,turn_yreg(A, MaxY),turn_yreg(B, MaxY)}; +turn_yregs({Op,A,B,C}, MaxY) -> + {Op,turn_yreg(A, MaxY),turn_yreg(B, MaxY),turn_yreg(C, MaxY)}; +turn_yregs(Tuple, MaxY) -> + turn_yregs(tuple_size(Tuple), Tuple, MaxY). + +turn_yregs(1, Tp, _) -> + Tp; +turn_yregs(N, Tp, MaxY) -> + E = turn_yreg(element(N, Tp), MaxY), + turn_yregs(N-1, setelement(N, Tp, E), MaxY). + +turn_yreg({yy,YY}, MaxY) -> + {y,MaxY-YY}; +turn_yreg({list,Ls},MaxY) -> + {list,turn_yreg(Ls, MaxY)}; +turn_yreg([_|_]=Ts, MaxY) -> + [turn_yreg(T, MaxY) || T <- Ts]; +turn_yreg(Other, _MaxY) -> + Other. %% select_cg(Sclause, V, TypeFail, ValueFail, StackReg, State) -> %% {Is,StackReg,State}. @@ -643,19 +667,13 @@ select_val_cg(tuple, R, [Arity,{f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) -> [{test,is_tuple,{f,Tf},[R]},{test,test_arity,{f,Vf},[R,Arity]}|Sis]; select_val_cg(tuple, R, Vls, Tf, Vf, Sis) -> [{test,is_tuple,{f,Tf},[R]},{select_tuple_arity,R,{f,Vf},{list,Vls}}|Sis]; -select_val_cg(map, R, [_Val,{f,Lbl}], Fail, Fail, [{label,Lbl}|Sis]) -> - [{test,is_map,{f,Fail},[R]}|Sis]; -select_val_cg(map, R, [_Val,{f,Lbl}|_], Tf, _Vf, [{label,Lbl}|Sis]) -> - [{test,is_map,{f,Tf},[R]}|Sis]; select_val_cg(Type, R, [Val, {f,Lbl}], Fail, Fail, [{label,Lbl}|Sis]) -> [{test,is_eq_exact,{f,Fail},[R,{Type,Val}]}|Sis]; select_val_cg(Type, R, [Val, {f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) -> [{test,select_type_test(Type),{f,Tf},[R]}, {test,is_eq_exact,{f,Vf},[R,{Type,Val}]}|Sis]; select_val_cg(Type, R, Vls0, Tf, Vf, Sis) -> - Vls1 = map(fun ({f,_Lbl} = F) -> F; - (Value) -> {Type,Value} - end, Vls0), + Vls1 = [case Value of {f,_Lbl} -> Value; _ -> {Type,Value} end || Value <- Vls0], [{test,select_type_test(Type),{f,Tf},[R]}, {select_val,R,{f,Vf},{list,Vls1}}|Sis]. select_type_test(integer) -> is_integer; @@ -688,22 +706,37 @@ select_nil(#l{ke={val_clause,nil,B}}, V, Tf, Vf, Bef, St0) -> select_binary(#l{ke={val_clause,{binary,{var,V}},B},i=I,vdb=Vdb}, V, Tf, Vf, Bef, St0) -> Int0 = clear_dead(Bef#sr{reg=Bef#sr.reg}, I, Vdb), - {Bis,Aft,St1} = match_cg(B, Vf, Int0, St0), + {Bis0,Aft,St1} = match_cg(B, Vf, Int0, St0), CtxReg = fetch_var(V, Int0), Live = max_reg(Bef#sr.reg), - {[{test,bs_start_match2,{f,Tf},Live,[CtxReg,V],CtxReg}, - {bs_save2,CtxReg,{V,V}}|Bis], - Aft,St1}; + Bis1 = [{test,bs_start_match2,{f,Tf},Live,[CtxReg,V],CtxReg}, + {bs_save2,CtxReg,{V,V}}|Bis0], + Bis = finish_select_binary(Bis1), + {Bis,Aft,St1}; select_binary(#l{ke={val_clause,{binary,{var,Ivar}},B},i=I,vdb=Vdb}, V, Tf, Vf, Bef, St0) -> Regs = put_reg(Ivar, Bef#sr.reg), Int0 = clear_dead(Bef#sr{reg=Regs}, I, Vdb), - {Bis,Aft,St1} = match_cg(B, Vf, Int0, St0), + {Bis0,Aft,St1} = match_cg(B, Vf, Int0, St0), CtxReg = fetch_var(Ivar, Int0), Live = max_reg(Bef#sr.reg), - {[{test,bs_start_match2,{f,Tf},Live,[fetch_var(V, Bef),Ivar],CtxReg}, - {bs_save2,CtxReg,{Ivar,Ivar}}|Bis], - Aft,St1}. + Bis1 = [{test,bs_start_match2,{f,Tf},Live,[fetch_var(V, Bef),Ivar],CtxReg}, + {bs_save2,CtxReg,{Ivar,Ivar}}|Bis0], + Bis = finish_select_binary(Bis1), + {Bis,Aft,St1}. + +finish_select_binary([{bs_save2,R,Point}=I,{bs_restore2,R,Point}|Is]) -> + [I|finish_select_binary(Is)]; +finish_select_binary([{bs_save2,R,Point}=I,{test,is_eq_exact,_,_}=Test, + {bs_restore2,R,Point}|Is]) -> + [I,Test|finish_select_binary(Is)]; +finish_select_binary([{test,bs_match_string,F,[Ctx,BinList]}|Is]) + when is_list(BinList) -> + I = {test,bs_match_string,F,[Ctx,list_to_bitstring(BinList)]}, + [I|finish_select_binary(Is)]; +finish_select_binary([I|Is]) -> + [I|finish_select_binary(Is)]; +finish_select_binary([]) -> []. %% New instructions for selection of binary segments. @@ -794,21 +827,24 @@ select_extract_bin([{var,Hd},{var,Tl}], Size0, Unit, Type, Flags, Vf, {bs_save2,CtxReg,{Ctx,Tl}}],Int1} end, {Es,clear_dead(Aft, I, Vdb),St}; -select_extract_bin([{var,Hd}], Size0, Unit, binary, Flags, Vf, +select_extract_bin([{var,Hd}], Size, Unit, binary, Flags, Vf, I, Vdb, Bef, Ctx, Body, St) -> - SizeReg = get_bin_size_reg(Size0, Bef), + %% Match the last segment of a binary. We KNOW that the size + %% must be 'all'. + Size = {atom,all}, %Assertion. {Es,Aft} = case vdb_find(Hd, Vdb) of {_,_,Lhd} when Lhd =< I -> + %% The result will not be used. Furthermore, since we + %% we are at the end of the binary, the position will + %% not be used again; thus, it is safe to do a cheaper + %% test of the unit. CtxReg = fetch_var(Ctx, Bef), - {case SizeReg =:= {atom,all} andalso is_context_unused(Body) of - true when Unit =:= 1 -> + {case Unit of + 1 -> []; - true -> - [{test,bs_test_unit,{f,Vf},[CtxReg,Unit]}]; - false -> - [{test,bs_skip_bits2,{f,Vf}, - [CtxReg,SizeReg,Unit,{field_flags,Flags}]}] + _ -> + [{test,bs_test_unit,{f,Vf},[CtxReg,Unit]}] end,Bef}; {_,_,_} -> case is_context_unused(Body) of @@ -820,7 +856,7 @@ select_extract_bin([{var,Hd}], Size0, Unit, binary, Flags, Vf, Name = bs_get_binary2, Live = max_reg(Bef#sr.reg), {[{test,Name,{f,Vf},Live, - [CtxReg,SizeReg,Unit,{field_flags,Flags}],Rhd}], + [CtxReg,Size,Unit,{field_flags,Flags}],Rhd}], Int1}; true -> %% Since the matching context will not be used again, @@ -835,7 +871,7 @@ select_extract_bin([{var,Hd}], Size0, Unit, binary, Flags, Vf, Name = bs_get_binary2, Live = max_reg(Int1#sr.reg), {[{test,Name,{f,Vf},Live, - [CtxReg,SizeReg,Unit,{field_flags,Flags}],CtxReg}], + [CtxReg,Size,Unit,{field_flags,Flags}],CtxReg}], Int1} end end, @@ -928,7 +964,7 @@ select_extract_tuple(Src, Vs, I, Vdb, Bef, St) -> select_map(Scs, V, Tf, Vf, Bef, St0) -> Reg = fetch_var(V, Bef), {Is,Aft,St1} = - match_fmf(fun(#l{ke={val_clause,{map,_,Es},B},i=I,vdb=Vdb}, Fail, St1) -> + match_fmf(fun(#l{ke={val_clause,{map,exact,_,Es},B},i=I,vdb=Vdb}, Fail, St1) -> select_map_val(V, Es, B, Fail, I, Vdb, Bef, St1) end, Vf, St0, Scs), {[{test,is_map,{f,Tf},[Reg]}|Is],Aft,St1}. @@ -947,27 +983,34 @@ select_extract_map(Src, Vs, Fail, I, Vdb, Bef, St) -> %% Assume keys are term-sorted Rsrc = fetch_var(Src, Bef), - {{HasKs,GetVs},Aft} = lists:foldr(fun - ({map_pair,Key,{var,V}},{{HasKsi,GetVsi},Int0}) -> + {{HasKs,GetVs,HasVarKs,GetVarVs},Aft} = lists:foldr(fun + ({map_pair,{var,K},{var,V}},{{HasKsi,GetVsi,HasVarVsi,GetVarVsi},Int0}) -> case vdb_find(V, Vdb) of {V,_,L} when L =< I -> - {{[Key|HasKsi],GetVsi},Int0}; + RK = fetch_var(K,Int0), + {{HasKsi,GetVsi,[RK|HasVarVsi],GetVarVsi},Int0}; _Other -> Reg1 = put_reg(V, Int0#sr.reg), Int1 = Int0#sr{reg=Reg1}, - {{HasKsi,[Key,fetch_reg(V, Reg1)|GetVsi]},Int1} + RK = fetch_var(K,Int0), + RV = fetch_reg(V,Reg1), + {{HasKsi,GetVsi,HasVarVsi,[[RK,RV]|GetVarVsi]},Int1} + end; + ({map_pair,Key,{var,V}},{{HasKsi,GetVsi,HasVarVsi,GetVarVsi},Int0}) -> + case vdb_find(V, Vdb) of + {V,_,L} when L =< I -> + {{[Key|HasKsi],GetVsi,HasVarVsi,GetVarVsi},Int0}; + _Other -> + Reg1 = put_reg(V, Int0#sr.reg), + Int1 = Int0#sr{reg=Reg1}, + {{HasKsi,[Key,fetch_reg(V, Reg1)|GetVsi],HasVarVsi,GetVarVsi},Int1} end - end, {{[],[]},Bef}, Vs), - - Code = case {HasKs,GetVs} of - {HasKs,[]} -> - [{test,has_map_fields,{f,Fail},Rsrc,{list,HasKs}}]; - {[],GetVs} -> - [{get_map_elements, {f,Fail},Rsrc,{list,GetVs}}]; - {HasKs,GetVs} -> - [{test,has_map_fields,{f,Fail},Rsrc,{list,HasKs}}, - {get_map_elements, {f,Fail},Rsrc,{list,GetVs}}] - end, + end, {{[],[],[],[]},Bef}, Vs), + + Code = [{test,has_map_fields,{f,Fail},Rsrc,{list,HasKs}} || HasKs =/= []] ++ + [{test,has_map_fields,{f,Fail},Rsrc,{list,[K]}} || K <- HasVarKs] ++ + [{get_map_elements, {f,Fail},Rsrc,{list,GetVs}} || GetVs =/= []] ++ + [{get_map_elements, {f,Fail},Rsrc,{list,[K,V]}} || [K,V] <- GetVarVs], {Code, Aft, St}. @@ -1038,7 +1081,7 @@ protected_cg(Ts, Rs, _Fail, I, Vdb, Bef, St0) -> St2#cg{bfail=Pfail}), %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{Rs,I,Vdb,Aft}]), %% Set return values to false. - Mis = map(fun ({var,V}) -> {move,{atom,false},fetch_var(V, Aft)} end, Rs), + Mis = [{move,{atom,false},fetch_var(V,Aft)}||{var,V} <- Rs], {Tis ++ [{jump,{f,Psucc}}, {label,Pfail}] ++ Mis ++ [{label,Psucc}], Aft,St3#cg{bfail=St0#cg.bfail}}. @@ -1046,6 +1089,23 @@ protected_cg(Ts, Rs, _Fail, I, Vdb, Bef, St0) -> %% test_cg(TestName, Args, Fail, I, Vdb, Bef, St) -> {[Ainstr],Aft,St}. %% Generate test instruction. Use explicit fail label here. +test_cg(is_map, [A], Fail, I, Vdb, Bef, St) -> + %% We must avoid creating code like this: + %% + %% move x(0) y(0) + %% is_map Fail [x(0)] + %% make_fun => x(0) %% Overwrite x(0) + %% put_map_assoc y(0) ... + %% + %% The code is safe, but beam_validator does not understand that. + %% Extending beam_validator to handle such (rare) code as the + %% above would make it slower for all programs. Instead, change + %% the code generator to always prefer the Y register for is_map() + %% and put_map_assoc() instructions, ensuring that they use the + %% same register. + Arg = cg_reg_arg_prefer_y(A, Bef), + Aft = clear_dead(Bef, I, Vdb), + {[{test,is_map,{f,Fail},[Arg]}],Aft,St}; test_cg(Test, As, Fail, I, Vdb, Bef, St) -> Args = cg_reg_args(As, Bef), Aft = clear_dead(Bef, I, Vdb), @@ -1112,19 +1172,15 @@ call_cg(Func, As, Rs, Le, Vdb, Bef, St0) -> %% Inside a guard. The only allowed function call is to %% erlang:error/1,2. We will generate the following code: %% - %% jump FailureLabel %% move {atom,ok} DestReg - %% - %% The 'move' instruction will never be executed, but we - %% generate it anyway in case the beam_validator is run - %% on unoptimized code. + %% jump FailureLabel {remote,{atom,erlang},{atom,error}} = Func, %Assertion. [{var,DestVar}] = Rs, Int0 = clear_dead(Bef, Le#l.i, Vdb), Reg = put_reg(DestVar, Int0#sr.reg), Int = Int0#sr{reg=Reg}, Dst = fetch_reg(DestVar, Reg), - {[{jump,{f,Fail}},{move,{atom,ok},Dst}], + {[{move,{atom,ok},Dst},{jump,{f,Fail}}], clear_dead(Int, Le#l.i, Vdb),St0}; #cg{} -> %% Ordinary function call in a function body. @@ -1221,13 +1277,12 @@ enter_line(_, _, _) -> local_func_label(Name, Arity, St) -> local_func_label({Name,Arity}, St). -local_func_label(Key, #cg{functable=Tab}=St0) -> - case gb_trees:lookup(Key, Tab) of - {value,Label} -> - {Label,St0}; - none -> +local_func_label(Key, #cg{functable=Map}=St0) -> + case Map of + #{Key := Label} -> {Label,St0}; + _ -> {Label,St} = new_label(St0), - {Label,St#cg{functable=gb_trees:insert(Key, Label, Tab)}} + {Label,St#cg{functable=Map#{Key => Label}}} end. %% need_stack_frame(State) -> State' @@ -1288,12 +1343,13 @@ bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) -> %% that we save any variable that will be live after this BIF call. MayFail = not erl_bifs:is_safe(erlang, Bif, length(As)), - {Sis,Int0} = case St0#cg.in_catch andalso - St0#cg.bfail =:= 0 andalso - MayFail of - true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb); - false -> {[],Bef} - end, + {Sis,Int0} = + case MayFail of + true -> + maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0); + false -> + {[],Bef} + end, Int1 = clear_dead(Int0, Le#l.i, Vdb), Reg = put_reg(V, Int1#sr.reg), Int = Int1#sr{reg=Reg}, @@ -1324,11 +1380,7 @@ gc_bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) -> %% Currently, we are somewhat pessimistic in %% that we save any variable that will be live after this BIF call. - {Sis,Int0} = - case St0#cg.in_catch andalso St0#cg.bfail =:= 0 of - true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb); - false -> {[],Bef} - end, + {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0), Int1 = clear_dead(Int0, Le#l.i, Vdb), Reg = put_reg(V, Int1#sr.reg), @@ -1473,8 +1525,7 @@ set_cg([{var,R}], {cons,Es}, Le, Vdb, Bef, St) -> Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)}, Ret = fetch_reg(R, Int1#sr.reg), {[{put_list,S1,S2,Ret}], Int1, St}; -set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, - #cg{in_catch=InCatch, bfail=Bfail}=St) -> +set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, #cg{bfail=Bfail}=St) -> %% At run-time, binaries are constructed in three stages: %% 1) First the size of the binary is calculated. %% 2) Then the binary is allocated. @@ -1493,46 +1544,58 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, %% First generate the code that constructs each field. Fail = {f,Bfail}, PutCode = cg_bin_put(Segs, Fail, Bef), - {Sis,Int1} = - case InCatch of - true -> adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb); - false -> {[],Int0} - end, + {Sis,Int1} = maybe_adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb, St), MaxRegs = max_reg(Bef#sr.reg), Aft = clear_dead(Int1, Le#l.i, Vdb), %% Now generate the complete code for constructing the binary. Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a), {Sis++Code,Aft,St}; -set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef, - #cg{in_catch=InCatch,bfail=Bfail}=St) -> - Fail = {f,Bfail}, - {Sis,Int0} = - case InCatch of - true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb); - false -> {[],Bef} - end, - SrcReg = cg_reg_arg(Map,Int0), +%% Map: single variable key. +set_cg([{var,R}], {map,Op,Map,[{map_pair,{var,_}=K,V}]}, Le, Vdb, Bef, St0) -> + {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0), + + SrcReg = cg_reg_arg_prefer_y(Map, Int0), Line = line(Le#l.a), - %% The instruction needs to store keys in term sorted order - %% All keys has to be unique here - Pairs = map_pair_strip_and_termsort(Es), + List = [cg_reg_arg(K,Int0),cg_reg_arg(V,Int0)], + + Live = max_reg(Bef#sr.reg), + + %% The target register can reuse one of the source registers. + Aft0 = clear_dead(Int0, Le#l.i, Vdb), + Aft = Aft0#sr{reg=put_reg(R, Aft0#sr.reg)}, + Target = fetch_reg(R, Aft#sr.reg), + + {Is,St1} = set_cg_map(Line, Op, SrcReg, Target, Live, List, St0), + {Sis++Is,Aft,St1}; + +%% Map: (possibly) multiple literal keys. +set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef, St0) -> + + %% assert key literals + [] = [Var||{map_pair,{var,_}=Var,_} <- Es], + + {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0), + SrcReg = cg_reg_arg_prefer_y(Map, Int0), + Line = line(Le#l.a), %% fetch registers for values to be put into the map + Pairs = [{K,V} || {_,K,V} <- Es], List = flatmap(fun({K,V}) -> [K,cg_reg_arg(V,Int0)] end, Pairs), Live = max_reg(Bef#sr.reg), - Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)}, - Aft = clear_dead(Int1, Le#l.i, Vdb), - Target = fetch_reg(R, Int1#sr.reg), - I = case Op of - assoc -> put_map_assoc; - exact -> put_map_exact - end, - {Sis++[Line]++[{I,Fail,SrcReg,Target,Live,{list,List}}],Aft,St}; + %% The target register can reuse one of the source registers. + Aft0 = clear_dead(Int0, Le#l.i, Vdb), + Aft = Aft0#sr{reg=put_reg(R, Aft0#sr.reg)}, + Target = fetch_reg(R, Aft#sr.reg), + + {Is,St1} = set_cg_map(Line, Op, SrcReg, Target, Live, List, St0), + {Sis++Is,Aft,St1}; + +%% Everything else. set_cg([{var,R}], Con, Le, Vdb, Bef, St) -> %% Find a place for the return register first. Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)}, @@ -1545,15 +1608,33 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) -> end, {Ais,clear_dead(Int, Le#l.i, Vdb),St}. -map_pair_strip_and_termsort(Es) -> - %% format in - %% [{map_pair,K,V}] - %% where K is for example {integer, 1} and we want to sort on 1. - Ls = [{K,V}||{_,K,V}<-Es], - lists:sort(fun ({{_,A},_}, {{_,B},_}) -> erts_internal:cmp_term(A,B) =< 0; - ({nil,_}, {{_,B},_}) -> [] =< B; - ({{_,A},_}, {nil,_}) -> A =< [] - end, Ls). + +set_cg_map(Line, Op0, SrcReg, Target, Live, List, St0) -> + Bfail = St0#cg.bfail, + Fail = {f,St0#cg.bfail}, + Op = case Op0 of + assoc -> put_map_assoc; + exact -> put_map_exact + end, + {OkLbl,St1} = new_label(St0), + {BadLbl,St2} = new_label(St1), + Is = if + Bfail =:= 0 orelse Op =:= put_map_assoc -> + [Line,{Op,{f,0},SrcReg,Target,Live,{list,List}}]; + true -> + %% Ensure that Target is always set, even if + %% the map update operation fails. That is necessary + %% because Target may be included in a test_heap + %% instruction. + [Line, + {Op,{f,BadLbl},SrcReg,Target,Live,{list,List}}, + {jump,{f,OkLbl}}, + {label,BadLbl}, + {move,{atom,ok},Target}, + {jump,Fail}, + {label,OkLbl}] + end, + {Is,St2}. %%% %%% Code generation for constructing binaries. @@ -1797,6 +1878,9 @@ cg_reg_args(As, Bef) -> [cg_reg_arg(A, Bef) || A <- As]. cg_reg_arg({var,V}, Bef) -> fetch_var(V, Bef); cg_reg_arg(Literal, _) -> Literal. +cg_reg_arg_prefer_y({var,V}, Bef) -> fetch_var_prefer_y(V, Bef); +cg_reg_arg_prefer_y(Literal, _) -> Literal. + %% cg_setup_call([Arg], Bef, Cur, Vdb) -> {[Instr],Aft}. %% Do the complete setup for a call/enter. @@ -1929,25 +2013,28 @@ clear_dead(Sr, Until, Vdb) -> stk=clear_dead_stk(Sr#sr.stk, Until, Vdb)}. clear_dead_reg(Sr, Until, Vdb) -> - Reg = map(fun ({_I,V} = IV) -> - case vdb_find(V, Vdb) of - {V,_,L} when L > Until -> IV; - _ -> free %Remove anything else - end; - ({reserved,_I,_V} = Reserved) -> Reserved; - (free) -> free - end, Sr#sr.reg), + Reg = [case R of + {_I,V} = IV -> + case vdb_find(V, Vdb) of + {V,_,L} when L > Until -> IV; + _ -> free %Remove anything else + end; + {reserved,_I,_V} = Reserved -> Reserved; + free -> free + end || R <- Sr#sr.reg], reserve(Sr#sr.res, Reg, Sr#sr.stk). clear_dead_stk(Stk, Until, Vdb) -> - map(fun ({V} = T) -> - case vdb_find(V, Vdb) of - {V,_,L} when L > Until -> T; - _ -> dead %Remove anything else - end; - (free) -> free; - (dead) -> dead - end, Stk). + [case S of + {V} = T -> + case vdb_find(V, Vdb) of + {V,_,L} when L > Until -> T; + _ -> dead %Remove anything else + end; + free -> free; + dead -> dead + end || S <- Stk]. + %% sr_merge(Sr1, Sr2) -> Sr. %% Merge two stack/register states keeping the longest of both stack @@ -1975,6 +2062,19 @@ trim_free([R|Rs0]) -> end; trim_free([]) -> []. +%% maybe_adjust_stack(Bef, FirstBefore, LastFrom, Vdb, St) -> {[Ainstr],Aft}. +%% Adjust the stack, but only if the code is inside a catch and not +%% inside a guard. Use this funtion before instructions that may +%% cause an exception. + +maybe_adjust_stack(Bef, Fb, Lf, Vdb, St) -> + case St of + #cg{in_catch=true,bfail=0} -> + adjust_stack(Bef, Fb, Lf, Vdb); + #cg{} -> + {[],Bef} + end. + %% adjust_stack(Bef, FirstBefore, LastFrom, Vdb) -> {[Ainstr],Aft}. %% Do complete stack adjustment by compressing stack and adding %% variables to be saved. Try to optimise ordering on stack by @@ -2022,6 +2122,12 @@ fetch_var(V, Sr) -> error -> fetch_stack(V, Sr#sr.stk) end. +fetch_var_prefer_y(V, #sr{reg=Reg,stk=Stk}) -> + case find_stack(V, Stk) of + {ok,R} -> R; + error -> fetch_reg(V, Reg) + end. + load_vars(Vs, Regs) -> foldl(fun ({var,V}, Rs) -> put_reg(V, Rs) end, Regs, Vs). @@ -2077,9 +2183,11 @@ put_stack(Val, [free|Stk]) -> [{Val}|Stk]; put_stack(Val, [NotFree|Stk]) -> [NotFree|put_stack(Val, Stk)]. put_stack_carefully(Val, Stk0) -> - case catch put_stack_carefully1(Val, Stk0) of - error -> error; - Stk1 when is_list(Stk1) -> Stk1 + try + put_stack_carefully1(Val, Stk0) + catch + throw:error -> + error end. put_stack_carefully1(_, []) -> throw(error); @@ -2093,11 +2201,11 @@ fetch_stack(Var, Stk) -> fetch_stack(Var, Stk, 0). fetch_stack(V, [{V}|_], I) -> {yy,I}; fetch_stack(V, [_|Stk], I) -> fetch_stack(V, Stk, I+1). -% find_stack(Var, Stk) -> find_stack(Var, Stk, 0). +find_stack(Var, Stk) -> find_stack(Var, Stk, 0). -% find_stack(V, [{V}|Stk], I) -> {ok,{yy,I}}; -% find_stack(V, [O|Stk], I) -> find_stack(V, Stk, I+1); -% find_stack(V, [], I) -> error. +find_stack(V, [{V}|_], I) -> {ok,{yy,I}}; +find_stack(V, [_|Stk], I) -> find_stack(V, Stk, I+1); +find_stack(_, [], _) -> error. on_stack(V, Stk) -> keymember(V, 1, Stk). diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 59ec0d4199..d71411de80 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2014. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -66,6 +67,7 @@ %% match arguments are novars %% case arguments are novars %% receive timeouts are novars +%% binaries and maps are novars %% let/set arguments are expressions %% fun is not a safe @@ -77,13 +79,11 @@ splitwith/2,keyfind/3,sort/1,foreach/2,droplast/1,last/1]). -import(ordsets, [add_element/2,del_element/2,is_element/2, union/1,union/2,intersection/2,subtract/2]). --import(cerl, [ann_c_cons/3,ann_c_cons_skel/3,ann_c_tuple/2,c_tuple/1, - ann_c_map/2, ann_c_map/3]). +-import(cerl, [ann_c_cons/3,ann_c_tuple/2,c_tuple/1, + ann_c_map/3]). -include("core_parse.hrl"). --define(REC_OFFSET, 100000000). % Also in erl_expand_records. - %% Internal core expressions and help functions. %% N.B. annotations fields in place as normal Core expressions. @@ -105,7 +105,9 @@ -record(iset, {anno=#a{},var,arg}). -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}). +-record(igen, {anno=#a{},ceps=[],acc_pat,acc_guard, + skip_pat,tail,tail_pat,arg}). +-record(isimple, {anno=#a{},term :: cerl:cerl()}). -type iapply() :: #iapply{}. -type ibinary() :: #ibinary{}. @@ -124,11 +126,12 @@ -type itry() :: #itry{}. -type ifilter() :: #ifilter{}. -type igen() :: #igen{}. +-type isimple() :: #isimple{}. -type i() :: iapply() | ibinary() | icall() | icase() | icatch() | iclause() | ifun() | iletrec() | imatch() | iprimop() | iprotect() | ireceive1() | ireceive2() | iset() | itry() - | ifilter() | igen(). + | ifilter() | igen() | isimple(). -type warning() :: {file:filename(), [{integer(), module(), term()}]}. @@ -166,63 +169,86 @@ form({attribute,_,file,{File,_Line}}, {Fs,As,Ws,_}, _Opts) -> form({attribute,_,_,_}=F, {Fs,As,Ws,File}, _Opts) -> {Fs,[attribute(F)|As],Ws,File}. -attribute({attribute,Line,Name,Val}) -> - {#c_literal{val=Name, anno=[Line]}, #c_literal{val=Val, anno=[Line]}}. +attribute(Attribute) -> + Fun = fun(A) -> [erl_anno:location(A)] end, + {attribute,Line,Name,Val} = erl_parse:map_anno(Fun, Attribute), + {#c_literal{val=Name, anno=Line}, #c_literal{val=Val, anno=Line}}. + +%% function_dump(module_info,_,_,_) -> ok; +%% function_dump(Name,Arity,Format,Terms) -> +%% io:format("~w/~w " ++ Format,[Name,Arity]++Terms), +%% ok. function({function,_,Name,Arity,Cs0}, Ws0, File, Opts) -> - %%ok = io:fwrite("~p - ", [{Name,Arity}]), St0 = #core{vcount=0,opts=Opts,ws=Ws0,file=[{file,File}]}, {B0,St1} = body(Cs0, Name, Arity, St0), - %%ok = io:fwrite("1", []), - %%ok = io:fwrite("~w:~p~n", [?LINE,B0]), + %% ok = function_dump(Name,Arity,"body:~n~p~n",[B0]), {B1,St2} = ubody(B0, St1), - %%ok = io:fwrite("2", []), - %%ok = io:fwrite("~w:~p~n", [?LINE,B1]), + %% ok = function_dump(Name,Arity,"ubody:~n~p~n",[B1]), {B2,#core{ws=Ws}} = cbody(B1, St2), - %%ok = io:fwrite("3~n", []), - %%ok = io:fwrite("~w:~p~n", [?LINE,B2]), + %% ok = function_dump(Name,Arity,"cbody:~n~p~n",[B2]), {{#c_var{name={Name,Arity}},B2},Ws}. body(Cs0, Name, Arity, St0) -> Anno = lineno_anno(element(2, hd(Cs0)), St0), {Args,St1} = new_vars(Anno, Arity, St0), - {Cs1,St2} = clauses(Cs0, St1), - {Ps,St3} = new_vars(Arity, St2), %Need new variables here - Fc = function_clause(Ps, Anno, {Name,Arity}), - {#ifun{anno=#a{anno=Anno},id=[],vars=Args,clauses=Cs1,fc=Fc},St3}. + case clauses(Cs0, St1) of + {Cs1,[],St2} -> + {Ps,St3} = new_vars(Arity, St2), %Need new variables here + Fc = function_clause(Ps, Anno, {Name,Arity}), + {#ifun{anno=#a{anno=Anno},id=[],vars=Args,clauses=Cs1,fc=Fc},St3}; + {Cs1,Eps,St2} -> + %% We have pre-expressions from patterns and + %% these needs to be letified before matching + %% since only bound variables are allowed + AnnoGen = #a{anno=[compiler_generated]}, + {Ps1,St3} = new_vars(Arity, St2), %Need new variables here + Fc1 = function_clause(Ps1, Anno, {Name,Arity}), + {Ps2,St4} = new_vars(Arity, St3), %Need new variables here + Fc2 = function_clause(Ps2, Anno, {Name,Arity}), + Case = #icase{anno=AnnoGen,args=Args, + clauses=Cs1, + fc=Fc2}, + {#ifun{anno=#a{anno=Anno},id=[],vars=Args, + clauses=[#iclause{anno=AnnoGen,pats=Ps1, + guard=[#c_literal{val=true}], + body=Eps ++ [Case]}], + fc=Fc1},St4} + end. %% clause(Clause, State) -> {Cclause,State} | noclause. %% clauses([Clause], State) -> {[Cclause],State}. %% Convert clauses. Trap bad pattern aliases and remove clause from %% clause list. -clauses([C0|Cs0], St0) -> +clauses([C0|Cs0],St0) -> case clause(C0, St0) of - {noclause,St} -> clauses(Cs0, St); - {C,St1} -> - {Cs,St2} = clauses(Cs0, St1), - {[C|Cs],St2} + {noclause,_,St} -> clauses(Cs0,St); + {C,Eps1,St1} -> + {Cs,Eps2,St2} = clauses(Cs0, St1), + {[C|Cs],Eps1++Eps2,St2} end; -clauses([], St) -> {[],St}. +clauses([],St) -> {[],[],St}. clause({clause,Lc,H0,G0,B0}, St0) -> try head(H0, St0) of - H1 -> - {G1,St1} = guard(G0, St0), - {B1,St2} = exprs(B0, St1), - Anno = lineno_anno(Lc, St2), - {#iclause{anno=#a{anno=Anno},pats=H1,guard=G1,body=B1},St2} + {H1,Eps,St1} -> + {G1,St2} = guard(G0, St1), + {B1,St3} = exprs(B0, St2), + Anno = lineno_anno(Lc, St3), + {#iclause{anno=#a{anno=Anno},pats=H1,guard=G1,body=B1},Eps,St3} catch throw:nomatch -> St = add_warning(Lc, nomatch, St0), - {noclause,St} %Bad pattern + {noclause,[],St} %Bad pattern end. clause_arity({clause,_,H0,_,_}) -> length(H0). -%% head([P], State) -> [P]. +%% head([P], State) -> {[P],[Cexpr],State}. -head(Ps, St) -> pattern_list(Ps, St). +head(Ps, St) -> + pattern_list(Ps, St). %% guard([Expr], State) -> {[Cexpr],State}. %% Build an explict and/or tree of guard alternatives, then traverse @@ -266,13 +292,15 @@ gexpr({protect,Line,Arg}, Bools0, St0) -> {#iprotect{anno=#a{anno=Anno},body=Eps++[E]},[],Bools0,St} end; gexpr({op,L,'andalso',E1,E2}, Bools, St0) -> - {#c_var{name=V0},St} = new_var(L, St0), + Anno = lineno_anno(L, St0), + {#c_var{name=V0},St} = new_var(Anno, St0), V = {var,L,V0}, False = {atom,L,false}, E = make_bool_switch_guard(L, E1, V, E2, False), gexpr(E, Bools, St); gexpr({op,L,'orelse',E1,E2}, Bools, St0) -> - {#c_var{name=V0},St} = new_var(L, St0), + Anno = lineno_anno(L, St0), + {#c_var{name=V0},St} = new_var(Anno, St0), V = {var,L,V0}, True = {atom,L,true}, E = make_bool_switch_guard(L, E1, V, True, E2), @@ -361,33 +389,30 @@ gexpr_test(E0, Bools0, St0) -> Lanno = Anno#a.anno, {New,St2} = new_var(Lanno, St1), Bools = [New|Bools0], - {#icall{anno=Anno, %Must have an #a{} - module=#c_literal{anno=Lanno,val=erlang}, - name=#c_literal{anno=Lanno,val='=:='}, - args=[New,#c_literal{anno=Lanno,val=true}]}, + {icall_eq_true(New), Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2} end; _ -> - Anno = get_ianno(E1), Lanno = get_lineno_anno(E1), + ACompGen = #a{anno=[compiler_generated]}, case is_simple(E1) of true -> Bools = [E1|Bools0], - {#icall{anno=Anno, %Must have an #a{} - module=#c_literal{anno=Lanno,val=erlang}, - name=#c_literal{anno=Lanno,val='=:='}, - args=[E1,#c_literal{anno=Lanno,val=true}]},Eps0,Bools,St1}; + {icall_eq_true(E1),Eps0,Bools,St1}; false -> {New,St2} = new_var(Lanno, St1), Bools = [New|Bools0], - {#icall{anno=Anno, %Must have an #a{} - module=#c_literal{anno=Lanno,val=erlang}, - name=#c_literal{anno=Lanno,val='=:='}, - args=[New,#c_literal{anno=Lanno,val=true}]}, - Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2} + {icall_eq_true(New), + Eps0 ++ [#iset{anno=ACompGen,var=New,arg=E1}],Bools,St2} end end. +icall_eq_true(Arg) -> + #icall{anno=#a{anno=[compiler_generated]}, + module=#c_literal{val=erlang}, + name=#c_literal{val='=:='}, + args=[Arg,#c_literal{val=true}]}. + force_booleans(Vs0, E, Eps, St) -> Vs1 = [set_anno(V, []) || V <- Vs0], Vs = unforce(E, Eps, Vs1), @@ -397,16 +422,15 @@ force_booleans_1([], E, Eps, St) -> {E,Eps,St}; force_booleans_1([V|Vs], E0, Eps0, St0) -> {E1,Eps1,St1} = force_safe(E0, St0), - Lanno = element(2, V), - Anno = #a{anno=Lanno}, - Call = #icall{anno=Anno,module=#c_literal{anno=Lanno,val=erlang}, - name=#c_literal{anno=Lanno,val=is_boolean}, + ACompGen = #a{anno=[compiler_generated]}, + Call = #icall{anno=ACompGen,module=#c_literal{val=erlang}, + name=#c_literal{val=is_boolean}, args=[V]}, - {New,St} = new_var(Lanno, St1), - Iset = #iset{anno=Anno,var=New,arg=Call}, + {New,St} = new_var([], St1), + Iset = #iset{var=New,arg=Call}, Eps = Eps0 ++ Eps1 ++ [Iset], - E = #icall{anno=Anno, - module=#c_literal{anno=Lanno,val=erlang},name=#c_literal{anno=Lanno,val='and'}, + E = #icall{anno=ACompGen, + module=#c_literal{val=erlang},name=#c_literal{val='and'}, args=[E1,New]}, force_booleans_1(Vs, E, Eps, St). @@ -445,7 +469,8 @@ unforce_tree([#iset{var=#c_var{name=V},arg=Arg0}|Es], D0) -> unforce_tree(Es, D); unforce_tree([#icall{}=Call], D) -> unforce_tree_subst(Call, D); -unforce_tree([Top], _) -> Top. +unforce_tree([#c_var{name=V}], D) -> + gb_trees:get(V, D). unforce_tree_subst(#icall{module=#c_literal{val=erlang}, name=#c_literal{val='=:='}, @@ -493,58 +518,32 @@ exprs([], St) -> {[],St}. %% Generate an internal core expression. expr({var,L,V}, St) -> {#c_var{anno=lineno_anno(L, St),name=V},[],St}; -expr({char,L,C}, St) -> {#c_literal{anno=lineno_anno(L, St),val=C},[],St}; -expr({integer,L,I}, St) -> {#c_literal{anno=lineno_anno(L, St),val=I},[],St}; -expr({float,L,F}, St) -> {#c_literal{anno=lineno_anno(L, St),val=F},[],St}; -expr({atom,L,A}, St) -> {#c_literal{anno=lineno_anno(L, St),val=A},[],St}; -expr({nil,L}, St) -> {#c_literal{anno=lineno_anno(L, St),val=[]},[],St}; -expr({string,L,S}, St) -> {#c_literal{anno=lineno_anno(L, St),val=S},[],St}; +expr({char,L,C}, St) -> {#c_literal{anno=full_anno(L, St),val=C},[],St}; +expr({integer,L,I}, St) -> {#c_literal{anno=full_anno(L, St),val=I},[],St}; +expr({float,L,F}, St) -> {#c_literal{anno=full_anno(L, St),val=F},[],St}; +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 = lineno_anno(L, St2), + A = full_anno(L, St2), {annotate_cons(A, H1, T1, St2),Hps ++ Tps,St2}; 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); expr({bc,L,E,Qs}, St) -> - bc_tq(L, E, Qs, {nil,L}, St); + bc_tq(L, E, Qs, St); expr({tuple,L,Es0}, St0) -> {Es1,Eps,St1} = safe_list(Es0, St0), A = record_anno(L, St1), {annotate_tuple(A, Es1, St1),Eps,St1}; expr({map,L,Es0}, St0) -> - % erl_lint should make sure only #{ K => V } are allowed - % in map construction. - try map_pair_list(Es0, St0) of - {Es1,Eps,St1} -> - A = lineno_anno(L, St1), - {ann_c_map(A,Es1),Eps,St1} - catch - throw:{bad_map,Warning} -> - St = add_warning(L, Warning, St0), - LineAnno = lineno_anno(L, St), - As = [#c_literal{anno=LineAnno,val=badarg}], - {#icall{anno=#a{anno=LineAnno}, %Must have an #a{} - module=#c_literal{anno=LineAnno,val=erlang}, - name=#c_literal{anno=LineAnno,val=error}, - args=As},[],St} - end; -expr({map,L,M0,Es0}, St0) -> - try expr_map(M0,Es0,lineno_anno(L, St0),St0) of - {_,_,_}=Res -> Res - catch - throw:{bad_map,Warning} -> - St = add_warning(L, Warning, St0), - LineAnno = lineno_anno(L, St), - As = [#c_literal{anno=LineAnno,val=badarg}], - {#icall{anno=#a{anno=LineAnno}, %Must have an #a{} - module=#c_literal{anno=LineAnno,val=erlang}, - name=#c_literal{anno=LineAnno,val=error}, - args=As},[],St} - end; + map_build_pairs(#c_literal{val=#{}}, Es0, full_anno(L, St0), St0); +expr({map,L,M,Es}, St) -> + expr_map(M, Es, L, St); expr({bin,L,Es0}, St0) -> - try expr_bin(Es0, lineno_anno(L, St0), St0) of + try expr_bin(Es0, full_anno(L, St0), St0) of {_,_,_}=Res -> Res catch throw:bad_binary -> @@ -562,26 +561,26 @@ expr({block,_,Es0}, St0) -> {E1,Eps,St2} = expr(last(Es0), St1), {E1,Es1 ++ Eps,St2}; expr({'if',L,Cs0}, St0) -> - {Cs1,St1} = clauses(Cs0, St0), + {Cs1,Ceps,St1} = clauses(Cs0, St0), Lanno = lineno_anno(L, St1), Fc = fail_clause([], Lanno, #c_literal{val=if_clause}), - {#icase{anno=#a{anno=Lanno},args=[],clauses=Cs1,fc=Fc},[],St1}; + {#icase{anno=#a{anno=Lanno},args=[],clauses=Cs1,fc=Fc},Ceps,St1}; expr({'case',L,E0,Cs0}, St0) -> {E1,Eps,St1} = novars(E0, St0), - {Cs1,St2} = clauses(Cs0, St1), + {Cs1,Ceps,St2} = clauses(Cs0, St1), {Fpat,St3} = new_var(St2), Lanno = lineno_anno(L, St2), Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=case_clause},Fpat])), - {#icase{anno=#a{anno=Lanno},args=[E1],clauses=Cs1,fc=Fc},Eps,St3}; + {#icase{anno=#a{anno=Lanno},args=[E1],clauses=Cs1,fc=Fc},Eps++Ceps,St3}; expr({'receive',L,Cs0}, St0) -> - {Cs1,St1} = clauses(Cs0, St0), - {#ireceive1{anno=#a{anno=lineno_anno(L, St1)},clauses=Cs1}, [], St1}; + {Cs1,Ceps,St1} = clauses(Cs0, St0), + {#ireceive1{anno=#a{anno=lineno_anno(L, St1)},clauses=Cs1},Ceps, St1}; expr({'receive',L,Cs0,Te0,Tes0}, St0) -> {Te1,Teps,St1} = novars(Te0, St0), {Tes1,St2} = exprs(Tes0, St1), - {Cs1,St3} = clauses(Cs0, St2), + {Cs1,Ceps,St3} = clauses(Cs0, St2), {#ireceive2{anno=#a{anno=lineno_anno(L, St3)}, - clauses=Cs1,timeout=Te1,action=Tes1},Teps,St3}; + clauses=Cs1,timeout=Te1,action=Tes1},Teps++Ceps,St3}; expr({'try',L,Es0,[],Ecs,[]}, St0) -> %% 'try ... catch ... end' {Es1,St1} = exprs(Es0, St0), @@ -595,7 +594,7 @@ expr({'try',L,Es0,Cs0,Ecs,[]}, St0) -> %% 'try ... of ... catch ... end' {Es1,St1} = exprs(Es0, St0), {V,St2} = new_var(St1), %This name should be arbitrary - {Cs1,St3} = clauses(Cs0, St2), + {Cs1,Ceps,St3} = clauses(Cs0, St2), {Fpat,St4} = new_var(St3), Lanno = lineno_anno(L, St4), Fc = fail_clause([Fpat], Lanno, @@ -604,7 +603,7 @@ expr({'try',L,Es0,Cs0,Ecs,[]}, St0) -> {#itry{anno=#a{anno=lineno_anno(L, St5)},args=Es1, vars=[V],body=[#icase{anno=#a{anno=Lanno},args=[V],clauses=Cs1,fc=Fc}], evars=Evs,handler=Hs}, - [],St5}; + Ceps,St5}; expr({'try',L,Es0,[],[],As0}, St0) -> %% 'try ... after ... end' {Es1,St1} = exprs(Es0, St0), @@ -634,11 +633,11 @@ expr({'catch',L,E0}, St0) -> Lanno = lineno_anno(L, St1), {#icatch{anno=#a{anno=Lanno},body=Eps ++ [E1]},[],St1}; expr({'fun',L,{function,F,A},{_,_,_}=Id}, St) -> - Lanno = lineno_anno(L, St), + Lanno = full_anno(L, St), {#c_var{anno=Lanno++[{id,Id}],name={F,A}},[],St}; expr({'fun',L,{function,M,F,A}}, St0) -> {As,Aps,St1} = safe_list([M,F,A], St0), - Lanno = lineno_anno(L, St1), + Lanno = full_anno(L, St1), {#icall{anno=#a{anno=Lanno}, module=#c_literal{val=erlang}, name=#c_literal{val=make_fun}, @@ -649,13 +648,9 @@ expr({named_fun,L,'_',Cs,Id}, St) -> fun_tq(Id, Cs, L, St, unnamed); expr({named_fun,L,Name,Cs,Id}, St) -> fun_tq(Id, Cs, L, St, {named,Name}); -expr({call,L,{remote,_,M,F},As0}, #core{wanted=Wanted}=St0) -> +expr({call,L,{remote,_,M,F},As0}, St0) -> {[M1,F1|As1],Aps,St1} = safe_list([M,F|As0], St0), - Lanno = lineno_anno(L, St1), - Anno = case Wanted of - false -> [result_not_wanted|Lanno]; - true -> Lanno - end, + Anno = full_anno(L, St1), {#icall{anno=#a{anno=Anno},module=M1,name=F1,args=As1},Aps,St1}; expr({call,Lc,{atom,Lf,F},As0}, St0) -> {As1,Aps,St1} = safe_list(As0, St0), @@ -673,24 +668,51 @@ expr({match,L,P0,E0}, St0) -> {var,_,'_'} -> St0#core{wanted=false}; _ -> St0 end, - {E2,Eps,St2} = novars(E1, St1), + {E2,Eps1,St2} = novars(E1, St1), St3 = St2#core{wanted=St0#core.wanted}, - P2 = try - pattern(P1, St3) + {P2,Eps2,St4} = try + pattern(P1, St3) catch throw:Thrown -> - Thrown + {Thrown,[],St3} end, - {Fpat,St4} = new_var(St3), - Lanno = lineno_anno(L, St4), + {Fpat,St5} = new_var(St4), + Lanno = lineno_anno(L, St5), Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=badmatch},Fpat])), case P2 of nomatch -> - St = add_warning(L, nomatch, St4), - {#icase{anno=#a{anno=Lanno}, - args=[E2],clauses=[],fc=Fc},Eps,St}; + %% 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,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]}, + 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},Eps,St4} + {#imatch{anno=#a{anno=Lanno},pat=P2,arg=E2,fc=Fc},Eps1++Eps2,St5} end; expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) -> %% Optimise '++' here because of the list comprehension algorithm. @@ -704,37 +726,65 @@ expr({op,_,'++',{lc,Llc,E,Qs0},More}, St0) -> {Y,Yps,St} = lc_tq(Llc, E, Qs, Mc, St2), {Y,Mps++Yps,St}; expr({op,L,'andalso',E1,E2}, St0) -> - {#c_var{name=V0},St} = new_var(L, St0), + Anno = lineno_anno(L, St0), + {#c_var{name=V0},St} = new_var(Anno, St0), V = {var,L,V0}, False = {atom,L,false}, E = make_bool_switch(L, E1, V, E2, False, St0), expr(E, St); expr({op,L,'orelse',E1,E2}, St0) -> - {#c_var{name=V0},St} = new_var(L, St0), + Anno = lineno_anno(L, St0), + {#c_var{name=V0},St} = new_var(Anno, St0), V = {var,L,V0}, True = {atom,L,true}, E = make_bool_switch(L, E1, V, True, E2, St0), expr(E, St); expr({op,L,Op,A0}, St0) -> {A1,Aps,St1} = safe(A0, St0), - LineAnno = lineno_anno(L, St1), + LineAnno = full_anno(L, St1), {#icall{anno=#a{anno=LineAnno}, %Must have an #a{} module=#c_literal{anno=LineAnno,val=erlang}, name=#c_literal{anno=LineAnno,val=Op},args=[A1]},Aps,St1}; expr({op,L,Op,L0,R0}, St0) -> {As,Aps,St1} = safe_list([L0,R0], St0), - LineAnno = lineno_anno(L, St1), + LineAnno = full_anno(L, St1), {#icall{anno=#a{anno=LineAnno}, %Must have an #a{} 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{}) -> make_bool_switch_body(L, E, V, T, F). make_bool_switch_body(L, E, V, T, F) -> - NegL = neg_line(abs_line(L)), + NegL = no_compiler_warning(L), Error = {tuple,NegL,[{atom,NegL,badarg},V]}, {'case',NegL,E, [{clause,NegL,[{atom,NegL,true}],[],[T]}, @@ -745,106 +795,98 @@ make_bool_switch_body(L, E, V, T, F) -> make_bool_switch_guard(_, E, _, {atom,_,true}, {atom,_,false}) -> E; make_bool_switch_guard(L, E, V, T, F) -> - NegL = neg_line(abs_line(L)), + NegL = no_compiler_warning(L), {'case',NegL,E, [{clause,NegL,[{atom,NegL,true}],[],[T]}, {clause,NegL,[{atom,NegL,false}],[],[F]}, {clause,NegL,[V],[],[V]} ]}. -expr_map(M0,Es0,A,St0) -> - {M1,Mps,St1} = safe(M0, St0), +expr_map(M0, Es0, L, St0) -> + {M1,Eps0,St1} = safe(M0, St0), + Badmap = badmap_term(M1, St1), + A = lineno_anno(L, St1), + Fc = fail_clause([], [{eval_failure,badmap}|A], Badmap), case is_valid_map_src(M1) of true -> - case {M1,Es0} of - {#c_var{}, []} -> - %% transform M#{} to is_map(M) - {Vpat,St2} = new_var(St1), - {Fpat,St3} = new_var(St2), - Cs = [#iclause{ - anno=A, - pats=[Vpat], - guard=[#icall{anno=#a{anno=A}, + {M2,Eps1,St2} = map_build_pairs(M1, Es0, full_anno(L, St1), St1), + M3 = case Es0 of + [] -> M1; + [_|_] -> M2 + end, + Cs = [#iclause{ + anno=#a{anno=[compiler_generated|A]}, + pats=[], + guard=[#icall{anno=#a{anno=A}, module=#c_literal{anno=A,val=erlang}, name=#c_literal{anno=A,val=is_map}, - args=[Vpat]}], - body=[Vpat]}], - Fc = fail_clause([Fpat], A, #c_literal{val=badarg}), - {#icase{anno=#a{anno=A},args=[M1],clauses=Cs,fc=Fc},Mps,St3}; - {_,_} -> - {Es1,Eps,St2} = map_pair_list(Es0, St1), - {ann_c_map(A,M1,Es1),Mps++Eps,St2} - end; - false -> throw({bad_map,bad_map}) + args=[M1]}], + body=[M3]}], + Eps = Eps0 ++ Eps1, + {#icase{anno=#a{anno=A},args=[],clauses=Cs,fc=Fc},Eps,St2}; + false -> + %% Not a map source. The update will always fail. + St2 = add_warning(L, badmap, St1), + #iclause{body=[Fail]} = Fc, + {Fail,Eps0,St2} end. +badmap_term(_Map, #core{in_guard=true}) -> + %% The code generator cannot handle complex error reasons + %% in guards. But the exact error reason does not matter anyway + %% since it is not user-visible. + #c_literal{val=badmap}; +badmap_term(Map, #core{in_guard=false}) -> + c_tuple([#c_literal{val=badmap},Map]). + +map_build_pairs(Map, Es0, Ann, St0) -> + {Es,Pre,St1} = map_build_pairs_1(Es0, St0), + {ann_c_map(Ann, Map, Es),Pre,St1}. + +map_build_pairs_1([{Op0,L,K0,V0}|Es], St0) -> + {K,Pre0,St1} = safe(K0, St0), + {V,Pre1,St2} = safe(V0, St1), + {Pairs,Pre2,St3} = map_build_pairs_1(Es, St2), + As = lineno_anno(L, St3), + Op = map_op(Op0), + Pair = cerl:ann_c_map_pair(As, Op, K, V), + {[Pair|Pairs],Pre0++Pre1++Pre2,St3}; +map_build_pairs_1([], St) -> + {[],[],St}. + +map_op(map_field_assoc) -> #c_literal{val=assoc}; +map_op(map_field_exact) -> #c_literal{val=exact}. + is_valid_map_src(#c_literal{val = M}) when is_map(M) -> true; -is_valid_map_src(#c_map{}) -> true; -is_valid_map_src(#c_var{}) -> true; +is_valid_map_src(#c_var{}=Var) -> not cerl:is_c_fname(Var); is_valid_map_src(_) -> false. -map_pair_list(Es, St) -> - foldr(fun - ({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) -> - {K1,Ep0,St1} = safe(K0, St0), - K = ensure_valid_map_key(K1), - {V,Ep1,St2} = safe(V0, St1), - A = lineno_anno(L, St2), - Pair = #c_map_pair{op=#c_literal{val=assoc},anno=A,key=K,val=V}, - {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}; - ({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) -> - {K1,Ep0,St1} = safe(K0, St0), - K = ensure_valid_map_key(K1), - {V,Ep1,St2} = safe(V0, St1), - A = lineno_anno(L, St2), - Pair = #c_map_pair{op=#c_literal{val=exact},anno=A,key=K,val=V}, - {[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2} - end, {[],[],St}, Es). - -ensure_valid_map_key(K0) -> - case coalesced_map_key(K0) of - {ok,K1} -> K1; - error -> throw({bad_map,bad_map_key}) - end. - -coalesced_map_key(#c_literal{}=K) -> {ok,K}; -%% Dialyzer hack redux -%% DO coalesce tuples and list in maps for dialyzer -%% Dialyzer tries to break this apart, don't let it -coalesced_map_key(#c_tuple{}=K) -> - case core_lib:is_literal(K) of - true -> {ok,cerl:fold_literal(K)}; - false -> error - end; -coalesced_map_key(#c_cons{}=K) -> - case core_lib:is_literal(K) of - true -> {ok,cerl:fold_literal(K)}; - false -> error - end; -coalesced_map_key(_) -> error. - %% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}. try_exception(Ecs0, St0) -> %% Note that Tag is not needed for rethrow - it is already in Info. {Evs,St1} = new_vars(3, St0), % Tag, Value, Info - {Ecs1,St2} = clauses(Ecs0, St1), + {Ecs1,Ceps,St2} = clauses(Ecs0, St1), [_,Value,Info] = Evs, - Ec = #iclause{anno=#a{anno=[compiler_generated]}, + LA = case Ecs1 of + [] -> []; + [C|_] -> get_lineno_anno(C) + end, + Ec = #iclause{anno=#a{anno=[compiler_generated|LA]}, pats=[c_tuple(Evs)],guard=[#c_literal{val=true}], body=[#iprimop{anno=#a{}, %Must have an #a{} name=#c_literal{val=raise}, args=[Info,Value]}]}, - Hs = [#icase{anno=#a{},args=[c_tuple(Evs)],clauses=Ecs1,fc=Ec}], - {Evs,Hs,St2}. + Hs = [#icase{anno=#a{anno=LA},args=[c_tuple(Evs)],clauses=Ecs1,fc=Ec}], + {Evs,Ceps++Hs,St2}. try_after(As, St0) -> %% See above. - {Evs,St1} = new_vars(3, St0), % Tag, Value, Info + {Evs,St1} = new_vars(3, St0), % Tag, Value, Info [_,Value,Info] = Evs, - B = As ++ [#iprimop{anno=#a{}, %Must have an #a{} - name=#c_literal{val=raise}, - args=[Info,Value]}], + B = As ++ [#iprimop{anno=#a{}, % Must have an #a{} + name=#c_literal{val=raise}, + args=[Info,Value]}], Ec = #iclause{anno=#a{anno=[compiler_generated]}, pats=[c_tuple(Evs)],guard=[#c_literal{val=true}], body=B}, @@ -886,10 +928,10 @@ constant_bin_1(Es) -> ({float,_,F}, B) -> {value,F,B}; ({atom,_,undefined}, B) -> {value,undefined,B} end, - case catch eval_bits:expr_grp(Es, EmptyBindings, EvalFun) of + try eval_bits:expr_grp(Es, EmptyBindings, EvalFun) of {value,Bin,EmptyBindings} -> - Bin; - _ -> + Bin + catch error:_ -> error end. @@ -936,7 +978,7 @@ verify_suitable_fields([]) -> ok. %% (We don't need an exact result for this purpose.) count_bits(Int) -> - count_bits_1(abs_line(Int), 64). + count_bits_1(abs(Int), 64). count_bits_1(0, Bits) -> Bits; count_bits_1(Int, Bits) -> count_bits_1(Int bsr 64, Bits+64). @@ -978,20 +1020,21 @@ bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) -> fun_tq({_,_,Name}=Id, Cs0, L, St0, NameInfo) -> Arity = clause_arity(hd(Cs0)), - {Cs1,St1} = clauses(Cs0, St0), + {Cs1,Ceps,St1} = clauses(Cs0, St0), {Args,St2} = new_vars(Arity, St1), {Ps,St3} = new_vars(Arity, St2), %Need new variables here - Anno = lineno_anno(L, St3), + Anno = full_anno(L, St3), Fc = function_clause(Ps, Anno, {Name,Arity}), Fun = #ifun{anno=#a{anno=Anno}, id=[{id,Id}], %We KNOW! vars=Args,clauses=Cs1,fc=Fc,name=NameInfo}, - {Fun,[],St3}. + {Fun,Ceps,St3}. %% lc_tq(Line, Exp, [Qualifier], Mc, State) -> {LetRec,[PreExp],State}. %% This TQ from Simon PJ pp 127-138. -lc_tq(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard, +lc_tq(Line, E, [#igen{anno=GAnno,ceps=Ceps, + acc_pat=AccPat,acc_guard=AccGuard, skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, arg={Pre,Arg}}|Qs], Mc, St0) -> {Name,St1} = new_fun_name("lc", St0), @@ -1026,7 +1069,7 @@ lc_tq(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard, Fun = #ifun{anno=LAnno,id=[],vars=[Var],clauses=Cs,fc=Fc}, {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,1},Fun}], body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg]}]}, - [],St4}; + Ceps,St4}; 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) -> @@ -1040,7 +1083,7 @@ lc_tq(Line, E0, [], Mc0, St0) -> %% This TQ from Gustafsson ERLANG'05. %% More could be transformed before calling bc_tq. -bc_tq(Line, Exp, Qs0, _, St0) -> +bc_tq(Line, Exp, Qs0, St0) -> {BinVar,St1} = new_var(St0), {Sz,SzPre,St2} = bc_initial_size(Exp, Qs0, St1), {Qs,St3} = preprocess_quals(Line, Qs0, St2), @@ -1051,7 +1094,8 @@ bc_tq(Line, Exp, Qs0, _, St0) -> args=[Sz]}}] ++ BcPre, {E,Pre,St}. -bc_tq1(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard, +bc_tq1(Line, E, [#igen{anno=GAnno,ceps=Ceps, + acc_pat=AccPat,acc_guard=AccGuard, skip_pat=SkipPat,tail=Tail,tail_pat=TailPat, arg={Pre,Arg}}|Qs], Mc, St0) -> {Name,St1} = new_fun_name("lbc", St0), @@ -1089,17 +1133,43 @@ bc_tq1(Line, E, [#igen{anno=GAnno,acc_pat=AccPat,acc_guard=AccGuard, Fun = #ifun{anno=LAnno,id=[],vars=Vars,clauses=Cs,fc=Fc}, {#iletrec{anno=LAnno#a{anno=[list_comprehension|LA]},defs=[{{Name,2},Fun}], body=Pre ++ [#iapply{anno=LAnno,op=F,args=[Arg,Mc]}]}, - [],St4}; + Ceps,St4}; 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) -> - {E,Pre,St} = expr({bin,Bl,[{bin_element,Bl, - {var,Bl,AccVar#c_var.name}, - {atom,Bl,all}, - [binary,{unit,1}]}|Elements]}, St0), + bc_tq_build(Bl, [], AccVar, Elements, St0); +bc_tq1(Line, E0, [], AccVar, St0) -> + BsFlags = [binary,{unit,1}], + BsSize = {atom,Line,all}, + {E1,Pre0,St1} = safe(E0, St0), + case E1 of + #c_var{name=VarName} -> + Var = {var,Line,VarName}, + Els = [{bin_element,Line,Var,BsSize,BsFlags}], + bc_tq_build(Line, Pre0, AccVar, Els, St1); + #c_literal{val=Val} when is_bitstring(Val) -> + Bits = bit_size(Val), + <<Int0:Bits>> = Val, + Int = {integer,Line,Int0}, + Sz = {integer,Line,Bits}, + Els = [{bin_element,Line,Int,Sz,[integer,{unit,1},big]}], + bc_tq_build(Line, Pre0, AccVar, Els, St1); + _ -> + %% Any other safe (cons, tuple, literal) is not a + %% bitstring. Force the evaluation to fail (and + %% generate a warning). + Els = [{bin_element,Line,{atom,Line,bad_value},BsSize,BsFlags}], + bc_tq_build(Line, Pre0, AccVar, Els, St1) + end. + +bc_tq_build(Line, Pre0, #c_var{name=AccVar}, Elements0, St0) -> + Elements = [{bin_element,Line,{var,Line,AccVar},{atom,Line,all}, + [binary,{unit,1}]}|Elements0], + {E,Pre,St} = expr({bin,Line,Elements}, St0), #a{anno=A} = Anno0 = get_anno(E), Anno = Anno0#a{anno=[compiler_generated,single_use|A]}, - {set_anno(E, Anno),Pre,St}. + {set_anno(E, Anno),Pre0++Pre,St}. + %% filter_tq(Line, Expr, Filter, Mc, State, [Qualifier], TqFun) -> %% {Case,[PreExpr],State}. @@ -1153,7 +1223,7 @@ preprocess_quals(Line, [Q|Qs0], St0, Acc) -> {Gen,St} = generator(Line, Q, Gs, St0), preprocess_quals(Line, Qs, St, [Gen|Acc]); false -> - LAnno = #a{anno=lineno_anno(get_anno(Q), St0)}, + LAnno = #a{anno=lineno_anno(get_qual_anno(Q), St0)}, case is_guard_test(Q) of true -> %% When a filter is a guard test, its argument in the @@ -1178,6 +1248,11 @@ is_generator({generate,_,_,_}) -> true; is_generator({b_generate,_,_,_}) -> true; is_generator(_) -> false. +%% Retrieve the annotation from an Erlang AST form. +%% (Use get_anno/1 to retrieve the annotation from Core Erlang forms). + +get_qual_anno(Abstract) -> element(2, Abstract). + %% %% Generators are abstracted as sextuplets: %% - acc_pat is the accumulator pattern, e.g. [Pat|Tail] for Pat <- Expr. @@ -1200,7 +1275,7 @@ is_generator(_) -> false. generator(Line, {generate,Lg,P0,E}, Gs, St0) -> LA = lineno_anno(Line, St0), GA = lineno_anno(Lg, St0), - {Head,St1} = list_gen_pattern(P0, Line, St0), + {Head,Ceps,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 @@ -1220,25 +1295,27 @@ generator(Line, {generate,Lg,P0,E}, Gs, St0) -> ann_c_cons(LA, Skip, Tail)} end, {Ce,Pre,St4} = safe(E, St3), - Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, + Gen = #igen{anno=#a{anno=GA},ceps=Ceps, + acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, tail=Tail,tail_pat=#c_literal{anno=LA,val=[]},arg={Pre,Ce}}, {Gen,St4}; generator(Line, {b_generate,Lg,P,E}, Gs, St0) -> LA = lineno_anno(Line, St0), GA = lineno_anno(Lg, St0), - Cp = #c_binary{segments=Segs} = pattern(P, St0), + {Cp = #c_binary{segments=Segs},[],St1} = pattern(P, St0), + %% The function append_tail_segment/2 keeps variable patterns as-is, making %% it possible to have the same skip clause removal as with list generators. - {AccSegs,Tail,TailSeg,St1} = append_tail_segment(Segs, St0), + {AccSegs,Tail,TailSeg,St2} = append_tail_segment(Segs, St1), AccPat = Cp#c_binary{segments=AccSegs}, - {Cg,St2} = lc_guard_tests(Gs, St1), - {SkipSegs,St3} = emasculate_segments(AccSegs, St2), + {Cg,St3} = lc_guard_tests(Gs, St2), + {SkipSegs,St4} = emasculate_segments(AccSegs, St3), SkipPat = Cp#c_binary{segments=SkipSegs}, - {Ce,Pre,St4} = safe(E, St3), + {Ce,Pre,St5} = safe(E, St4), Gen = #igen{anno=#a{anno=GA},acc_pat=AccPat,acc_guard=Cg,skip_pat=SkipPat, tail=Tail,tail_pat=#c_binary{anno=LA,segments=[TailSeg]}, arg={Pre,Ce}}, - {Gen,St4}. + {Gen,St5}. append_tail_segment(Segs, St0) -> {Var,St} = new_var(St0), @@ -1267,9 +1344,9 @@ lc_guard_tests(Gs0, St0) -> list_gen_pattern(P0, Line, St) -> try - {pattern(P0, St),St} + pattern(P0,St) catch - nomatch -> {nomatch,add_warning(Line, nomatch, St)} + nomatch -> {nomatch,[],add_warning(Line, nomatch, St)} end. %%% @@ -1313,7 +1390,9 @@ bc_elem_size({bin,_,El}, St0) -> Vs = [V || {_,#c_var{name=V}} <- Vars0], {E,Pre,St} = bc_mul_pairs(F, #c_literal{val=Bits}, [], St0), {E,Pre,Vs,St} - end. + end; +bc_elem_size(_, _) -> + throw(impossible). bc_elem_size_1([{bin_element,_,_,{integer,_,N},Flags}|Es], Bits, Vars) -> {unit,U} = keyfind(unit, 1, Flags), @@ -1489,9 +1568,22 @@ force_novars(#iapply{}=App, St) -> {App,[],St}; force_novars(#icall{}=Call, St) -> {Call,[],St}; force_novars(#ifun{}=Fun, St) -> {Fun,[],St}; %These are novars too force_novars(#ibinary{}=Bin, St) -> {Bin,[],St}; +force_novars(#c_map{}=Bin, St) -> {Bin,[],St}; force_novars(Ce, St) -> force_safe(Ce, St). + +%% safe_pattern_expr(Expr, State) -> {Cexpr,[PreExpr],State}. +%% only literals and variables are safe expressions in patterns +safe_pattern_expr(E,St0) -> + case safe(E,St0) of + {#c_var{},_,_}=Safe -> Safe; + {#c_literal{},_,_}=Safe -> Safe; + {Ce,Eps,St1} -> + {V,St2} = new_var(St1), + {V,Eps++[#iset{var=V,arg=Ce}],St2} + 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 @@ -1566,90 +1658,92 @@ fold_match({match,L,P0,E0}, P) -> {{match,L,P0,P1},E1}; fold_match(E, P) -> {P,E}. -%% pattern(Pattern, State) -> CorePat. +%% pattern(Pattern, State) -> {CorePat,[PreExp],State}. %% Transform a pattern by removing line numbers. We also normalise %% aliases in patterns to standard form, {alias,Pat,[Var]}. - -pattern({var,L,V}, St) -> #c_var{anno=lineno_anno(L, St),name=V}; -pattern({char,L,C}, St) -> #c_literal{anno=lineno_anno(L, St),val=C}; -pattern({integer,L,I}, St) -> #c_literal{anno=lineno_anno(L, St),val=I}; -pattern({float,L,F}, St) -> #c_literal{anno=lineno_anno(L, St),val=F}; -pattern({atom,L,A}, St) -> #c_literal{anno=lineno_anno(L, St),val=A}; -pattern({string,L,S}, St) -> #c_literal{anno=lineno_anno(L, St),val=S}; -pattern({nil,L}, St) -> #c_literal{anno=lineno_anno(L, St),val=[]}; +%% +%% In patterns we may have expressions +%% 1) Binaries -> #c_bitstr{size=Expr} +%% 2) Maps -> #c_map_pair{key=Expr} +%% +%% Both of these may generate pre-expressions since only bound variables +%% or literals are allowed for these in core patterns. +%% +%% Therefor, we need to drag both the state and the collection of pre-expression +%% around in the whole pattern transformation tree. + +pattern({var,L,V}, St) -> {#c_var{anno=lineno_anno(L, St),name=V},[],St}; +pattern({char,L,C}, St) -> {#c_literal{anno=lineno_anno(L, St),val=C},[],St}; +pattern({integer,L,I}, St) -> {#c_literal{anno=lineno_anno(L, St),val=I},[],St}; +pattern({float,L,F}, St) -> {#c_literal{anno=lineno_anno(L, St),val=F},[],St}; +pattern({atom,L,A}, St) -> {#c_literal{anno=lineno_anno(L, St),val=A},[],St}; +pattern({string,L,S}, St) -> {#c_literal{anno=lineno_anno(L, St),val=S},[],St}; +pattern({nil,L}, St) -> {#c_literal{anno=lineno_anno(L, St),val=[]},[],St}; pattern({cons,L,H,T}, St) -> - annotate_cons(lineno_anno(L, St), pattern(H, St), pattern(T, St), St); + {Ph,Eps1,St1} = pattern(H, St), + {Pt,Eps2,St2} = pattern(T, St1), + {annotate_cons(lineno_anno(L, St), Ph, Pt, St2),Eps1++Eps2,St2}; pattern({tuple,L,Ps}, St) -> - annotate_tuple(record_anno(L, St), pattern_list(Ps, St), St); -pattern({map,L,Ps}, St) -> - #c_map{anno=lineno_anno(L, St), es=pattern_map_pairs(Ps, St)}; + {Ps1,Eps,St1} = pattern_list(Ps,St), + {annotate_tuple(record_anno(L, St), Ps1, St),Eps,St1}; +pattern({map,L,Pairs}, St0) -> + {Ps,Eps,St1} = pattern_map_pairs(Pairs, St0), + {#c_map{anno=lineno_anno(L, St1),es=Ps,is_pat=true},Eps,St1}; pattern({bin,L,Ps}, St) -> %% We don't create a #ibinary record here, since there is %% no need to hold any used/new annotations in a pattern. - #c_binary{anno=lineno_anno(L, St),segments=pat_bin(Ps, St)}; + {#c_binary{anno=lineno_anno(L, St),segments=pat_bin(Ps, St)},[],St}; pattern({match,_,P1,P2}, St) -> - pat_alias(pattern(P1, St), pattern(P2, St)). + {Cp1,Eps1,St1} = pattern(P1,St), + {Cp2,Eps2,St2} = pattern(P2,St1), + {pat_alias(Cp1,Cp2),Eps1++Eps2,St2}. %% pattern_map_pairs([MapFieldExact],State) -> [#c_map_pairs{}] pattern_map_pairs(Ps, St) -> - %% check literal key uniqueness (dict is needed) - %% pattern all pairs - {CMapPairs, Kdb} = lists:mapfoldl(fun - (P,Kdbi) -> - #c_map_pair{key=Ck,val=Cv} = CMapPair = pattern_map_pair(P,St), - K = core_lib:literal_value(Ck), - case dict:find(K,Kdbi) of - {ok, Vs} -> - {CMapPair, dict:store(K,[Cv|Vs],Kdbi)}; - _ -> - {CMapPair, dict:store(K,[Cv],Kdbi)} - end - end, dict:new(), Ps), - pattern_alias_map_pairs(CMapPairs,Kdb,dict:new(),St). - -pattern_alias_map_pairs([],_,_,_) -> []; -pattern_alias_map_pairs([#c_map_pair{key=Ck}=Pair|Pairs],Kdb,Kset,St) -> - %% alias same keys if needed - K = core_lib:literal_value(Ck), - case dict:find(K,Kset) of - {ok,processed} -> - pattern_alias_map_pairs(Pairs,Kdb,Kset,St); - _ -> - Cvs = dict:fetch(K,Kdb), - Cv = pattern_alias_map_pair_patterns(Cvs), - Kset1 = dict:store(K, processed, Kset), - [Pair#c_map_pair{val=Cv}|pattern_alias_map_pairs(Pairs,Kdb,Kset1,St)] - end. - -pattern_alias_map_pair_patterns([Cv]) -> Cv; -pattern_alias_map_pair_patterns([Cv1,Cv2|Cvs]) -> - pattern_alias_map_pair_patterns([pat_alias(Cv1,Cv2)|Cvs]). - -pattern_map_pair({map_field_exact,L,K,V},St) -> - #c_map_pair{anno=lineno_anno(L, St), - op=#c_literal{val=exact}, - key=pattern_map_key(K,St), - val=pattern(V, St)}. - -pattern_map_key(K,St) -> - %% Throws 'nomatch' if the key can't be a literal - %% this will be a cryptic error message but it is better than nothing - case expr(K,St) of - {Key0,[],_} -> - %% Dialyzer hack redux - case coalesced_map_key(Key0) of - {ok,Key1} -> Key1; - error -> throw(nomatch) - end; - _ -> throw(nomatch) - end. + %% check literal key uniqueness + %% - guaranteed via aliasing map pairs + %% pattern all pairs in two steps + %% 1) Construct Core Pattern + %% 2) Alias Keys in Core Pattern + {CMapPairs, {Eps,St1}} = lists:mapfoldl(fun + (P,{EpsM,Sti0}) -> + {CMapPair,EpsP,Sti1} = pattern_map_pair(P,Sti0), + {CMapPair, {EpsM++EpsP,Sti1}} + end, {[],St}, Ps), + {pat_alias_map_pairs(CMapPairs),Eps,St1}. + +pattern_map_pair({map_field_exact,L,K,V}, St0) -> + {Ck,EpsK,St1} = safe_pattern_expr(K, St0), + {Cv,EpsV,St2} = pattern(V, St1), + {#c_map_pair{anno=lineno_anno(L, St2), + op=#c_literal{val=exact}, + key=Ck, + val=Cv},EpsK++EpsV,St2}. + +pat_alias_map_pairs(Ps) -> + D = foldl(fun(#c_map_pair{key=K0}=Pair, D0) -> + K = cerl:set_ann(K0, []), + dict:append(K, Pair, D0) + end, dict:new(), Ps), + pat_alias_map_pairs_1(dict:to_list(D)). + +pat_alias_map_pairs_1([{_,[#c_map_pair{val=V0}=Pair|Vs]}|T]) -> + V = foldl(fun(#c_map_pair{val=V}, Pat) -> + pat_alias(V, Pat) + end, V0, Vs), + [Pair#c_map_pair{val=V}|pat_alias_map_pairs_1(T)]; +pat_alias_map_pairs_1([]) -> []. %% pat_bin([BinElement], State) -> [BinSeg]. pat_bin(Ps, St) -> [pat_segment(P, St) || P <- Ps]. -pat_segment({bin_element,_,Term,Size,[Type,{unit,Unit}|Flags]}, St) -> - #c_bitstr{val=pattern(Term, St),size=pattern(Size, St), +pat_segment({bin_element,L,Val,Size,[Type,{unit,Unit}|Flags]}, St) -> + Anno = lineno_anno(L, St), + {Pval,[],St1} = pattern(Val,St), + {Psize,[],_St2} = pattern(Size,St1), + #c_bitstr{anno=Anno, + val=Pval,size=Psize, unit=#c_literal{val=Unit}, type=#c_literal{val=Type}, flags=#c_literal{val=Flags}}. @@ -1657,38 +1751,55 @@ pat_segment({bin_element,_,Term,Size,[Type,{unit,Unit}|Flags]}, St) -> %% pat_alias(CorePat, CorePat) -> AliasPat. %% Normalise aliases. Trap bad aliases by throwing 'nomatch'. -pat_alias(#c_var{name=V1}, P2) -> #c_alias{var=#c_var{name=V1},pat=P2}; -pat_alias(P1, #c_var{name=V2}) -> #c_alias{var=#c_var{name=V2},pat=P1}; -pat_alias(#c_cons{}=Cons, #c_literal{anno=A,val=[H|T]}=S) -> - pat_alias(Cons, ann_c_cons_skel(A, #c_literal{anno=A,val=H}, - S#c_literal{val=T})); -pat_alias(#c_literal{anno=A,val=[H|T]}=S, #c_cons{}=Cons) -> - pat_alias(ann_c_cons_skel(A, #c_literal{anno=A,val=H}, - S#c_literal{val=T}), Cons); -pat_alias(#c_cons{anno=Anno,hd=H1,tl=T1}, #c_cons{hd=H2,tl=T2}) -> - ann_c_cons(Anno, pat_alias(H1, H2), pat_alias(T1, T2)); -pat_alias(#c_tuple{anno=Anno,es=Es1}, #c_literal{val=T}) when is_tuple(T) -> - Es2 = [#c_literal{val=E} || E <- tuple_to_list(T)], - ann_c_tuple(Anno, pat_alias_list(Es1, Es2)); -pat_alias(#c_literal{anno=Anno,val=T}, #c_tuple{es=Es2}) when is_tuple(T) -> - Es1 = [#c_literal{val=E} || E <- tuple_to_list(T)], - ann_c_tuple(Anno, pat_alias_list(Es1, Es2)); -pat_alias(#c_tuple{anno=Anno,es=Es1}, #c_tuple{es=Es2}) -> - ann_c_tuple(Anno, pat_alias_list(Es1, Es2)); -pat_alias(#c_alias{var=V1,pat=P1}, - #c_alias{var=V2,pat=P2}) -> - if V1 =:= V2 -> #c_alias{var=V1,pat=pat_alias(P1, P2)}; - true -> #c_alias{var=V1,pat=#c_alias{var=V2,pat=pat_alias(P1, P2)}} +pat_alias(#c_var{name=V1}=P, #c_var{name=V1}) -> P; +pat_alias(#c_var{name=V1}=Var, + #c_alias{var=#c_var{name=V2},pat=Pat}=Alias) -> + if + V1 =:= V2 -> + Alias; + true -> + Alias#c_alias{pat=pat_alias(Var, Pat)} + end; +pat_alias(#c_var{}=P1, P2) -> #c_alias{var=P1,pat=P2}; + +pat_alias(#c_alias{var=#c_var{name=V1}}=Alias, #c_var{name=V1}) -> + Alias; +pat_alias(#c_alias{var=#c_var{name=V1}=Var1,pat=P1}, + #c_alias{var=#c_var{name=V2}=Var2,pat=P2}) -> + Pat = pat_alias(P1, P2), + if + V1 =:= V2 -> + #c_alias{var=Var1,pat=Pat}; + true -> + pat_alias(Var1, pat_alias(Var2, Pat)) end; -pat_alias(#c_alias{var=V1,pat=P1}, P2) -> - #c_alias{var=V1,pat=pat_alias(P1, P2)}; -pat_alias(P1, #c_alias{var=V2,pat=P2}) -> - #c_alias{var=V2,pat=pat_alias(P1, P2)}; +pat_alias(#c_alias{var=#c_var{}=Var,pat=P1}, P2) -> + #c_alias{var=Var,pat=pat_alias(P1, P2)}; + +pat_alias(#c_map{es=Es1}=M, #c_map{es=Es2}) -> + M#c_map{es=pat_alias_map_pairs(Es1 ++ Es2)}; + +pat_alias(P1, #c_var{}=Var) -> + #c_alias{var=Var,pat=P1}; +pat_alias(P1, #c_alias{pat=P2}=Alias) -> + Alias#c_alias{pat=pat_alias(P1, P2)}; + pat_alias(P1, P2) -> - case {set_anno(P1, []),set_anno(P2, [])} of - {P,P} -> P; + %% Aliases between binaries are not allowed, so the only + %% legal patterns that remain are data patterns. + case cerl:is_data(P1) andalso cerl:is_data(P2) of + false -> throw(nomatch); + true -> ok + end, + Type = cerl:data_type(P1), + case cerl:data_type(P2) of + Type -> ok; _ -> throw(nomatch) - end. + end, + Es1 = cerl:data_es(P1), + Es2 = cerl:data_es(P2), + Es = pat_alias_list(Es1, Es2), + cerl:make_data(Type, Es). %% pat_alias_list([A1], [A2]) -> [A]. @@ -1697,9 +1808,15 @@ pat_alias_list([A1|A1s], [A2|A2s]) -> pat_alias_list([], []) -> []; pat_alias_list(_, _) -> throw(nomatch). -%% pattern_list([P], State) -> [P]. +%% pattern_list([P], State) -> {[P],Exprs,St} + +pattern_list([P0|Ps0], St0) -> + {P1,Eps,St1} = pattern(P0, St0), + {Ps1,Epsl,St2} = pattern_list(Ps0, St1), + {[P1|Ps1], Eps ++ Epsl, St2}; +pattern_list([], St) -> + {[],[],St}. -pattern_list(Ps, St) -> [pattern(P, St) || P <- Ps]. %% make_vars([Name]) -> [{Var,Name}]. @@ -1721,7 +1838,7 @@ new_var_name(#core{vcount=C}=St) -> new_var(St) -> new_var([], St). -new_var(Anno, St0) -> +new_var(Anno, St0) when is_list(Anno) -> {New,St} = new_var_name(St0), {#c_var{anno=Anno,name=New},St}. @@ -1779,7 +1896,7 @@ uclauses(Lcs, Ks, St0) -> uclause(Cl0, Ks, St0) -> {Cl1,_Pvs,Used,New,St1} = uclause(Cl0, Ks, Ks, St0), - A0 = get_ianno(Cl1), + A0 = get_anno(Cl1), A = A0#a{us=Used,ns=New}, {Cl1#iclause{anno=A},St1}. @@ -1823,27 +1940,22 @@ uguard(Pg, Gs0, Ks, St0) -> %% uexprs([Kexpr], [KnownVar], State) -> {[Kexpr],State}. uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) -> - %% Optimise for simple set of unbound variable. - case upattern(P0, Ks, St0) of - {#c_var{},[],_Pvs,_Pus,_} -> - %% Throw our work away and just set to iset. + case upat_is_new_var(P0, Ks) of + true -> + %% Assignment to a new variable. uexprs([#iset{var=P0,arg=Arg}|Les], Ks, St0); - _Other -> - %% Throw our work away and set to icase. - if - Les =:= [] -> - %% Need to explicitly return match "value", make - %% safe for efficiency. - {La0,Lps,St1} = force_safe(Arg, St0), - La = mark_compiler_generated(La0), - Mc = #iclause{anno=A,pats=[P0],guard=[],body=[La]}, - uexprs(Lps ++ [#icase{anno=A, - args=[La0],clauses=[Mc],fc=Fc}], Ks, St1); - true -> - Mc = #iclause{anno=A,pats=[P0],guard=[],body=Les}, - uexprs([#icase{anno=A,args=[Arg], - clauses=[Mc],fc=Fc}], Ks, St0) - end + false when Les =:= [] -> + %% Need to explicitly return match "value", make + %% safe for efficiency. + {La0,Lps,St1} = force_safe(Arg, St0), + La = mark_compiler_generated(La0), + Mc = #iclause{anno=A,pats=[P0],guard=[],body=[La]}, + uexprs(Lps ++ [#icase{anno=A, + args=[La0],clauses=[Mc],fc=Fc}], Ks, St1); + false -> + Mc = #iclause{anno=A,pats=[P0],guard=[],body=Les}, + uexprs([#icase{anno=A,args=[Arg], + clauses=[Mc],fc=Fc}], Ks, St0) end; uexprs([Le0|Les0], Ks, St0) -> {Le1,St1} = uexpr(Le0, Ks, St0), @@ -1851,6 +1963,15 @@ uexprs([Le0|Les0], Ks, St0) -> {[Le1|Les1],St2}; uexprs([], _, St) -> {[],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); +upat_is_new_var(_, _) -> + false. + %% Mark a "safe" as compiler-generated. mark_compiler_generated(#c_cons{anno=A,hd=H,tl=T}) -> ann_c_cons([compiler_generated|A], mark_compiler_generated(H), @@ -1948,11 +2069,11 @@ uexpr(#ibinary{anno=A,segments=Ss}, _, St) -> uexpr(#c_literal{}=Lit, _, St) -> Anno = get_anno(Lit), {set_anno(Lit, #a{us=[],anno=Anno}),St}; -uexpr(Lit, _, St) -> - true = is_simple(Lit), %Sanity check! - Vs = lit_vars(Lit), - Anno = get_anno(Lit), - {set_anno(Lit, #a{us=Vs,anno=Anno}),St}. +uexpr(Simple, _, St) -> + true = is_simple(Simple), %Sanity check! + Vs = lit_vars(Simple), + Anno = get_anno(Simple), + {#isimple{anno=#a{us=Vs,anno=Anno},term=Simple},St}. uexpr_list(Les0, Ks, St0) -> mapfoldl(fun (Le, St) -> uexpr(Le, Ks, St) end, St0, Les0). @@ -1966,7 +2087,7 @@ ufun_clauses(Lcs, Ks, St0) -> ufun_clause(Cl0, Ks, St0) -> {Cl1,Pvs,Used,_,St1} = uclause(Cl0, [], Ks, St0), - A0 = get_ianno(Cl1), + A0 = get_anno(Cl1), A = A0#a{us=subtract(intersection(Used, Ks), Pvs),ns=[]}, {Cl1#iclause{anno=A},St1}. @@ -1981,7 +2102,8 @@ upattern(#c_var{name=V}=Var, Ks, St0) -> true -> {N,St1} = new_var_name(St0), New = #c_var{name=N}, - Test = #icall{anno=#a{us=add_element(N, [V])}, + LA = get_lineno_anno(Var), + Test = #icall{anno=#a{anno=LA,us=add_element(N, [V])}, module=#c_literal{val=erlang}, name=#c_literal{val='=:='}, args=[New,Var]}, @@ -1999,9 +2121,14 @@ upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) -> upattern(#c_map{es=Es0}=Map, Ks, St0) -> {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0), {Map#c_map{es=Es1},Esg,Esv,Eus,St1}; -upattern(#c_map_pair{op=#c_literal{val=exact},val=V0}=MapPair, Ks, St0) -> - {V,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0), - {MapPair#c_map_pair{val=V},Vg,Vv,Vu,St1}; +upattern(#c_map_pair{op=#c_literal{val=exact},key=K0,val=V0}=Pair,Ks,St0) -> + {V,Vg,Vn,Vu,St1} = upattern(V0, Ks, St0), + % A variable key must be considered used here + Ku = case K0 of + #c_var{name=Name} -> [Name]; + _ -> [] + end, + {Pair#c_map_pair{val=V},Vg,Vn,union(Ku,Vu),St1}; upattern(#c_binary{segments=Es0}=Bin, Ks, St0) -> {Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0), {Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1}; @@ -2124,7 +2251,8 @@ cguard(Gs, St0) -> cexprs([#iset{var=#c_var{name=Name}=Var}=Iset], As, St) -> %% Make return value explicit, and make Var true top level. - cexprs([Iset,Var#c_var{anno=#a{us=[Name]}}], As, St); + Isimple = #isimple{anno=#a{us=[Name]},term=Var}, + cexprs([Iset,Isimple], As, St); cexprs([Le], As, St0) -> {Ce,Es,Us,St1} = cexpr(Le, As, St0), Exp = make_vars(As), %The export variables @@ -2239,12 +2367,9 @@ cexpr(#c_literal{}=Lit, _As, St) -> Anno = get_anno(Lit), Vs = Anno#a.us, {set_anno(Lit, Anno#a.anno),[],Vs,St}; -cexpr(Lit, _As, St) -> - true = is_simple(Lit), %Sanity check! - Anno = get_anno(Lit), - Vs = Anno#a.us, - %%Vs = lit_vars(Lit), - {set_anno(Lit, Anno#a.anno),[],Vs,St}. +cexpr(#isimple{anno=#a{us=Vs},term=Simple}, _As, St) -> + true = is_simple(Simple), %Sanity check! + {Simple,[],Vs,St}. cfun(#ifun{anno=A,id=Id,vars=Args,clauses=Lcs,fc=Lfc}, _As, St0) -> {Ccs,St1} = cclauses(Lcs, [], St0), %NEVER export! @@ -2267,11 +2392,6 @@ lit_vars(#c_map_pair{key=K,val=V}, Vs) -> lit_vars(K, lit_vars(V, Vs)); lit_vars(#c_var{name=V}, Vs) -> add_element(V, Vs); lit_vars(_, Vs) -> Vs. %These are atomic -% lit_bin_vars(Segs, Vs) -> -% foldl(fun (#c_bitstr{val=V,size=S}, Vs0) -> -% lit_vars(V, lit_vars(S, Vs0)) -% end, Vs, Segs). - lit_list_vars(Ls) -> lit_list_vars(Ls, []). lit_list_vars(Ls, Vs) -> @@ -2285,37 +2405,26 @@ bitstr_vars(Segs, Vs) -> lit_vars(V, lit_vars(S, Vs0)) end, Vs, Segs). -record_anno(L, St) when L >= ?REC_OFFSET -> - case member(dialyzer, St#core.opts) of - true -> - [record | lineno_anno(L - ?REC_OFFSET, St)]; - false -> - lineno_anno(L, St) - end; -record_anno(L, St) when L < -?REC_OFFSET -> - case member(dialyzer, St#core.opts) of +record_anno(L, St) -> + case + erl_anno:record(L) andalso member(dialyzer, St#core.opts) + of true -> - [record | lineno_anno(L + ?REC_OFFSET, St)]; + [record | lineno_anno(L, St)]; false -> - lineno_anno(L, St) - end; -record_anno(L, St) -> + full_anno(L, St) + end. + +full_anno(L, #core{wanted=false}=St) -> + [result_not_wanted|lineno_anno(L, St)]; +full_anno(L, #core{wanted=true}=St) -> lineno_anno(L, St). lineno_anno(L, St) -> - {line, Line} = erl_parse:get_attribute(L, line), - if - Line < 0 -> - [-Line] ++ St#core.file ++ [compiler_generated]; - true -> - [Line] ++ St#core.file - end. - -get_ianno(Ce) -> - case get_anno(Ce) of - #a{}=A -> A; - A when is_list(A) -> #a{anno=A} - end. + Line = erl_anno:line(L), + Generated = erl_anno:generated(L), + CompilerGenerated = [compiler_generated || Generated], + [Line] ++ St#core.file ++ CompilerGenerated. get_lineno_anno(Ce) -> case get_anno(Ce) of @@ -2323,15 +2432,8 @@ get_lineno_anno(Ce) -> A when is_list(A) -> A end. -location(L) -> - {location,Location} = erl_parse:get_attribute(L, location), - Location. - -abs_line(L) -> - erl_parse:set_line(L, fun(Line) -> abs(Line) end). - -neg_line(L) -> - erl_parse:set_line(L, fun(Line) -> -abs(Line) end). +no_compiler_warning(Anno) -> + erl_anno:set_generated(true, Anno). %% %% The following three functions are used both with cerl:cerl() and with i()'s @@ -2372,11 +2474,13 @@ format_error(nomatch) -> "pattern cannot possibly match"; format_error(bad_binary) -> "binary construction will fail because of a type mismatch"; -format_error(bad_map_key) -> - "map construction will fail because of none literal key (large binaries are not literals)"; -format_error(bad_map) -> +format_error(badmap) -> "map construction will fail because of a type mismatch". -add_warning(Line, Term, #core{ws=Ws,file=[{file,File}]}=St) when Line >= 0 -> - St#core{ws=[{File,[{location(Line),?MODULE,Term}]}|Ws]}; -add_warning(_, _, St) -> St. +add_warning(Anno, Term, #core{ws=Ws,file=[{file,File}]}=St) -> + case erl_anno:generated(Anno) of + false -> + St#core{ws=[{File,[{erl_anno:location(Anno),?MODULE,Term}]}|Ws]}; + true -> + St + end. diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 40d2f72b4c..b4bbc5e739 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2013. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -114,9 +115,9 @@ copy_anno(Kdst, Ksrc) -> ff, %Current function vcount=0, %Variable counter fcount=0, %Fun counter - ds=[], %Defined variables + ds=cerl_sets:new() :: cerl_sets:set(), %Defined variables funs=[], %Fun functions - free=[], %Free variables + free=#{}, %Free variables ws=[] :: [warning()], %Warnings. guard_refc=0}). %> 0 means in guard @@ -131,24 +132,27 @@ module(#c_module{anno=A,name=M,exports=Es,attrs=As,defs=Fs}, _Options) -> {ok,#k_mdef{anno=A,name=M#c_literal.val,exports=Kes,attributes=Kas, body=Kfs ++ St#kern.funs},lists:sort(St#kern.ws)}. -attributes([{#c_literal{val=Name},Val}|As]) -> +attributes([{#c_literal{val=Name},#c_literal{val=Val}}|As]) -> case include_attribute(Name) of false -> attributes(As); true -> - [{Name,core_lib:literal_value(Val)}|attributes(As)] + [{Name,Val}|attributes(As)] end; attributes([]) -> []. include_attribute(type) -> false; include_attribute(spec) -> false; +include_attribute(callback) -> false; include_attribute(opaque) -> false; include_attribute(export_type) -> false; +include_attribute(record) -> false; +include_attribute(optional_callbacks) -> false; include_attribute(_) -> true. function({#c_var{name={F,Arity}=FA},Body}, St0) -> try - St1 = St0#kern{func=FA,ff=undefined,vcount=0,fcount=0,ds=sets:new()}, + St1 = St0#kern{func=FA,ff=undefined,vcount=0,fcount=0,ds=cerl_sets:new()}, {#ifun{anno=Ab,vars=Kvs,body=B0},[],St2} = expr(Body, new_sub(), St1), {B1,_,St3} = ubody(B0, return, St2), %%B1 = B0, St3 = St2, %Null second pass @@ -238,7 +242,7 @@ gexpr_test_add(Ke, St0) -> expr(#c_var{anno=A,name={_Name,Arity}}=Fname, Sub, St) -> %% A local in an expression. %% For now, these are wrapped into a fun by reverse - %% etha-conversion, but really, there should be exactly one + %% eta-conversion, but really, there should be exactly one %% such "lambda function" for each escaping local name, %% instead of one for each occurrence as done now. Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} || @@ -273,17 +277,7 @@ expr(#c_tuple{anno=A,es=Ces}, Sub, St0) -> {Kes,Ep,St1} = atomic_list(Ces, Sub, St0), {#k_tuple{anno=A,es=Kes},Ep,St1}; expr(#c_map{anno=A,arg=Var,es=Ces}, Sub, St0) -> - try expr_map(A,Var,Ces,Sub,St0) of - {_,_,_}=Res -> Res - catch - throw:bad_map -> - St1 = add_warning(get_line(A), bad_map, A, St0), - Erl = #c_literal{val=erlang}, - Name = #c_literal{val=error}, - Args = [#c_literal{val=badarg}], - Error = #c_call{anno=A,module=Erl,name=Name,args=Args}, - expr(Error, Sub, St1) - end; + expr_map(A, Var, Ces, Sub, St0); expr(#c_binary{anno=A,segments=Cv}, Sub, St0) -> try atomic_bin(Cv, Sub, St0) of {Kv,Ep,St1} -> @@ -408,7 +402,7 @@ expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) -> Call = #c_call{anno=A, module=#c_literal{val=erlang}, name=#c_literal{val=apply}, - args=[M0,F0,make_list(Cargs)]}, + args=[M0,F0,cerl:make_list(Cargs)]}, expr(Call, Sub, St1); _ -> {[M1,F1|Kargs],Ap,St} = atomic_list([M0,F0|Cargs], Sub, St1), @@ -503,85 +497,90 @@ translate_match_fail_1(Anno, As, Sub, #kern{ff=FF}) -> end. translate_fc(Args) -> - [#c_literal{val=function_clause},make_list(Args)]. + [#c_literal{val=function_clause},cerl:make_list(Args)]. expr_map(A,Var0,Ces,Sub,St0) -> - %% An extra pass of validation of Map src because of inlining {Var,Mps,St1} = expr(Var0, Sub, St0), - case is_valid_map_src(Var) of - true -> - {Km,Eps,St2} = map_split_pairs(A, Var, Ces, Sub, St1), - {Km,Eps++Mps,St2}; - false -> throw(bad_map) - end. - -is_valid_map_src(#k_map{}) -> true; -is_valid_map_src(#k_literal{val=M}) when is_map(M) -> true; -is_valid_map_src(#k_var{}) -> true; -is_valid_map_src(_) -> false. + {Km,Eps,St2} = map_split_pairs(A, Var, Ces, Sub, St1), + {Km,Eps++Mps,St2}. map_split_pairs(A, Var, Ces, Sub, St0) -> - %% two steps - %% 1. force variables - %% 2. remove multiples - Pairs0 = [{Op,K,V} || #c_map_pair{op=#c_literal{val=Op},key=K,val=V} <- Ces], + %% 1. Force variables. + %% 2. Group adjacent pairs with literal keys. + %% 3. Within each such group, remove multiple assignments to the same key. + %% 4. Partition each group according to operator ('=>' and ':='). + Pairs0 = [{Op,K,V} || + #c_map_pair{op=#c_literal{val=Op},key=K,val=V} <- Ces], {Pairs,Esp,St1} = foldr(fun ({Op,K0,V0}, {Ops,Espi,Sti0}) when Op =:= assoc; Op =:= exact -> - {K,[],Sti1} = expr(K0, Sub, Sti0), - {V,Ep,Sti2} = atomic(V0, Sub, Sti1), - {[{Op,K,V}|Ops],Ep ++ Espi,Sti2} + {K,Eps1,Sti1} = atomic(K0, Sub, Sti0), + {V,Eps2,Sti2} = atomic(V0, Sub, Sti1), + {[{Op,K,V}|Ops],Eps1 ++ Eps2 ++ Espi,Sti2} end, {[],[],St0}, Pairs0), - - case map_group_pairs(Pairs) of - {Assoc,[]} -> - Kes = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc], - {#k_map{anno=A,op=assoc,var=Var,es=Kes},Esp,St1}; - {[],Exact} -> - Kes = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact], - {#k_map{anno=A,op=exact,var=Var,es=Kes},Esp,St1}; - {Assoc,Exact} -> - Kes1 = [#k_map_pair{key=K,val=V}||{_,{assoc,K,V}} <- Assoc], - {Mvar,Em,St2} = force_atomic(#k_map{anno=A,op=assoc,var=Var,es=Kes1},St1), - Kes2 = [#k_map_pair{key=K,val=V}||{_,{exact,K,V}} <- Exact], - {#k_map{anno=A,op=exact,var=Mvar,es=Kes2},Esp ++ Em,St2} - + map_split_pairs_1(A, Var, Pairs, Esp, St1). + +map_split_pairs_1(A, Map0, [{Op,Key,Val}|Pairs1]=Pairs0, Esp0, St0) -> + {Map1,Em,St1} = force_atomic(Map0, St0), + case Key of + #k_var{} -> + %% Don't combine variable keys with other keys. + Kes = [#k_map_pair{key=Key,val=Val}], + Map = #k_map{anno=A,op=Op,var=Map1,es=Kes}, + map_split_pairs_1(A, Map, Pairs1, Esp0 ++ Em, St1); + _ -> + %% Literal key. Split off all literal keys. + {L,Pairs} = splitwith(fun({_,#k_var{},_}) -> false; + ({_,_,_}) -> true + end, Pairs0), + {Map,Esp,St2} = map_group_pairs(A, Map1, L, Esp0 ++ Em, St1), + map_split_pairs_1(A, Map, Pairs, Esp, St2) + end; +map_split_pairs_1(_, Map, [], Esp, St0) -> + {Map,Esp,St0}. + +map_group_pairs(A, Var, Pairs0, Esp, St0) -> + Pairs = map_remove_dup_keys(Pairs0), + Assoc = [#k_map_pair{key=K,val=V} || {_,{assoc,K,V}} <- Pairs], + Exact = [#k_map_pair{key=K,val=V} || {_,{exact,K,V}} <- Pairs], + case {Assoc,Exact} of + {[_|_],[]} -> + {#k_map{anno=A,op=assoc,var=Var,es=Assoc},Esp,St0}; + {[],[_|_]} -> + {#k_map{anno=A,op=exact,var=Var,es=Exact},Esp,St0}; + {[_|_],[_|_]} -> + Map = #k_map{anno=A,op=assoc,var=Var,es=Assoc}, + {Mvar,Em,St1} = force_atomic(Map, St0), + {#k_map{anno=A,op=exact,var=Mvar,es=Exact},Esp ++ Em,St1} end. -%% Group map by Assoc operations and Exact operations - -map_group_pairs(Es) -> - Groups = dict:to_list(map_group_pairs(Es,dict:new())), - partition(fun({_,{Op,_,_}}) -> Op =:= assoc end, Groups). +map_remove_dup_keys(Es) -> + dict:to_list(map_remove_dup_keys(Es, dict:new())). -map_group_pairs([{assoc,K,V}|Es0],Used0) -> - Used1 = case map_key_is_used(K,Used0) of - {ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0); - {ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0); - _ -> map_key_set_used(K,{assoc,K,V},Used0) - end, - map_group_pairs(Es0,Used1); -map_group_pairs([{exact,K,V}|Es0],Used0) -> - Used1 = case map_key_is_used(K,Used0) of - {ok, {assoc,_,_}} -> map_key_set_used(K,{assoc,K,V},Used0); - {ok, {exact,_,_}} -> map_key_set_used(K,{exact,K,V},Used0); - _ -> map_key_set_used(K,{exact,K,V},Used0) - end, - map_group_pairs(Es0,Used1); -map_group_pairs([],Used) -> - Used. - -map_key_set_used(K,How,Used) -> - dict:store(map_key_clean(K),How,Used). - -map_key_is_used(K,Used) -> - dict:find(map_key_clean(K),Used). +map_remove_dup_keys([{assoc,K0,V}|Es0],Used0) -> + K = map_key_clean(K0), + Op = case dict:find(K, Used0) of + {ok,{exact,_,_}} -> exact; + _ -> assoc + end, + Used1 = dict:store(K, {Op,K0,V}, Used0), + map_remove_dup_keys(Es0, Used1); +map_remove_dup_keys([{exact,K0,V}|Es0],Used0) -> + K = map_key_clean(K0), + Op = case dict:find(K, Used0) of + {ok,{assoc,_,_}} -> assoc; + _ -> exact + end, + Used1 = dict:store(K, {Op,K0,V}, Used0), + map_remove_dup_keys(Es0, Used1); +map_remove_dup_keys([], Used) -> Used. -%% Be explicit instead of using set_kanno(K,[]) -map_key_clean(#k_literal{val=V}) -> {k_literal,V}; -map_key_clean(#k_int{val=V}) -> {k_int,V}; -map_key_clean(#k_float{val=V}) -> {k_float,V}; -map_key_clean(#k_atom{val=V}) -> {k_atom,V}; -map_key_clean(#k_nil{}) -> k_nil. +%% Be explicit instead of using set_kanno(K, []). +map_key_clean(#k_var{name=V}) -> {var,V}; +map_key_clean(#k_literal{val=V}) -> {lit,V}; +map_key_clean(#k_int{val=V}) -> {lit,V}; +map_key_clean(#k_float{val=V}) -> {lit,V}; +map_key_clean(#k_atom{val=V}) -> {lit,V}; +map_key_clean(#k_nil{}) -> {lit,[]}. %% call_type(Module, Function, Arity) -> call | bif | apply | error. @@ -660,12 +659,12 @@ atomic_bin([#c_bitstr{anno=A,val=E0,size=S0,unit=U0,type=T,flags=Fs0}|Es0], {E,Ap1,St1} = atomic(E0, Sub, St0), {S1,Ap2,St2} = atomic(S0, Sub, St1), validate_bin_element_size(S1), - U1 = core_lib:literal_value(U0), - Fs1 = core_lib:literal_value(Fs0), + U1 = cerl:concrete(U0), + Fs1 = cerl:concrete(Fs0), {Es,Ap3,St3} = atomic_bin(Es0, Sub, St2), {#k_bin_seg{anno=A,size=S1, unit=U1, - type=core_lib:literal_value(T), + type=cerl:concrete(T), flags=Fs1, seg=E,next=Es}, Ap1++Ap2++Ap3,St3}; @@ -720,15 +719,15 @@ force_variable(Ke, St0) -> %% handling. pattern(#c_var{anno=A,name=V}, _Isub, Osub, St0) -> - case sets:is_element(V, St0#kern.ds) of + case cerl_sets:is_element(V, St0#kern.ds) of true -> {New,St1} = new_var_name(St0), {#k_var{anno=A,name=New}, set_vsub(V, New, Osub), - St1#kern{ds=sets:add_element(New, St1#kern.ds)}}; + St1#kern{ds=cerl_sets:add_element(New, St1#kern.ds)}}; false -> {#k_var{anno=A,name=V},Osub, - St0#kern{ds=sets:add_element(V, St0#kern.ds)}} + St0#kern{ds=cerl_sets:add_element(V, St0#kern.ds)}} end; pattern(#c_literal{anno=A,val=Val}, _Isub, Osub, St) -> {#k_literal{anno=A,val=Val},Osub,St}; @@ -757,23 +756,22 @@ flatten_alias(#c_alias{var=V,pat=P}) -> flatten_alias(Pat) -> {[],Pat}. pattern_map_pairs(Ces0, Isub, Osub0, St0) -> - %% It is assumed that all core keys are literals - %% It is later assumed that these keys are term sorted - %% so we need to sort them here - Ces1 = lists:sort(fun - (#c_map_pair{key=CkA},#c_map_pair{key=CkB}) -> - A = core_lib:literal_value(CkA), - B = core_lib:literal_value(CkB), - erts_internal:cmp_term(A,B) < 0 - end, Ces0), %% pattern the pair keys and values as normal {Kes,{Osub1,St1}} = lists:mapfoldl(fun (#c_map_pair{anno=A,key=Ck,val=Cv},{Osubi0,Sti0}) -> - {Kk,Osubi1,Sti1} = pattern(Ck, Isub, Osubi0, Sti0), - {Kv,Osubi2,Sti2} = pattern(Cv, Isub, Osubi1, Sti1), + {Kk,[],Sti1} = expr(Ck, Isub, Sti0), + {Kv,Osubi2,Sti2} = pattern(Cv, Isub, Osubi0, Sti1), {#k_map_pair{anno=A,key=Kk,val=Kv},{Osubi2,Sti2}} - end, {Osub0, St0}, Ces1), - {Kes,Osub1,St1}. + end, {Osub0, St0}, Ces0), + %% It is later assumed that these keys are term sorted + %% so we need to sort them here + Kes1 = lists:sort(fun + (#k_map_pair{key=KkA},#k_map_pair{key=KkB}) -> + A = map_key_clean(KkA), + B = map_key_clean(KkB), + erts_internal:cmp_term(A,B) < 0 + end, Kes), + {Kes1,Osub1,St1}. pattern_bin(Es, Isub, Osub0, St0) -> {Kbin,{_,Osub},St} = pattern_bin_1(Es, Isub, Osub0, St0), @@ -793,8 +791,8 @@ pattern_bin_1([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0], %% problems. #k_atom{val=bad_size} end, - U0 = core_lib:literal_value(U), - Fs0 = core_lib:literal_value(Fs), + U0 = cerl:concrete(U), + Fs0 = cerl:concrete(Fs), %%ok= io:fwrite("~w: ~p~n", [?LINE,{B0,S,U0,Fs0}]), {E,Osub1,St2} = pattern(E0, Isub0, Osub0, St1), Isub1 = case E0 of @@ -805,7 +803,7 @@ pattern_bin_1([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0], {Es,{Isub,Osub},St3} = pattern_bin_1(Es0, Isub1, Osub1, St2), {#k_bin_seg{anno=A,size=S, unit=U0, - type=core_lib:literal_value(T), + type=cerl:concrete(T), flags=Fs0, seg=E,next=Es}, {Isub,Osub},St3}; @@ -842,12 +840,23 @@ get_vsub(V, Vsub) -> set_vsub(V, S, Vsub) -> orddict:store(V, S, Vsub). -subst_vsub(V, S, Vsub0) -> - %% Fold chained substitutions. - Vsub1 = orddict:map(fun (_, V1) when V1 =:= V -> S; - (_, V1) -> V1 - end, Vsub0), - orddict:store(V, S, Vsub1). +subst_vsub(Key, New, [{K,Key}|Dict]) -> + %% Fold chained substitution. + [{K,New}|subst_vsub(Key, New, Dict)]; +subst_vsub(Key, New, [{K,_}|_]=Dict) when Key < K -> + %% Insert the new substitution here, and continue + %% look for chained substitutions. + [{Key,New}|subst_vsub_1(Key, New, Dict)]; +subst_vsub(Key, New, [{K,_}=E|Dict]) when Key > K -> + [E|subst_vsub(Key, New, Dict)]; +subst_vsub(Key, New, []) -> [{Key,New}]. + +subst_vsub_1(V, S, [{K,V}|Dict]) -> + %% Fold chained substitution. + [{K,S}|subst_vsub_1(V, S, Dict)]; +subst_vsub_1(V, S, [E|Dict]) -> + [E|subst_vsub_1(V, S, Dict)]; +subst_vsub_1(_, _, []) -> []. get_fsub(F, A, Fsub) -> case orddict:find({F,A}, Fsub) of @@ -892,7 +901,7 @@ new_vars(0, St, Vs) -> {Vs,St}. make_vars(Vs) -> [ #k_var{name=V} || V <- Vs ]. add_var_def(V, St) -> - St#kern{ds=sets:add_element(V#k_var.name, St#kern.ds)}. + St#kern{ds=cerl_sets:add_element(V#k_var.name, St#kern.ds)}. %%add_vars_def(Vs, St) -> %% Ds = foldl(fun (#k_var{name=V}, Ds) -> add_element(V, Ds) end, @@ -1550,13 +1559,11 @@ arg_val(Arg, C) -> {set_kanno(S, []),U,T,Fs} end; #k_map{op=exact,es=Es} -> - Keys = [begin - #k_map_pair{key=#k_literal{val=Key}} = Pair, - Key - end || Pair <- Es], - %% multiple keys may have the same name - %% do not use ordsets - lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) < 0 end, Keys) + lists:sort(fun(A,B) -> + %% on the form K :: {'lit' | 'var', term()} + %% lit < var as intended + erts_internal:cmp_term(A,B) < 0 + end, [map_key_clean(Key) || #k_map_pair{key=Key} <- Es]) end. %% ubody_used_vars(Expr, State) -> [UsedVar] @@ -1833,14 +1840,17 @@ handle_reuse_anno_1(V, _St) -> V. %% get_free(Name, Arity, State) -> [Free]. %% store_free(Name, Arity, [Free], State) -> State. -get_free(F, A, St) -> - case orddict:find({F,A}, St#kern.free) of - {ok,Val} -> Val; - error -> [] +get_free(F, A, #kern{free=FreeMap}) -> + Key = {F,A}, + case FreeMap of + #{Key:=Val} -> Val; + _ -> [] end. -store_free(F, A, Free, St) -> - St#kern{free=orddict:store({F,A}, Free, St#kern.free)}. +store_free(F, A, Free, #kern{free=FreeMap0}=St) -> + Key = {F,A}, + FreeMap = FreeMap0#{Key=>Free}, + St#kern{free=FreeMap}. break_rets({break,Rs}) -> Rs; break_rets(return) -> []. @@ -1943,6 +1953,7 @@ lit_list_vars(Ps) -> %% pat_vars(Pattern) -> {[UsedVarName],[NewVarName]}. %% Return variables in a pattern. All variables are new variables %% except those in the size field of binary segments. +%% and map_pair keys pat_vars(#k_var{name=N}) -> {[],[N]}; %%pat_vars(#k_char{}) -> {[],[]}; @@ -1967,8 +1978,10 @@ pat_vars(#k_tuple{es=Es}) -> pat_list_vars(Es); pat_vars(#k_map{es=Es}) -> pat_list_vars(Es); -pat_vars(#k_map_pair{val=V}) -> - pat_vars(V). +pat_vars(#k_map_pair{key=K,val=V}) -> + {U1,New} = pat_vars(V), + {[], U2} = pat_vars(K), + {union(U1,U2),New}. pat_list_vars(Ps) -> foldl(fun (P, {Used0,New0}) -> @@ -1976,11 +1989,6 @@ pat_list_vars(Ps) -> {union(Used0, Used),union(New0, New)} end, {[],[]}, Ps). -make_list(Es) -> - foldr(fun(E, Acc) -> - #c_cons{hd=E,tl=Acc} - end, #c_literal{val=[]}, Es). - %% List of integers in interval [N,M]. Empty list if N > M. integers(N, M) when N =< M -> @@ -2009,9 +2017,7 @@ format_error(nomatch_shadow) -> format_error(bad_call) -> "invalid module and/or function name; this call will always fail"; format_error(bad_segment_size) -> - "binary construction will fail because of a type mismatch"; -format_error(bad_map) -> - "map construction will fail because of a type mismatch". + "binary construction will fail because of a type mismatch". add_warning(none, Term, Anno, #kern{ws=Ws}=St) -> File = get_file(Anno), diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl index ab66445f73..5216a1a620 100644 --- a/lib/compiler/src/v3_kernel.hrl +++ b/lib/compiler/src/v3_kernel.hrl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -38,7 +39,7 @@ -record(k_nil, {anno=[]}). -record(k_tuple, {anno=[],es}). --record(k_map, {anno=[],var,op,es}). +-record(k_map, {anno=[],var=#k_literal{val=#{}},op,es}). -record(k_map_pair, {anno=[],key,val}). -record(k_cons, {anno=[],hd,tl}). -record(k_binary, {anno=[],segs}). diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl index b33eba50eb..0b90f0a1e0 100644 --- a/lib/compiler/src/v3_kernel_pp.erl +++ b/lib/compiler/src/v3_kernel_pp.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index cd4b5fd674..1452b78d1d 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -45,7 +46,7 @@ -export([vdb_find/2]). --import(lists, [member/2,map/2,foldl/3,reverse/1,sort/1]). +-import(lists, [member/2,map/2,reverse/1,sort/1]). -import(ordsets, [add_element/2,intersection/2,union/2]). -include("v3_kernel.hrl"). @@ -68,7 +69,7 @@ functions([], Acc) -> reverse(Acc). function(#k_fdef{anno=#k{a=Anno},func=F,arity=Ar,vars=Vs,body=Kb}) -> try As = var_list(Vs), - Vdb0 = foldl(fun ({var,N}, Vdb) -> new_var(N, 0, Vdb) end, [], As), + Vdb0 = init_vars(As), %% Force a top-level match! B0 = case Kb of #k_match{} -> Kb; @@ -94,14 +95,14 @@ function(#k_fdef{anno=#k{a=Anno},func=F,arity=Ar,vars=Vs,body=Kb}) -> body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) -> %%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]), A = get_kanno(Ke), - Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)), + Vdb1 = use_vars(union(A#k.us, A#k.ns), I, Vdb0), {Es,MaxI,Vdb2} = body(Kb, I+1, Vdb1), E = expr(Ke, I, Vdb2), {[E|Es],MaxI,Vdb2}; body(Ke, I, Vdb0) -> %%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]), A = get_kanno(Ke), - Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)), + Vdb1 = use_vars(union(A#k.us, A#k.ns), I, Vdb0), E = expr(Ke, I, Vdb1), {[E],I,Vdb1}. @@ -150,12 +151,12 @@ expr(#k_try_enter{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh}, I, Vdb) - %% the body and handler. Add try tag 'variable'. Ab = get_kanno(Kb), Ah = get_kanno(Kh), - Tdb1 = use_vars(Ab#k.us, I+3, use_vars(Ah#k.us, I+3, Tdb0)), + Tdb1 = use_vars(union(Ab#k.us, Ah#k.us), I+3, Tdb0), Tdb2 = vdb_sub(I, I+2, Tdb1), Vnames = fun (Kvar) -> Kvar#k_var.name end, %Get the variable names {Aes,_,Adb} = body(Ka, I+2, add_var({catch_tag,I+1}, I+1, 1000000, Tdb2)), - {Bes,_,Bdb} = body(Kb, I+4, new_vars(map(Vnames, Vs), I+3, Tdb2)), - {Hes,_,Hdb} = body(Kh, I+4, new_vars(map(Vnames, Evs), I+3, Tdb2)), + {Bes,_,Bdb} = body(Kb, I+4, new_vars(sort(map(Vnames, Vs)), I+3, Tdb2)), + {Hes,_,Hdb} = body(Kh, I+4, new_vars(sort(map(Vnames, Evs)), I+3, Tdb2)), #l{ke={try_enter,#l{ke={block,Aes},i=I+1,vdb=Adb,a=[]}, var_list(Vs),#l{ke={block,Bes},i=I+3,vdb=Bdb,a=[]}, var_list(Evs),#l{ke={block,Hes},i=I+3,vdb=Hdb,a=[]}}, @@ -171,7 +172,7 @@ expr(#k_receive{anno=A,var=V,body=Kb,timeout=T,action=Ka,ret=Rs}, I, Vdb) -> %% Work out imported variables which need to be locked. Rdb = vdb_sub(I, I+1, Vdb), M = match(Kb, add_element(V#k_var.name, A#k.us), I+1, [], - new_var(V#k_var.name, I, Rdb)), + new_vars([V#k_var.name], I, Rdb)), {Tes,_,Adb} = body(Ka, I+1, Rdb), #l{ke={receive_loop,atomic(T),variable(V),M, #l{ke=Tes,i=I+1,vdb=Adb,a=[]},var_list(Rs)}, @@ -199,12 +200,12 @@ body_try(#k_try{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh,ret=Rs}, %% the body and handler. Add try tag 'variable'. Ab = get_kanno(Kb), Ah = get_kanno(Kh), - Tdb1 = use_vars(Ab#k.us, I+3, use_vars(Ah#k.us, I+3, Tdb0)), + Tdb1 = use_vars(union(Ab#k.us, Ah#k.us), I+3, Tdb0), Tdb2 = vdb_sub(I, I+2, Tdb1), Vnames = fun (Kvar) -> Kvar#k_var.name end, %Get the variable names {Aes,_,Adb} = body(Ka, I+2, add_var({catch_tag,I+1}, I+1, locked, Tdb2)), - {Bes,_,Bdb} = body(Kb, I+4, new_vars(map(Vnames, Vs), I+3, Tdb2)), - {Hes,_,Hdb} = body(Kh, I+4, new_vars(map(Vnames, Evs), I+3, Tdb2)), + {Bes,_,Bdb} = body(Kb, I+4, new_vars(sort(map(Vnames, Vs)), I+3, Tdb2)), + {Hes,_,Hdb} = body(Kh, I+4, new_vars(sort(map(Vnames, Evs)), I+3, Tdb2)), #l{ke={'try',#l{ke={block,Aes},i=I+1,vdb=Adb,a=[]}, var_list(Vs),#l{ke={block,Bes},i=I+3,vdb=Bdb,a=[]}, var_list(Evs),#l{ke={block,Hes},i=I+3,vdb=Hdb,a=[]}, @@ -270,7 +271,7 @@ match(#k_select{anno=A,var=V,types=Kts}, Ls0, I, Ctxt, Vdb0) -> end, Vdb1 = use_vars(union(A#k.us, Ls1), I, Vdb0), Ts = [type_clause(Tc, Ls1, I+1, Ctxt, Vdb1) || Tc <- Kts], - #l{ke={select,literal2(V, Ctxt),Ts},i=I,vdb=Vdb1,a=Anno}; + #l{ke={select,literal(V, Ctxt),Ts},i=I,vdb=Vdb1,a=Anno}; match(#k_guard{anno=A,clauses=Kcs}, Ls, I, Ctxt, Vdb0) -> Vdb1 = use_vars(union(A#k.us, Ls), I, Vdb0), Cs = [guard_clause(G, Ls, I+1, Ctxt, Vdb1) || G <- Kcs], @@ -297,7 +298,7 @@ val_clause(#k_val_clause{anno=A,val=V,body=Kb}, Ls0, I, Ctxt0, Vdb0) -> _ -> Ctxt0 end, B = match(Kb, Ls1, I+1, Ctxt, Vdb1), - #l{ke={val_clause,literal2(V, Ctxt),B},i=I,vdb=use_vars(Bus, I+1, Vdb1),a=A#k.a}. + #l{ke={val_clause,literal(V, Ctxt),B},i=I,vdb=use_vars(Bus, I+1, Vdb1),a=A#k.a}. guard_clause(#k_guard_clause{anno=A,guard=Kg,body=Kb}, Ls, I, Ctxt, Vdb0) -> Vdb1 = use_vars(union(A#k.us, Ls), I+2, Vdb0), @@ -350,6 +351,7 @@ atomic_list(Ks) -> [atomic(K) || K <- Ks]. %% literal_list([Klit]) -> [Lit]. literal(#k_var{name=N}, _) -> {var,N}; +literal(#k_literal{val=I}, _) -> {literal,I}; literal(#k_int{val=I}, _) -> {integer,I}; literal(#k_float{val=F}, _) -> {float,F}; literal(#k_atom{val=N}, _) -> {atom,N}; @@ -358,58 +360,29 @@ literal(#k_nil{}, _) -> nil; literal(#k_cons{hd=H,tl=T}, Ctxt) -> {cons,[literal(H, Ctxt),literal(T, Ctxt)]}; literal(#k_binary{segs=V}, Ctxt) -> - {binary,literal(V, Ctxt)}; + {binary,literal(V, Ctxt)}; +literal(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=[]}, Ctxt) -> + %% Only occurs in patterns. + {bin_seg,Ctxt,literal(S, Ctxt),U,T,Fs,[literal(Seg, Ctxt)]}; literal(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=N}, Ctxt) -> {bin_seg,Ctxt,literal(S, Ctxt),U,T,Fs, [literal(Seg, Ctxt),literal(N, Ctxt)]}; +literal(#k_bin_int{size=S,unit=U,flags=Fs,val=Int,next=N}, Ctxt) -> + %% Only occurs in patterns. + {bin_int,Ctxt,literal(S, Ctxt),U,Fs,Int, + [literal(N, Ctxt)]}; literal(#k_bin_end{}, Ctxt) -> {bin_end,Ctxt}; literal(#k_tuple{es=Es}, Ctxt) -> {tuple,literal_list(Es, Ctxt)}; -literal(#k_map{op=Op,var=Var,es=Es}, Ctxt) -> - {map,Op,literal(Var, Ctxt),literal_list(Es, Ctxt)}; +literal(#k_map{op=Op,var=Var,es=Es0}, Ctxt) -> + {map,Op,literal(Var, Ctxt),literal_list(Es0, Ctxt)}; literal(#k_map_pair{key=K,val=V}, Ctxt) -> - {map_pair,literal(K, Ctxt),literal(V, Ctxt)}; -literal(#k_literal{val=V}, _Ctxt) -> - {literal,V}. + {map_pair,literal(K, Ctxt),literal(V, Ctxt)}. literal_list(Ks, Ctxt) -> [literal(K, Ctxt) || K <- Ks]. -literal2(#k_var{name=N}, _) -> {var,N}; -literal2(#k_literal{val=I}, _) -> {literal,I}; -literal2(#k_int{val=I}, _) -> {integer,I}; -literal2(#k_float{val=F}, _) -> {float,F}; -literal2(#k_atom{val=N}, _) -> {atom,N}; -%%literal2(#k_char{val=C}, _) -> {char,C}; -literal2(#k_nil{}, _) -> nil; -literal2(#k_cons{hd=H,tl=T}, Ctxt) -> - {cons,[literal2(H, Ctxt),literal2(T, Ctxt)]}; -literal2(#k_binary{segs=V}, Ctxt) -> - {binary,literal2(V, Ctxt)}; -literal2(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=[]}, Ctxt) -> - {bin_seg,Ctxt,literal2(S, Ctxt),U,T,Fs,[literal2(Seg, Ctxt)]}; -literal2(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=N}, Ctxt) -> - {bin_seg,Ctxt,literal2(S, Ctxt),U,T,Fs, - [literal2(Seg, Ctxt),literal2(N, Ctxt)]}; -literal2(#k_bin_int{size=S,unit=U,flags=Fs,val=Int,next=N}, Ctxt) -> - {bin_int,Ctxt,literal2(S, Ctxt),U,Fs,Int, - [literal2(N, Ctxt)]}; -literal2(#k_bin_end{}, Ctxt) -> - {bin_end,Ctxt}; -literal2(#k_tuple{es=Es}, Ctxt) -> - {tuple,literal_list2(Es, Ctxt)}; -literal2(#k_map{op=Op,es=Es}, Ctxt) -> - {map,Op,literal_list2(Es, Ctxt)}; -literal2(#k_map_pair{key=K,val=V}, Ctxt) -> - {map_pair,literal2(K, Ctxt),literal2(V, Ctxt)}. - -literal_list2(Ks, Ctxt) -> - [literal2(K, Ctxt) || K <- Ks]. - -%% literal_bin(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=N}) -> -%% {bin_seg,literal(S),U,T,Fs,[literal(Seg),literal(N)]} - %% is_gc_bif(Name, Arity) -> true|false %% Determines whether the BIF Name/Arity might do a GC. @@ -428,79 +401,78 @@ is_gc_bif(Bif, Arity) -> erl_internal:new_type_test(Bif, Arity) orelse erl_internal:comp_op(Bif, Arity)). -%% new_var(VarName, I, Vdb) -> Vdb. +%% Keep track of life time for variables. +%% +%% init_vars([{var,VarName}]) -> Vdb. %% new_vars([VarName], I, Vdb) -> Vdb. -%% use_var(VarName, I, Vdb) -> Vdb. %% use_vars([VarName], I, Vdb) -> Vdb. %% add_var(VarName, F, L, Vdb) -> Vdb. +%% +%% The list of variable names for new_vars/3 and use_vars/3 +%% must be sorted. -new_var(V, I, Vdb) -> - vdb_store_new(V, I, I, Vdb). +init_vars(Vs) -> + vdb_new(Vs). -new_vars(Vs, I, Vdb0) -> - foldl(fun (V, Vdb) -> new_var(V, I, Vdb) end, Vdb0, Vs). +new_vars([], _, Vdb) -> Vdb; +new_vars([V], I, Vdb) -> vdb_store_new(V, {V,I,I}, Vdb); +new_vars(Vs, I, Vdb) -> vdb_update_vars(Vs, Vdb, I). -use_var(V, I, Vdb) -> +use_vars([], _, Vdb) -> + Vdb; +use_vars([V], I, Vdb) -> case vdb_find(V, Vdb) of - {V,F,L} when I > L -> vdb_update(V, F, I, Vdb); + {V,F,L} when I > L -> vdb_update(V, {V,F,I}, Vdb); {V,_,_} -> Vdb; - error -> vdb_store_new(V, I, I, Vdb) - end. + error -> vdb_store_new(V, {V,I,I}, Vdb) + end; +use_vars(Vs, I, Vdb) -> vdb_update_vars(Vs, Vdb, I). -use_vars([], _, Vdb) -> Vdb; -use_vars([V], I, Vdb) -> use_var(V, I, Vdb); -use_vars(Vs, I, Vdb) -> - Res = use_vars_1(sort(Vs), Vdb, I), - %% The following line can be used as an assertion. - %% Res = foldl(fun (V, Vdb) -> use_var(V, I, Vdb) end, Vdb, Vs), - Res. +add_var(V, F, L, Vdb) -> + vdb_store_new(V, {V,F,L}, Vdb). -%% Measurements show that it is worthwhile having this special -%% function that updates/inserts several variables at once. +%% is_in_guard() -> true|false. -use_vars_1([V|_]=Vs, [{V1,_,_}=Vd|Vdb], I) when V > V1 -> - [Vd|use_vars_1(Vs, Vdb, I)]; -use_vars_1([V|Vs], [{V1,_,_}|_]=Vdb, I) when V < V1 -> - %% New variable. - [{V,I,I}|use_vars_1(Vs, Vdb, I)]; -use_vars_1([V|Vs], [{_,F,L}=Vd|Vdb], I) -> - %% Existing variable. - if - I > L ->[{V,F,I}|use_vars_1(Vs, Vdb, I)]; - true -> [Vd|use_vars_1(Vs, Vdb, I)] - end; -use_vars_1([V|Vs], [], I) -> - %% New variable. - [{V,I,I}|use_vars_1(Vs, [], I)]; -use_vars_1([], Vdb, _) -> Vdb. +is_in_guard() -> + get(guard_refc) > 0. -add_var(V, F, L, Vdb) -> - vdb_store_new(V, F, L, Vdb). +%% vdb + +vdb_new(Vs) -> + sort([{V,0,0} || {var,V} <- Vs]). vdb_find(V, Vdb) -> - %% Performance note: Profiling shows that this function accounts for - %% a lot of the execution time when huge constant terms are built. - %% Using the BIF lists:keyfind/3 is a lot faster than the - %% original Erlang version. case lists:keyfind(V, 1, Vdb) of false -> error; Vd -> Vd end. -%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V < V1 -> error; -%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V == V1 -> Vd; -%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V > V1 -> vdb_find(V, Vdb); -%vdb_find(V, []) -> error. +vdb_update(V, Update, [{V,_,_}|Vdb]) -> + [Update|Vdb]; +vdb_update(V, Update, [Vd|Vdb]) -> + [Vd|vdb_update(V, Update, Vdb)]. -vdb_update(V, F, L, [{V1,_,_}=Vd|Vdb]) when V > V1 -> - [Vd|vdb_update(V, F, L, Vdb)]; -vdb_update(V, F, L, [{V1,_,_}|Vdb]) when V == V1 -> - [{V,F,L}|Vdb]. +vdb_store_new(V, New, [{V1,_,_}=Vd|Vdb]) when V > V1 -> + [Vd|vdb_store_new(V, New, Vdb)]; +vdb_store_new(V, New, [{V1,_,_}|_]=Vdb) when V < V1 -> + [New|Vdb]; +vdb_store_new(_, New, []) -> [New]. -vdb_store_new(V, F, L, [{V1,_,_}=Vd|Vdb]) when V > V1 -> - [Vd|vdb_store_new(V, F, L, Vdb)]; -vdb_store_new(V, F, L, [{V1,_,_}|_]=Vdb) when V < V1 -> [{V,F,L}|Vdb]; -vdb_store_new(V, F, L, []) -> [{V,F,L}]. +vdb_update_vars([V|_]=Vs, [{V1,_,_}=Vd|Vdb], I) when V > V1 -> + [Vd|vdb_update_vars(Vs, Vdb, I)]; +vdb_update_vars([V|Vs], [{V1,_,_}|_]=Vdb, I) when V < V1 -> + %% New variable. + [{V,I,I}|vdb_update_vars(Vs, Vdb, I)]; +vdb_update_vars([V|Vs], [{_,F,L}=Vd|Vdb], I) -> + %% Existing variable. + if + I > L -> [{V,F,I}|vdb_update_vars(Vs, Vdb, I)]; + true -> [Vd|vdb_update_vars(Vs, Vdb, I)] + end; +vdb_update_vars([V|Vs], [], I) -> + %% New variable. + [{V,I,I}|vdb_update_vars(Vs, [], I)]; +vdb_update_vars([], Vdb, _) -> Vdb. %% vdb_sub(Min, Max, Vdb) -> Vdb. %% Extract variables which are used before and after Min. Lock @@ -510,8 +482,3 @@ vdb_sub(Min, Max, Vdb) -> [ if L >= Max -> {V,F,locked}; true -> Vd end || {V,F,L}=Vd <- Vdb, F < Min, L >= Min ]. - -%% is_in_guard() -> true|false. - -is_in_guard() -> - get(guard_refc) > 0. diff --git a/lib/compiler/src/v3_life.hrl b/lib/compiler/src/v3_life.hrl index 541e4cf66d..9d03a86ccd 100644 --- a/lib/compiler/src/v3_life.hrl +++ b/lib/compiler/src/v3_life.hrl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index 0b56a49cd6..f0185acbc7 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -8,9 +8,16 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES= \ andor_SUITE \ apply_SUITE \ + beam_block_SUITE \ + beam_bool_SUITE \ beam_validator_SUITE \ beam_disasm_SUITE \ beam_except_SUITE \ + beam_jump_SUITE \ + beam_reorder_SUITE \ + beam_type_SUITE \ + beam_utils_SUITE \ + bif_SUITE \ bs_bincomp_SUITE \ bs_bit_binaries_SUITE \ bs_construct_SUITE \ @@ -32,14 +39,23 @@ MODULES= \ num_bif_SUITE \ receive_SUITE \ record_SUITE \ + regressions_SUITE \ trycatch_SUITE \ warnings_SUITE \ + z_SUITE \ test_lib NO_OPT= \ andor \ apply \ + beam_block \ + beam_bool \ beam_except \ + beam_jump \ + beam_reorder \ + beam_type \ + beam_utils \ + bif \ bs_construct \ bs_match \ bs_utf \ @@ -59,6 +75,9 @@ NO_OPT= \ INLINE= \ andor \ apply \ + beam_block \ + beam_bool \ + beam_utils \ bs_bincomp \ bs_bit_binaries \ bs_construct \ @@ -76,12 +95,6 @@ INLINE= \ receive \ record -CORE_MODULES = \ - bs_shadowed_size_var \ - unused_multiple_values_error \ - nested_call_in_case - - NO_OPT_MODULES= $(NO_OPT:%=%_no_opt_SUITE) NO_OPT_ERL_FILES= $(NO_OPT_MODULES:%=%.erl) POST_OPT_MODULES= $(NO_OPT:%=%_post_opt_SUITE) @@ -91,8 +104,6 @@ INLINE_ERL_FILES= $(INLINE_MODULES:%=%.erl) ERL_FILES= $(MODULES:%=%.erl) -CORE_FILES= $(CORE_MODULES:%=%.core) - ##TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) ##INSTALL_PROGS= $(TARGET_FILES) @@ -108,7 +119,7 @@ RELSYSDIR = $(RELEASE_PATH)/compiler_test # ---------------------------------------------------- ERL_MAKE_FLAGS += -ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include +clint +ERL_COMPILE_FLAGS += +clint +clint0 EBIN = . @@ -159,7 +170,7 @@ release_spec: opt release_tests_spec: make_emakefile $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) compiler.spec compiler.cover \ - $(EMAKEFILE) $(ERL_FILES) $(CORE_FILES) "$(RELSYSDIR)" + $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \ $(INLINE_ERL_FILES) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl index b5408ecd8f..05c087104d 100644 --- a/lib/compiler/test/andor_SUITE.erl +++ b/lib/compiler/test/andor_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2013. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -21,10 +22,9 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, t_case/1,t_and_or/1,t_andalso/1,t_orelse/1,inside/1,overlap/1, - combined/1,in_case/1,before_and_inside_if/1, - slow_compilation/1]). + combined/1,in_case/1,slow_compilation/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -33,9 +33,9 @@ all() -> [{group,p}]. groups() -> - [{p,test_lib:parallel(), + [{p,[parallel], [t_case,t_and_or,t_andalso,t_orelse,inside,overlap, - combined,in_case,before_and_inside_if]}]. + combined,in_case,slow_compilation]}]. init_per_suite(Config) -> Config. @@ -53,28 +53,28 @@ end_per_group(_GroupName, Config) -> t_case(Config) when is_list(Config) -> %% We test boolean cases almost but not quite like cases %% generated by andalso/orelse. - ?line less = t_case_a(1, 2), - ?line not_less = t_case_a(2, 2), - ?line {'EXIT',{{case_clause,false},_}} = (catch t_case_b({x,y,z}, 2)), - ?line {'EXIT',{{case_clause,true},_}} = (catch t_case_b(a, a)), - ?line eq = t_case_c(a, a), - ?line ne = t_case_c(42, []), - ?line t = t_case_d(x, x, true), - ?line f = t_case_d(x, x, false), - ?line f = t_case_d(x, y, true), - ?line {'EXIT',{badarg,_}} = (catch t_case_d(x, y, blurf)), - ?line true = (catch t_case_e({a,b}, {a,b})), - ?line false = (catch t_case_e({a,b}, 42)), - - ?line true = t_case_xy(42, 100, 700), - ?line true = t_case_xy(42, 100, whatever), - ?line false = t_case_xy(42, wrong, 700), - ?line false = t_case_xy(42, wrong, whatever), - - ?line true = t_case_xy(0, whatever, 700), - ?line true = t_case_xy(0, 100, 700), - ?line false = t_case_xy(0, whatever, wrong), - ?line false = t_case_xy(0, 100, wrong), + less = t_case_a(1, 2), + not_less = t_case_a(2, 2), + {'EXIT',{{case_clause,false},_}} = (catch t_case_b({x,y,z}, 2)), + {'EXIT',{{case_clause,true},_}} = (catch t_case_b(a, a)), + eq = t_case_c(a, a), + ne = t_case_c(42, []), + t = t_case_d(x, x, true), + f = t_case_d(x, x, false), + f = t_case_d(x, y, true), + {'EXIT',{badarg,_}} = (catch t_case_d(x, y, blurf)), + true = (catch t_case_e({a,b}, {a,b})), + false = (catch t_case_e({a,b}, 42)), + + true = t_case_xy(42, 100, 700), + true = t_case_xy(42, 100, whatever), + false = t_case_xy(42, wrong, 700), + false = t_case_xy(42, wrong, whatever), + + true = t_case_xy(0, whatever, 700), + true = t_case_xy(0, 100, 700), + false = t_case_xy(0, whatever, wrong), + false = t_case_xy(0, 100, wrong), ok. @@ -134,35 +134,35 @@ t_case_y(X, Y, Z) -> end). t_and_or(Config) when is_list(Config) -> - ?line true = true and true, - ?line false = true and false, - ?line false = false and true, - ?line false = false and false, - - ?line true = id(true) and true, - ?line false = id(true) and false, - ?line false = id(false) and true, - ?line false = id(false) and false, - - ?line true = true and id(true), - ?line false = true and id(false), - ?line false = false and id(true), - ?line false = false and id(false), - - ?line true = true or true, - ?line true = true or false, - ?line true = false or true, - ?line false = false or false, - - ?line true = id(true) or true, - ?line true = id(true) or false, - ?line true = id(false) or true, - ?line false = id(false) or false, - - ?line true = true or id(true), - ?line true = true or id(false), - ?line true = false or id(true), - ?line false = false or id(false), + true = true and true, + false = true and false, + false = false and true, + false = false and false, + + true = id(true) and true, + false = id(true) and false, + false = id(false) and true, + false = id(false) and false, + + true = true and id(true), + false = true and id(false), + false = false and id(true), + false = false and id(false), + + true = true or true, + true = true or false, + true = false or true, + false = false or false, + + true = id(true) or true, + true = id(true) or false, + true = id(false) or true, + false = id(false) or false, + + true = true or id(true), + true = true or id(false), + true = false or id(true), + false = false or id(false), True = id(true), @@ -173,35 +173,41 @@ t_and_or(Config) when is_list(Config) -> true = (fun (X = true) when X or true or X -> true end)(True), - ok. + Tuple = id({a,b}), + case Tuple of + {_,_} -> + {'EXIT',{badarg,_}} = (catch true and Tuple) + end, + + ok. t_andalso(Config) when is_list(Config) -> Bs = [true,false], Ps = [{X,Y} || X <- Bs, Y <- Bs], lists:foreach(fun (P) -> t_andalso_1(P) end, Ps), - ?line true = true andalso true, - ?line false = true andalso false, - ?line false = false andalso true, - ?line false = false andalso false, + true = true andalso true, + false = true andalso false, + false = false andalso true, + false = false andalso false, - ?line true = ?GUARD(true andalso true), - ?line false = ?GUARD(true andalso false), - ?line false = ?GUARD(false andalso true), - ?line false = ?GUARD(false andalso false), + true = ?GUARD(true andalso true), + false = ?GUARD(true andalso false), + false = ?GUARD(false andalso true), + false = ?GUARD(false andalso false), - ?line false = false andalso glurf, - ?line false = false andalso exit(exit_now), + false = false andalso glurf, + false = false andalso exit(exit_now), - ?line true = not id(false) andalso not id(false), - ?line false = not id(false) andalso not id(true), - ?line false = not id(true) andalso not id(false), - ?line false = not id(true) andalso not id(true), + true = not id(false) andalso not id(false), + false = not id(false) andalso not id(true), + false = not id(true) andalso not id(false), + false = not id(true) andalso not id(true), - ?line {'EXIT',{badarg,_}} = (catch not id(glurf) andalso id(true)), - ?line {'EXIT',{badarg,_}} = (catch not id(false) andalso not id(glurf)), - ?line false = id(false) andalso not id(glurf), - ?line false = false andalso not id(glurf), + {'EXIT',{badarg,_}} = (catch not id(glurf) andalso id(true)), + {'EXIT',{badarg,_}} = (catch not id(false) andalso not id(glurf)), + false = id(false) andalso not id(glurf), + false = false andalso not id(glurf), true = begin (X1 = true) andalso X1, X1 end, false = false = begin (X2 = false) andalso X2, X2 end, @@ -213,28 +219,28 @@ t_orelse(Config) when is_list(Config) -> Ps = [{X,Y} || X <- Bs, Y <- Bs], lists:foreach(fun (P) -> t_orelse_1(P) end, Ps), - ?line true = true orelse true, - ?line true = true orelse false, - ?line true = false orelse true, - ?line false = false orelse false, + true = true orelse true, + true = true orelse false, + true = false orelse true, + false = false orelse false, - ?line true = ?GUARD(true orelse true), - ?line true = ?GUARD(true orelse false), - ?line true = ?GUARD(false orelse true), - ?line false = ?GUARD(false orelse false), + true = ?GUARD(true orelse true), + true = ?GUARD(true orelse false), + true = ?GUARD(false orelse true), + false = ?GUARD(false orelse false), - ?line true = true orelse glurf, - ?line true = true orelse exit(exit_now), + true = true orelse glurf, + true = true orelse exit(exit_now), - ?line true = not id(false) orelse not id(false), - ?line true = not id(false) orelse not id(true), - ?line true = not id(true) orelse not id(false), - ?line false = not id(true) orelse not id(true), + true = not id(false) orelse not id(false), + true = not id(false) orelse not id(true), + true = not id(true) orelse not id(false), + false = not id(true) orelse not id(true), - ?line {'EXIT',{badarg,_}} = (catch not id(glurf) orelse id(true)), - ?line {'EXIT',{badarg,_}} = (catch not id(true) orelse not id(glurf)), - ?line true = id(true) orelse not id(glurf), - ?line true = true orelse not id(glurf), + {'EXIT',{badarg,_}} = (catch not id(glurf) orelse id(true)), + {'EXIT',{badarg,_}} = (catch not id(true) orelse not id(glurf)), + true = id(true) orelse not id(glurf), + true = true orelse not id(glurf), true = begin (X1 = true) orelse X1, X1 end, false = begin (X2 = false) orelse X2, X2 end, @@ -248,7 +254,7 @@ t_andalso_1({X,Y}) -> X andalso Y -> true; true -> false end, - check(V1, X and Y). + V1 = id(X and Y). t_orelse_1({X,Y}) -> io:fwrite("~w orelse ~w: ",[X,Y]), @@ -257,19 +263,19 @@ t_orelse_1({X,Y}) -> X orelse Y -> true; true -> false end, - check(V1, X or Y). + V1 = id(X or Y). inside(Config) when is_list(Config) -> - ?line true = inside(-8, 1), - ?line false = inside(-53.5, -879798), - ?line false = inside(1.0, -879), - ?line false = inside(59, -879), - ?line false = inside(-11, 1.0), - ?line false = inside(100, 0.2), - ?line false = inside(100, 1.2), - ?line false = inside(-53.5, 4), - ?line false = inside(1.0, 5.3), - ?line false = inside(59, 879), + true = inside(-8, 1), + false = inside(-53.5, -879798), + false = inside(1.0, -879), + false = inside(59, -879), + false = inside(-11, 1.0), + false = inside(100, 0.2), + false = inside(100, 1.2), + false = inside(-53.5, 4), + false = inside(1.0, 5.3), + false = inside(59, 879), ok. inside(Xm, Ym) -> @@ -304,15 +310,15 @@ inside_guard(Xm, Ym, X, Y, W, H) -> {false,Xm,Ym,X,Y,W,H}. overlap(Config) when is_list(Config) -> - ?line true = overlap(7.0, 2.0, 8.0, 0.5), - ?line true = overlap(7.0, 2.0, 8.0, 2.5), - ?line true = overlap(7.0, 2.0, 5.3, 2), - ?line true = overlap(7.0, 2.0, 0.0, 100.0), - - ?line false = overlap(-1, 2, -35, 0.5), - ?line false = overlap(-1, 2, 777, 0.5), - ?line false = overlap(-1, 2, 2, 10), - ?line false = overlap(2, 10, 12, 55.3), + true = overlap(7.0, 2.0, 8.0, 0.5), + true = overlap(7.0, 2.0, 8.0, 2.5), + true = overlap(7.0, 2.0, 5.3, 2), + true = overlap(7.0, 2.0, 0.0, 100.0), + + false = overlap(-1, 2, -35, 0.5), + false = overlap(-1, 2, 777, 0.5), + false = overlap(-1, 2, 2, 10), + false = overlap(2, 10, 12, 55.3), ok. overlap(Pos1, Len1, Pos2, Len2) -> @@ -336,33 +342,38 @@ overlap(Pos1, Len1, Pos2, Len2) -> -define(COMB(A,B,C), (A andalso B orelse C)). combined(Config) when is_list(Config) -> - ?line false = comb(false, false, false), - ?line true = comb(false, false, true), - ?line false = comb(false, true, false), - ?line true = comb(false, true, true), - - ?line false = comb(true, false, false), - ?line true = comb(true, true, false), - ?line true = comb(true, false, true), - ?line true = comb(true, true, true), - - ?line false = comb(false, blurf, false), - ?line true = comb(false, blurf, true), - ?line true = comb(true, true, blurf), - - ?line false = ?COMB(false, false, false), - ?line true = ?COMB(false, false, true), - ?line false = ?COMB(false, true, false), - ?line true = ?COMB(false, true, true), - - ?line false = ?COMB(true, false, false), - ?line true = ?COMB(true, true, false), - ?line true = ?COMB(true, false, true), - ?line true = ?COMB(true, true, true), - - ?line false = ?COMB(false, blurf, false), - ?line true = ?COMB(false, blurf, true), - ?line true = ?COMB(true, true, blurf), + false = comb(false, false, false), + true = comb(false, false, true), + false = comb(false, true, false), + true = comb(false, true, true), + + false = comb(true, false, false), + true = comb(true, true, false), + true = comb(true, false, true), + true = comb(true, true, true), + + false = comb(false, blurf, false), + true = comb(false, blurf, true), + true = comb(true, true, blurf), + + false = ?COMB(false, false, false), + true = ?COMB(false, false, true), + false = ?COMB(false, true, false), + true = ?COMB(false, true, true), + + false = ?COMB(true, false, false), + true = ?COMB(true, true, false), + true = ?COMB(true, false, true), + true = ?COMB(true, true, true), + + false = ?COMB(false, blurf, false), + true = ?COMB(false, blurf, true), + true = ?COMB(true, true, blurf), + + false = simple_comb(false, false), + false = simple_comb(false, true), + false = simple_comb(true, false), + true = simple_comb(true, true), ok. -undef(COMB). @@ -390,16 +401,23 @@ comb(A, B, C) -> end, id(Res). +simple_comb(A, B) -> + %% Use Res twice, to ensure that a careless optimization of 'not' + %% doesn't leave Res as a free variable. + Res = A andalso B, + _ = id(not Res), + Res. + %% Test that a boolean expression in a case expression is properly %% optimized (in particular, that the error behaviour is correct). in_case(Config) when is_list(Config) -> - ?line edge_rings = in_case_1(1, 1, 1, 1, 1), - ?line not_loop = in_case_1(0.5, 1, 1, 1, 1), - ?line loop = in_case_1(0.5, 0.9, 1.1, 1, 4), - ?line {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, 0)), - ?line {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, nan)), - ?line {'EXIT',{badarg,_}} = (catch in_case_1(1, 1, 1, blurf, 1)), - ?line {'EXIT',{badarith,_}} = (catch in_case_1([nan], 1, 1, 1, 1)), + edge_rings = in_case_1(1, 1, 1, 1, 1), + not_loop = in_case_1(0.5, 1, 1, 1, 1), + loop = in_case_1(0.5, 0.9, 1.1, 1, 4), + {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, 0)), + {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, nan)), + {'EXIT',{badarg,_}} = (catch in_case_1(1, 1, 1, blurf, 1)), + {'EXIT',{badarith,_}} = (catch in_case_1([nan], 1, 1, 1, 1)), ok. in_case_1(LenUp, LenDw, LenN, Rotation, Count) -> @@ -431,64 +449,6 @@ in_case_1_guard(LenUp, LenDw, LenN, Rotation, Count) -> false -> loop end. -before_and_inside_if(Config) when is_list(Config) -> - ?line no = before_and_inside_if([a], [b], delete), - ?line no = before_and_inside_if([a], [b], x), - ?line no = before_and_inside_if([a], [], delete), - ?line no = before_and_inside_if([a], [], x), - ?line no = before_and_inside_if([], [], delete), - ?line yes = before_and_inside_if([], [], x), - ?line yes = before_and_inside_if([], [b], delete), - ?line yes = before_and_inside_if([], [b], x), - - ?line {ch1,ch2} = before_and_inside_if_2([a], [b], blah), - ?line {ch1,ch2} = before_and_inside_if_2([a], [b], xx), - ?line {ch1,ch2} = before_and_inside_if_2([a], [], blah), - ?line {ch1,ch2} = before_and_inside_if_2([a], [], xx), - ?line {no,no} = before_and_inside_if_2([], [b], blah), - ?line {no,no} = before_and_inside_if_2([], [b], xx), - ?line {ch1,no} = before_and_inside_if_2([], [], blah), - ?line {no,ch2} = before_and_inside_if_2([], [], xx), - ok. - -%% Thanks to Simon Cornish and Kostis Sagonas. -%% Used to crash beam_bool. -before_and_inside_if(XDo1, XDo2, Do3) -> - Do1 = (XDo1 =/= []), - Do2 = (XDo2 =/= []), - if - %% This expression occurs in a try/catch (protected) - %% block, which cannot refer to variables outside of - %% the block that are boolean expressions. - Do1 =:= true; - Do1 =:= false, Do2 =:= false, Do3 =:= delete -> - no; - true -> - yes - end. - -%% Thanks to Simon Cornish. -%% Used to generate code that would not set {y,0} on -%% all paths before its use (and therefore fail -%% validation by the beam_validator). -before_and_inside_if_2(XDo1, XDo2, Do3) -> - Do1 = (XDo1 =/= []), - Do2 = (XDo2 =/= []), - CH1 = if Do1 == true; - Do1 == false,Do2==false,Do3 == blah -> - ch1; - true -> - no - end, - CH2 = if Do1 == true; - Do1 == false,Do2==false,Do3 == xx -> - ch2; - true -> - no - end, - {CH1,CH2}. - - -record(state, {stack = []}). slow_compilation(_) -> @@ -520,14 +480,6 @@ slow_compilation_1(T, _) when element(1, T) == a -> %% Utilities. -check(V1, V0) -> - if V1 /= V0 -> - io:fwrite("error: ~w.\n", [V1]), - ?t:fail(); - true -> - io:fwrite("ok: ~w.\n", [V1]) - end. - echo(X) -> io:fwrite("eval(~w); ",[X]), X. diff --git a/lib/compiler/test/apply_SUITE.erl b/lib/compiler/test/apply_SUITE.erl index 25f8a8dfb5..cca92e4713 100644 --- a/lib/compiler/test/apply_SUITE.erl +++ b/lib/compiler/test/apply_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -23,7 +24,7 @@ -export([foo/0,bar/1,baz/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -52,44 +53,44 @@ end_per_group(_GroupName, Config) -> -define(APPLY2(M, F, A1, A2), (fun(Res) -> Res = M:F(A1, A2) end)(apply(M, F, [A1,A2]))). mfa(Config) when is_list(Config) -> - ?line ok = ?APPLY0(?MODULE, foo), - ?line {[a,b]} = ?APPLY1(?MODULE, bar, [a,b]), - ?line {39,{a}} = ?APPLY2(?MODULE, baz, 39, {a}), - - ?line Mod = id(?MODULE), - ?line ok = ?APPLY0(Mod, foo), - ?line {[a,b]} = ?APPLY1(Mod, bar, [a,b]), - ?line {39,{a}} = ?APPLY2(Mod, baz, 39, {a}), - - ?line ok = ?APPLY0(?MODULE, (id(foo))), - ?line {[a,b]} = ?APPLY1(?MODULE, (id(bar)), [a,b]), - ?line {39,{a}} = ?APPLY2(?MODULE, (id(baz)), 39, {a}), - - ?line ok = ?APPLY0(Mod, (id(foo))), - ?line {[a,b]} = ?APPLY1(Mod, (id(bar)), [a,b]), - ?line {39,{a}} = ?APPLY2(Mod, (id(baz)), 39, {a}), - - ?line {'EXIT',_} = (catch ?APPLY2(Mod, (id(bazzzzzz)), a, b)), - ?line {'EXIT',_} = (catch ?APPLY2({}, baz, a, b)), - ?line {'EXIT',_} = (catch ?APPLY2(?MODULE, [], a, b)), - - ?line ok = apply(Mod, foo, id([])), - ?line {[a,b|c]} = apply(Mod, bar, id([[a,b|c]])), - ?line {[xx],{a}} = apply(?MODULE, baz, id([[xx],{a}])), - - ?line Erlang = id(erlang), - ?line Self = self(), - ?line Self = ?APPLY0(Erlang, self), - ?line 42.0 = ?APPLY1(Erlang, abs, -42.0), - ?line b = ?APPLY2(Erlang, element, 2, {a,b,c}), - ?line true = ?APPLY1(Erlang, is_function, fun erlang:list_to_binary/1), - ?line true = ?APPLY1(Erlang, is_function, fun() -> ok end), - ?line false = ?APPLY1(Erlang, is_function, blurf), - ?line true = ?APPLY2(Erlang, is_function, fun erlang:list_to_binary/1, 1), - ?line true = ?APPLY2(Erlang, is_function, fun() -> ok end, 0), - ?line false = ?APPLY2(Erlang, is_function, blurf, 0), - - ?line apply(Mod, foo, []). + ok = ?APPLY0(?MODULE, foo), + {[a,b]} = ?APPLY1(?MODULE, bar, [a,b]), + {39,{a}} = ?APPLY2(?MODULE, baz, 39, {a}), + + Mod = id(?MODULE), + ok = ?APPLY0(Mod, foo), + {[a,b]} = ?APPLY1(Mod, bar, [a,b]), + {39,{a}} = ?APPLY2(Mod, baz, 39, {a}), + + ok = ?APPLY0(?MODULE, (id(foo))), + {[a,b]} = ?APPLY1(?MODULE, (id(bar)), [a,b]), + {39,{a}} = ?APPLY2(?MODULE, (id(baz)), 39, {a}), + + ok = ?APPLY0(Mod, (id(foo))), + {[a,b]} = ?APPLY1(Mod, (id(bar)), [a,b]), + {39,{a}} = ?APPLY2(Mod, (id(baz)), 39, {a}), + + {'EXIT',_} = (catch ?APPLY2(Mod, (id(bazzzzzz)), a, b)), + {'EXIT',_} = (catch ?APPLY2({}, baz, a, b)), + {'EXIT',_} = (catch ?APPLY2(?MODULE, [], a, b)), + + ok = apply(Mod, foo, id([])), + {[a,b|c]} = apply(Mod, bar, id([[a,b|c]])), + {[xx],{a}} = apply(?MODULE, baz, id([[xx],{a}])), + + Erlang = id(erlang), + Self = self(), + Self = ?APPLY0(Erlang, self), + 42.0 = ?APPLY1(Erlang, abs, -42.0), + b = ?APPLY2(Erlang, element, 2, {a,b,c}), + true = ?APPLY1(Erlang, is_function, fun erlang:list_to_binary/1), + true = ?APPLY1(Erlang, is_function, fun() -> ok end), + false = ?APPLY1(Erlang, is_function, blurf), + true = ?APPLY2(Erlang, is_function, fun erlang:list_to_binary/1, 1), + true = ?APPLY2(Erlang, is_function, fun() -> ok end, 0), + false = ?APPLY2(Erlang, is_function, blurf, 0), + + apply(Mod, foo, []). foo() -> ok. @@ -105,21 +106,21 @@ baz(A, B) -> -define(FUNAPPLY2(F, A1, A2), (fun(Res) -> Res = F(A1, A2) end)(apply(F, [A1,A2]))). fun_apply(Config) when is_list(Config) -> - ?line Self = self(), + Self = self(), - ?line Self = ?FUNAPPLY0(fun() -> self() end), - ?line Self = ?FUNAPPLY0((id(fun() -> self() end))), - ?line ok = ?FUNAPPLY0(fun ?MODULE:foo/0), - ?line ok = ?FUNAPPLY0((id(fun ?MODULE:foo/0))), + Self = ?FUNAPPLY0(fun() -> self() end), + Self = ?FUNAPPLY0((id(fun() -> self() end))), + ok = ?FUNAPPLY0(fun ?MODULE:foo/0), + ok = ?FUNAPPLY0((id(fun ?MODULE:foo/0))), - ?line -42 = ?FUNAPPLY1(fun(A) -> -A end, 42), - ?line [x,yy] = ?FUNAPPLY1((id(fun(T) -> [x|T] end)), [yy]), - ?line {[a|b]} = ?FUNAPPLY1(fun ?MODULE:bar/1, [a|b]), - ?line {[a|b]} = ?FUNAPPLY1((id(fun ?MODULE:bar/1)), [a|b]), + -42 = ?FUNAPPLY1(fun(A) -> -A end, 42), + [x,yy] = ?FUNAPPLY1((id(fun(T) -> [x|T] end)), [yy]), + {[a|b]} = ?FUNAPPLY1(fun ?MODULE:bar/1, [a|b]), + {[a|b]} = ?FUNAPPLY1((id(fun ?MODULE:bar/1)), [a|b]), - ?line {a,b} = ?FUNAPPLY2(fun(A, B) -> {A,B} end, a, b), - ?line {a,[b]} = ?FUNAPPLY2((id(fun(A, B) -> {A,B} end)), a, [b]), - ?line {42,{a}} = ?FUNAPPLY2((id(fun ?MODULE:baz/2)), 42, {a}), + {a,b} = ?FUNAPPLY2(fun(A, B) -> {A,B} end, a, b), + {a,[b]} = ?FUNAPPLY2((id(fun(A, B) -> {A,B} end)), a, [b]), + {42,{a}} = ?FUNAPPLY2((id(fun ?MODULE:baz/2)), 42, {a}), ok. diff --git a/lib/compiler/test/beam_block_SUITE.erl b/lib/compiler/test/beam_block_SUITE.erl new file mode 100644 index 0000000000..9fcb6e497d --- /dev/null +++ b/lib/compiler/test/beam_block_SUITE.erl @@ -0,0 +1,222 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015-2016. 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% +%% +-module(beam_block_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1, + erl_202/1]). + +%% The only test for the following functions is that +%% the code compiles and is accepted by beam_validator. +-export([encode_wildcards3/4,find_operands/4]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +groups() -> + [{p,[parallel], + [get_map_elements, + otp_7345, + move_opt_across_gc_bif, + erl_202 + ]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +get_map_elements(_Config) -> + [{pred,var}] = get_map_elements([{pred,var}], #{}, []), + [{pred,var}] = get_map_elements([{pred,var}], #{pred=>[]}, []), + acc = get_map_elements([], #{pred=>[]}, acc), + ok. + +get_map_elements([{Pred,Var}|Left], Map, Acc) -> + case Map of + #{Var := List} -> + case lists:keyfind(Pred, 1, List) of + false -> + get_map_elements(Left, Map, [{Pred,Var}|Acc]) + end; + #{} -> + get_map_elements(Left, Map, [{Pred,Var}|Acc]) + end; +get_map_elements([], _Map, Acc) -> + Acc. + +%% The following code +%% +%% {get_list,{x,2},{x,0},{x,1}}. +%% {gc_bif,length,{f,0},1,[{x,0}],{x,0}}. +%% {move,{x,0},{x,1}}. +%% +%% was incorrectly optimized to +%% +%% {get_list,{x,2},{x,0},{y,0}}. +%% {gc_bif,length,{f,0},3,[{x,0}],{x,1}}. +%% +%% because beam_block:is_transparent({x,1}, +%% {gc_bif,length,{f,0},3,[{x,0}],{x,1}} +%% incorrectly returned true. + +-record(contextId,{cid,device_type,contextRef}). +-record(dpRef,{cid,tlli,ms_device_context_id}). +-record(qosProfileBssgp,{peak_bit_rate_msb, + peak_bit_rate_lsb, + t_a_precedence}). +-record(llUnitdataReq,{sapi, + l3_pdu_length, + pdu_life}). +-record(ptmsi,{value}). + +otp_7345(_Config) -> + #llUnitdataReq{l3_pdu_length=3,pdu_life=4} = + otp_7345(#contextId{}, 0, [[1,2,3],4,5]). + + +otp_7345(ObjRef, _RdEnv, Args) -> + Cid = ObjRef#contextId.cid, + _ = #dpRef{cid = Cid, + ms_device_context_id = cid_id, + tlli = #ptmsi{value = 0}}, + _ = #qosProfileBssgp{peak_bit_rate_msb = 0, + peak_bit_rate_lsb = 80, + t_a_precedence = 49}, + [Cpdu|_] = Args, + LlUnitdataReq = + #llUnitdataReq{sapi = 7, + l3_pdu_length = length(Cpdu), + pdu_life = + id(42) + div + 10}, + id(LlUnitdataReq). + + +%% Doing move optimizations across GC bifs are in general not safe. +move_opt_across_gc_bif(_Config) -> + [0,true,1] = positive(speaking), + ok. + +positive(speaking) -> + try + Positive = 0, + [+Positive, case Positive of _ -> true end, paris([], Positive)] + after + mailing + end. + +paris([], P) -> P + 1. + + +%% See https://bugs.erlang.org/browse/ERL-202. +%% Test that move_allocates/1 in beam_block doesn't move allocate +%% when it would not be safe. + +-record(erl_202_r1, {y}). +-record(erl_202_r2, {x}). + +erl_202(_Config) -> + Ref = make_ref(), + Ref = erl_202({{1,2},Ref}, 42), + + {Ref} = erl_202({7,8}, #erl_202_r1{y=#erl_202_r2{x=Ref}}), + + ok. + +erl_202({{_, _},X}, _) -> + X; +erl_202({_, _}, #erl_202_r1{y=R2}) -> + {R2#erl_202_r2.x}. + +%%% +%%% The only test of the following code is that it compiles. +%%% + +%% Slightly simplifed from megaco_binary_term_id_gen. +%% beam_block failed to note that the {gc_bif,'-'...} instruction could +%% fail, and that therefore {y,0} need to be initialized. +%% {allocate,8,6}. +%% %% {init,{y,0}} needed here. +%% {get_list,{x,1},{x,6},{x,7}}. +%% {'catch',{y,7},{f,3}}. +%% {move,{x,4},{y,1}}. +%% {move,{x,3},{y,2}}. +%% {move,{x,2},{y,3}}. +%% {move,{x,5},{y,4}}. +%% {move,{x,7},{y,5}}. +%% {move,{x,6},{y,6}}. +%% {gc_bif,'-',{f,0},8,[{x,3},{x,6}],{x,0}}. +%% {move,{x,0},{y,0}}. + +encode_wildcards3([],[],_,_) -> []; +encode_wildcards3([Level|Levels],[BitsInLevel|BitsRest],LevelNo,TotSize) -> + case (catch ?MODULE:encode_wildcard(Level,BitsInLevel,TotSize-BitsInLevel, + length(Levels))) of + {'EXIT',{Reason,Info}} -> + exit({Reason,{LevelNo,Info}}); + + no_wildcard -> + encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel); + + {level,Wl} -> + [Wl| + encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel)]; + + {recursive,Wr} -> + [Wr] + end. + +%% Slightly simplified code from hipe_rtl_ssapre. +%% beam_block used to do the following incorrect optimization: +%% +%% {gc_bif,length,{f,0},1,[{x,0}],{x,3}}. +%% ^^^^^ Was {x,0} - changing to {x,3} is not safe. +%% {gc_bif,'+',{f,0},0,[{y,2},{integer,1}],{x,0}}. +%% ^^^ Only one register live +%% . . . +%% {call_last,4,{f,2},4}. %% beam_validator noted that {x,3} wasn't live. + +find_operands(Cfg,XsiGraph,[],_Count) -> + {Cfg,XsiGraph}; +find_operands(Cfg,XsiGraph,ActiveList,Count) -> + {NewCfg,TempActiveList}=?MODULE:find_operands_for_active_list(Cfg,XsiGraph, + ActiveList,[]), + NewActiveList=lists:reverse(TempActiveList), + [Count+1, length(NewActiveList), length(digraph:vertices(XsiGraph))], + find_operands(NewCfg,XsiGraph,NewActiveList,Count+1). + +%%% +%%% Common functions. +%%% + +id(I) -> I. diff --git a/lib/compiler/test/beam_bool_SUITE.erl b/lib/compiler/test/beam_bool_SUITE.erl new file mode 100644 index 0000000000..e585eaedb5 --- /dev/null +++ b/lib/compiler/test/beam_bool_SUITE.erl @@ -0,0 +1,197 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015-2016. 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% +%% +-module(beam_bool_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + before_and_inside_if/1, + scotland/1,y_registers/1,protected/1, + maps/1]). + +suite() -> + [{ct_hooks,[ts_install_cth]}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +groups() -> + [{p,[parallel], + [before_and_inside_if, + scotland, + y_registers, + protected, + maps + ]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +before_and_inside_if(_Config) -> + no = before_and_inside_if([a], [b], delete), + no = before_and_inside_if([a], [b], x), + no = before_and_inside_if([a], [], delete), + no = before_and_inside_if([a], [], x), + no = before_and_inside_if([], [], delete), + yes = before_and_inside_if([], [], x), + yes = before_and_inside_if([], [b], delete), + yes = before_and_inside_if([], [b], x), + + {ch1,ch2} = before_and_inside_if_2([a], [b], blah), + {ch1,ch2} = before_and_inside_if_2([a], [b], xx), + {ch1,ch2} = before_and_inside_if_2([a], [], blah), + {ch1,ch2} = before_and_inside_if_2([a], [], xx), + {no,no} = before_and_inside_if_2([], [b], blah), + {no,no} = before_and_inside_if_2([], [b], xx), + {ch1,no} = before_and_inside_if_2([], [], blah), + {no,ch2} = before_and_inside_if_2([], [], xx), + ok. + +%% Thanks to Simon Cornish and Kostis Sagonas. +%% Used to crash beam_bool. +before_and_inside_if(XDo1, XDo2, Do3) -> + Do1 = (XDo1 =/= []), + Do2 = (XDo2 =/= []), + if + %% This expression occurs in a try/catch (protected) + %% block, which cannot refer to variables outside of + %% the block that are boolean expressions. + Do1 =:= true; + Do1 =:= false, Do2 =:= false, Do3 =:= delete -> + no; + true -> + yes + end. + +%% Thanks to Simon Cornish. +%% Used to generate code that would not set {y,0} on +%% all paths before its use (and therefore fail +%% validation by the beam_validator). +before_and_inside_if_2(XDo1, XDo2, Do3) -> + Do1 = (XDo1 =/= []), + Do2 = (XDo2 =/= []), + CH1 = if Do1 == true; + Do1 == false,Do2==false,Do3 == blah -> + ch1; + true -> + no + end, + CH2 = if Do1 == true; + Do1 == false,Do2==false,Do3 == xx -> + ch2; + true -> + no + end, + {CH1,CH2}. + + +%% beam_bool would remove the initialization of {y,0}. +%% (Thanks to Thomas Arts and QuickCheck.) + +scotland(_Config) -> + million = do_scotland(placed), + {'EXIT',{{badmatch,placed},_}} = (catch do_scotland(false)), + {'EXIT',{{badmatch,placed},_}} = (catch do_scotland(true)), + {'EXIT',{{badmatch,placed},_}} = (catch do_scotland(echo)), + ok. + +do_scotland(Echo) -> + found(case Echo of + Echo when true; Echo, Echo, Echo -> + Echo; + echo -> + [] + end, + Echo = placed). + +found(_, _) -> million. + + +%% ERL-143: beam_bool could not handle Y registers as a destination. +y_registers(_Config) -> + {'EXIT',{badarith,[_|_]}} = (catch baker(valentine)), + {'EXIT',{badarith,[_|_]}} = (catch baker(clementine)), + + {not_ok,true} = potter([]), + {ok,false} = potter([{encoding,any}]), + + ok. + +%% Thanks to Quickcheck. +baker(Baker) -> + (valentine == Baker) + + case Baker of + Baker when Baker; Baker -> + Baker; + Baker -> + [] + end. + +%% Thanks to Jose Valim. +potter(Modes) -> + Raw = lists:keyfind(encoding, 1, Modes) == false, + Final = case Raw of + X when X == false; X == nil -> ok; + _ -> not_ok + end, + {Final,Raw}. + +protected(_Config) -> + {'EXIT',{if_clause,_}} = (catch photographs({1, surprise, true}, opinions)), + + {{true}} = welcome({perfect, true}), + {'EXIT',{if_clause,_}} = (catch welcome({perfect, false})), + ok. + +photographs({_Violation, surprise, Deep}, opinions) -> + {if + 0; "here", Deep -> + Deep = Deep + end}. + +welcome({perfect, Profit}) -> + if + Profit, Profit, Profit; 0 -> + {id({Profit})} + end. + +maps(_Config) -> + ok = evidence(#{0 => 42}). + +%% Cover handling of put_map in in split_block_label_used/2. +evidence(#{0 := Charge}) when 0; #{[] => Charge} == #{[] => 42} -> + ok. + + +%%% +%%% Common utilities. +%%% + +id(I) -> + I. diff --git a/lib/compiler/test/beam_disasm_SUITE.erl b/lib/compiler/test/beam_disasm_SUITE.erl index 62afc80ca6..e2ec030d31 100644 --- a/lib/compiler/test/beam_disasm_SUITE.erl +++ b/lib/compiler/test/beam_disasm_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2012. All Rights Reserved. +%% Copyright Ericsson AB 2011-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% -module(beam_disasm_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). @@ -45,21 +46,20 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. -stripped(doc) -> - ["Check that stripped beam files can be disassembled"]; +%% Check that stripped beam files can be disassembled. stripped(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line SrcName = filename:join(PrivDir, "tmp.erl"), - ?line BeamName = filename:join(PrivDir, "tmp.beam"), + PrivDir = proplists:get_value(priv_dir, Config), + SrcName = filename:join(PrivDir, "tmp.erl"), + BeamName = filename:join(PrivDir, "tmp.beam"), Prog = <<"-module(tmp).\n-export([tmp/0]).\ntmp()->ok.\n">>, - ?line ok = file:write_file(SrcName, Prog), - ?line {ok, tmp} = + ok = file:write_file(SrcName, Prog), + {ok, tmp} = compile:file(SrcName, [{outdir, PrivDir}]), - ?line {beam_file, tmp, _, Attr, CompileInfo, [_|_]} = + {beam_file, tmp, _, Attr, CompileInfo, [_|_]} = beam_disasm:file(BeamName), - ?line true = is_list(Attr), - ?line true = is_list(CompileInfo), - ?line {ok, {tmp, _}} = beam_lib:strip(BeamName), - ?line {beam_file, tmp, _, [], [], [_|_]} = + true = is_list(Attr), + true = is_list(CompileInfo), + {ok, {tmp, _}} = beam_lib:strip(BeamName), + {beam_file, tmp, _, [], [], [_|_]} = beam_disasm:file(BeamName), ok. diff --git a/lib/compiler/test/beam_except_SUITE.erl b/lib/compiler/test/beam_except_SUITE.erl index d088863c5c..47367d6eab 100644 --- a/lib/compiler/test/beam_except_SUITE.erl +++ b/lib/compiler/test/beam_except_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2013. All Rights Reserved. +%% Copyright Ericsson AB 2011-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -20,15 +21,18 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - coverage/1]). + multiple_allocs/1,coverage/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [coverage]. + test_lib:recompile(?MODULE), + [{group,p}]. groups() -> - []. + [{p,[parallel], + [multiple_allocs, + coverage]}]. init_per_suite(Config) -> Config. @@ -42,6 +46,23 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +multiple_allocs(_Config) -> + {'EXIT',{{badmatch,#{true:=[p]}},_}} = + (catch could(pda, 0.0, {false,true}, {p})), + {'EXIT',{function_clause,_}} = (catch place(lee)), + {'EXIT',{{badmatch,wanted},_}} = (catch conditions()), + + ok. + +could(Coupons = pda, Favorite = _pleasure = 0.0, {_, true}, {Presents}) -> + (0 = true) = #{true => [Presents]}. + +place(lee) -> + (pregnancy = presentations) = [hours | [purchase || _ <- 0]] + wine. + +conditions() -> + (talking = going) = storage + [large = wanted]. + coverage(_) -> File = {file,"fake.erl"}, ok = fc(a), diff --git a/lib/compiler/test/beam_jump_SUITE.erl b/lib/compiler/test/beam_jump_SUITE.erl new file mode 100644 index 0000000000..088f63606c --- /dev/null +++ b/lib/compiler/test/beam_jump_SUITE.erl @@ -0,0 +1,74 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. 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% +%% +-module(beam_jump_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + undefined_label/1,ambiguous_catch_try_state/1]). + +suite() -> + [{ct_hooks,[ts_install_cth]}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +groups() -> + [{p,[parallel], + [undefined_label, + ambiguous_catch_try_state + ]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +undefined_label(_Config) -> + {'EXIT',{function_clause,_}} = (catch flights(0, [], [])), + ok. + +%% Would lose a label when compiled with no_copt. + +flights(0, [], []) when [], 0; 0.0, [], false -> + clark; +flights(_, Reproduction, introduction) when false, Reproduction -> + responsible. + +%% [ERL-209] beam_jump would share 'catch' blocks, causing an +%% ambiguous_catch_try_state error in beam_validator. + +ambiguous_catch_try_state(_Config) -> + {{'EXIT',{{case_clause,song},_}},{'EXIT',{{case_clause,song},_}}} = + checks(42), + ok. + +river() -> song. + +checks(Wanted) -> + %% Must be one line to cause the unsafe optimization. + {catch case river() of sheet -> begin +Wanted, if "da" -> Wanted end end end, catch case river() of sheet -> begin + Wanted, if "da" -> Wanted end end end}. diff --git a/lib/compiler/test/beam_reorder_SUITE.erl b/lib/compiler/test/beam_reorder_SUITE.erl new file mode 100644 index 0000000000..ff31f2d3bd --- /dev/null +++ b/lib/compiler/test/beam_reorder_SUITE.erl @@ -0,0 +1,81 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. 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% +%% +-module(beam_reorder_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + alloc/1,confused_beam_validator/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +groups() -> + [{p,[parallel], + [alloc, + confused_beam_validator + ]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +-record(alloc, {version}). + +alloc(_Config) -> + {ok,42} = alloc_a(1, 2, #alloc{version=42}), + {a,b,c} = alloc_b(1, 2, #alloc{version={a,b,c}}), + ok. + +alloc_a(_U1, _U2, R) -> + V = R#alloc.version, + Res = id({ok,V}), + _ = id(0), + Res. + +alloc_b(_U1, _U2, R) -> + V = R#alloc.version, + Res = id(V), + _ = id(0), + Res. + +confused_beam_validator(_Config) -> + {'EXIT',{{badmap,{any}},_}} = (catch efficient({any})), + ok. + +efficient({Var}=God) -> + id(God#{}), + catch + receive _ -> + Var + end. + +id(I) -> + I. diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl new file mode 100644 index 0000000000..69e2f1838d --- /dev/null +++ b/lib/compiler/test/beam_type_SUITE.erl @@ -0,0 +1,130 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. 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% +%% +-module(beam_type_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + integers/1,coverage/1,booleans/1,setelement/1,cons/1, + tuple/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +groups() -> + [{p,[parallel], + [integers, + coverage, + booleans, + setelement, + cons, + tuple + ]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +integers(_Config) -> + a = do_integers_1(2#11000), + b = do_integers_1(2#11001), + + a = do_integers_2(<<0:1>>), + {'EXIT',{{case_clause,-1},_}} = (catch do_integers_2(<<1:1>>)), + + college = do_integers_3(), + + ok. + +do_integers_1(B0) -> + B = B0 band 1, + case B band 15 of + 0 -> a; + 1 -> b + end. + +do_integers_2(Bin) -> + <<B:1/signed>> = Bin, + case B of + 0 -> a; + 1 -> b + end. + +do_integers_3() -> + case try 0 after [] end of + 0 -> college; + 1 -> 0 + end. + +coverage(_Config) -> + {'EXIT',{badarith,_}} = (catch id(1) bsl 0.5), + {'EXIT',{badarith,_}} = (catch id(2.0) bsl 2), + {'EXIT',{badarith,_}} = (catch a + 0.5), + {'EXIT',{badarith,_}} = (catch 2.0 * b), + + {'EXIT',{badarith,_}} = (catch id(42.0) / (1 bsl 2000)), + + id(id(42) band 387439739874298734983787934283479243879), + id(-1 band id(13)), + + ok. + +booleans(_Config) -> + {'EXIT',{{case_clause,_},_}} = (catch do_booleans(42)), + ok. + +do_booleans(B) -> + case is_integer(B) of + yes -> yes; + no -> no + end. + +setelement(_Config) -> + T0 = id({a,42}), + {a,_} = T0, + {b,_} = setelement(1, T0, b), + ok. + +cons(_Config) -> + [did] = cons(assigned, did), + ok. + +cons(assigned, Instrument) -> + [Instrument] = [did]. + +tuple(_Config) -> + {'EXIT',{{badmatch,{necessary}},_}} = (catch do_tuple()), + ok. + +do_tuple() -> + {0, _} = {necessary}. + +id(I) -> + I. diff --git a/lib/compiler/test/beam_utils_SUITE.erl b/lib/compiler/test/beam_utils_SUITE.erl new file mode 100644 index 0000000000..f6d4a311bb --- /dev/null +++ b/lib/compiler/test/beam_utils_SUITE.erl @@ -0,0 +1,357 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. 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% +%% +-module(beam_utils_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + apply_fun/1,apply_mf/1,bs_init/1,bs_save/1, + is_not_killed/1,is_not_used_at/1, + select/1,y_catch/1,otp_8949_b/1,liveopt/1,coverage/1, + y_registers/1]). +-export([id/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +groups() -> + [{p,[parallel], + [apply_fun, + apply_mf, + bs_init, + bs_save, + is_not_killed, + is_not_used_at, + select, + y_catch, + otp_8949_b, + liveopt, + coverage, + y_registers + ]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +apply_fun(_Config) -> + 3 = do_apply_fun(false, false), + 3 = do_apply_fun(false, true), + 3 = do_apply_fun(true, false), + 2 = do_apply_fun(true, true), + ok. + +do_apply_fun(X, Y) -> + F = fun(I) -> I+1 end, + Arg = case X andalso id(Y) of + true -> 1; + false -> 2 + end, + F(Arg). + +apply_mf(_Config) -> + ok = do_apply_mf_used({a,b}, ?MODULE, id), + error = do_apply_mf_used([a], ?MODULE, id), + {'EXIT',{{case_clause,{[],b}},_}} = (catch do_apply_mf_used({[],b}, ?MODULE, id)), + + error = do_apply_mf_killed({error,[a]}, ?MODULE, id), + ok = do_apply_mf_killed([b], ?MODULE, id), + {'EXIT',{{case_clause,{a,[b]}},_}} = (catch do_apply_mf_killed({a,[b]}, ?MODULE, id)), + {'EXIT',{{case_clause,{error,[]}},_}} = (catch do_apply_mf_killed({error,[]}, ?MODULE, id)), + + ok. + +do_apply_mf_used(Arg, Mod, Func) -> + Res = case id(Arg) of + {Decoded,_} when Decoded =/= [] -> + ok; + List when is_list(List) -> + error + end, + Mod:Func(Res). + +do_apply_mf_killed(Arg, Mod, Func) -> + Res = case id(Arg) of + {Tag,Decoded} when Decoded =/= [], Tag =:= error -> + error; + List when is_list(List) -> + ok + end, + Mod:Func(Res). + +bs_init(_Config) -> + <<7>> = do_bs_init_1([?MODULE], 7), + error = do_bs_init_1([?MODULE], 0.0), + error = do_bs_init_1([?MODULE], -43), + error = do_bs_init_1([?MODULE], 42), + + <<>> = do_bs_init_2([]), + <<0:32,((1 bsl 32)-1):32>> = do_bs_init_2([0,(1 bsl 32)-1]), + {'EXIT',{badarg,_}} = (catch do_bs_init_2([0.5])), + {'EXIT',{badarg,_}} = (catch do_bs_init_2([-1])), + {'EXIT',{badarg,_}} = (catch do_bs_init_2([1 bsl 32])), + ok. + +do_bs_init_1([?MODULE], Sz) -> + if + is_integer(Sz), Sz >= -42, Sz < 42 -> + id(<<Sz:8>>); + true -> + error + end. + +do_bs_init_2(SigNos) -> + << <<SigNo:32>> || + SigNo <- SigNos, + (is_integer(SigNo) andalso SigNo >= 0 andalso SigNo < (1 bsl 32)) orelse + erlang:error(badarg) + >>. + + +bs_save(_Config) -> + {a,30,<<>>} = do_bs_save(<<1:1,30:5>>), + {b,127,<<>>} = do_bs_save(<<1:1,31:5,0:1,127:7>>), + {c,127,<<>>} = do_bs_save(<<1:1,31:5,1:1,127:7>>), + {c,127,<<>>} = do_bs_save(<<0:1,31:5,1:1,127:7>>), + {d,1024,<<>>} = do_bs_save(<<0:1,31:5>>), + ok. + +do_bs_save(<<_:1, Tag:5, T/binary>>) when Tag < 31 -> + {a,Tag,T}; +do_bs_save(<<1:1, 31:5, 0:1, Tag:7, T/binary>>) -> + {b,Tag,T}; +do_bs_save(<<_:1, 31:5, 1:1, Tag:7, T/binary>>) -> + {c,Tag,T}; +do_bs_save(<<_:1, 31:5, T/binary>>) -> + {d,1024,T}. + +is_not_killed(_Config) -> + {Pid,Ref} = spawn_monitor(fun() -> exit(banan) end), + receive + {'DOWN', Ref, process, Pid, banan} -> + ok + end, + receive after 0 -> ok end. + +is_not_used_at(_Config) -> + {a,b} = do_is_not_used_at(a, [{a,b}]), + {a,b} = do_is_not_used_at(a, [x,{a,b}]), + {a,b} = do_is_not_used_at(a, [{x,y},{a,b}]), + none = do_is_not_used_at(z, [{a,b}]), + none = do_is_not_used_at(a, [x]), + none = do_is_not_used_at(a, [{x,y}]), + ok. + +do_is_not_used_at(Key, [P|Ps]) -> + if + tuple_size(P) >= 1, element(1, P) =:= Key -> + P; + true -> + do_is_not_used_at(Key, Ps) + end; +do_is_not_used_at(_Key, []) -> none. + +-record(select, {fixed=false}). + +select(_Config) -> + a = do_select(#select{}, 0, 0), + b = do_select(#select{}, 0, 1), + c = do_select(#select{fixed=true}, 0, 0), + c = do_select(#select{fixed=true}, 0, 1), + ok. + +do_select(Head, OldSize, BSize) -> + Overwrite0 = + if + OldSize =:= BSize -> same; + true -> true + end, + Overwrite = + if + Head#select.fixed =/= false -> + false; + true -> + Overwrite0 + end, + if + Overwrite =:= same -> + a; + Overwrite -> + b; + true -> + c + end. + +y_catch(_Config) -> + ok = try + do_y_catch(<<"<?xmlX">>, {state}), + failed + catch + throw:{<<"<?xmlX">>,{state}} -> + ok + end. + +do_y_catch(<<"<?xml",Rest0/binary>> = Bytes, State0) -> + {Rest1,State1} = + case do_y_catch_1(Rest0, State0) of + false -> + {Bytes,State0}; + true -> + {_XmlAttributes, R, S} = do_y_catch_2(Rest0), + {R,S} + end, + case catch id({Rest1,State1}) of + Other -> + throw(Other) + end. + +do_y_catch_1(<<_,_/binary>>, _) -> + false. + +do_y_catch_2(_) -> {a,b,c}. + +otp_8949_b(_Config) -> + self() ! something, + value = otp_8949_b([], false), + {'EXIT',_} = (catch otp_8949_b([], true)), + ok. + +%% Would cause an endless loop in beam_utils. +otp_8949_b(A, B) -> + Var = id(value), + if + A == [], B == false -> + ok + end, + receive + something -> + id(Var) + end. + +-record(alarmInfo, {type,cause,origin}). + +liveopt(_Config) -> + F = liveopt_fun(42, pebkac, user), + void = F(42, #alarmInfo{type=sctp,cause=pebkac,origin=user}), + ok. + +liveopt_fun(Peer, Cause, Origin) -> + fun(PeerNo, AlarmInfo) + when PeerNo == Peer andalso + AlarmInfo == #alarmInfo{type=sctp, + cause=Cause, + origin=Origin} -> + void + end. + +%% Thanks to QuickCheck. +coverage(_Config) -> + 42+7 = merchant([[],7,false]), + + {'EXIT',{{try_clause,0},_}} = (catch resulting([0], stone)), + 0.0 = resulting([true], stone), + + {'EXIT',{if_clause,_}} = (catch clinic(false)), + {'EXIT',{{try_clause,"trials"},_}} = (catch clinic(true)), + + {'EXIT',{function_clause,_}} = (catch town(overall, {{abc},alcohol})), + + ok. + +%% Cover check_liveness/3. +merchant([Merchant, Laws, Electric]) -> + id(42), + oklahoma([[] || 0 <- Merchant], + if true; Electric -> Laws end) + 42. +oklahoma([], Int) -> Int. + +town(overall, {{If}, Healing = alcohol}) + when Healing#{[] => Healing}; include -> + [If || Healing <- awareness]. + +%% Cover is_reg_used_at/3. +resulting([Conservation], stone) -> + try 0 of + Conservation when Conservation -> Conservation; + _ when Conservation; 0 -> 0.0 + after + Conservation + end. + +%% Cover is_reg_used_at_1/3. +clinic(Damage) -> + if + Damage -> + try "trials" of Damage when Damage -> Damage catch true -> [] end + end, + carefully. + +y_registers(_Config) -> + {'EXIT',{{badfun,0},_}} = (catch economic(0.0, jim)), + {'EXIT',{{badmatch,apartments},_}} = (catch louisiana()), + {a,b} = (boxes(true))({a,b}), + {'EXIT',{{case_clause,webmaster},_}} = (catch yellow(true)), + ok. + +economic(0.0 = Serves, Existence) -> + case Serves of + Serves -> 0 + end, + Existence = jim, + 0(), + Serves, + Existence. + +louisiana() -> + {catch necessarily, + try + [] == reg, + true = apartments + catch [] -> barbara + end}. + +boxes(Call) -> + case Call of + Call -> approval + end, + Call, + fun id/1. + +yellow(Hill) -> + case webmaster of + station -> eyes; Hill -> + "under" + end, + Hill, + id(42). + + +%% The identity function. +id(I) -> I. diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl index 626f89ba7a..263fd2ca7e 100644 --- a/lib/compiler/test/beam_validator_SUITE.erl +++ b/lib/compiler/test/beam_validator_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -21,44 +22,45 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, - beam_files/1,compiler_bug/1,stupid_but_valid/1, + compiler_bug/1,stupid_but_valid/1, xrange/1,yrange/1,stack/1,call_last/1,merge_undefined/1, uninit/1,unsafe_catch/1, - dead_code/1,mult_labels/1, + dead_code/1, overwrite_catchtag/1,overwrite_trytag/1,accessing_tags/1,bad_catch_try/1, cons_guard/1, freg_range/1,freg_uninit/1,freg_state/1, - bin_match/1,bad_bin_match/1,bin_aligned/1,bad_dsetel/1, + bad_bin_match/1,bad_dsetel/1, state_after_fault_in_catch/1,no_exception_in_catch/1, - undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1]). + undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1, + map_field_lists/1,cover_bin_opt/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = test_server:timetrap(?t:minutes(10)), - [{watchdog,Dog}|Config]. + Config. end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,10}}]. all() -> test_lib:recompile(?MODULE), - [beam_files,{group,p}]. + [{group,p}]. groups() -> [{p,test_lib:parallel(), [compiler_bug,stupid_but_valid,xrange, yrange,stack,call_last,merge_undefined,uninit, - unsafe_catch,dead_code,mult_labels, + unsafe_catch,dead_code, overwrite_catchtag,overwrite_trytag,accessing_tags, bad_catch_try,cons_guard,freg_range,freg_uninit, - freg_state,bin_match,bad_bin_match,bin_aligned,bad_dsetel, + freg_state,bad_bin_match,bad_dsetel, state_after_fault_in_catch,no_exception_in_catch, - undef_label,illegal_instruction,failing_gc_guard_bif]}]. + undef_label,illegal_instruction,failing_gc_guard_bif, + map_field_lists,cover_bin_opt]}]. init_per_suite(Config) -> Config. @@ -72,304 +74,288 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - -beam_files(Config) when is_list(Config) -> - ?line DataDir = proplists:get_value(data_dir, Config), - ?line Wc = filename:join([DataDir,"..","..","*","*.beam"]), - %% Must have at least two files here, or there will be - %% a grammatical error in the output of the io:format/2 call below. ;-) - ?line [_,_|_] = Fs = filelib:wildcard(Wc), - ?line io:format("~p files\n", [length(Fs)]), - test_lib:p_run(fun do_beam_file/1, Fs). - - -do_beam_file(F) -> - case beam_validator:file(F) of - ok -> - ok; - {error,Es} -> - io:format("File: ~s", [F]), - io:format("Error: ~p\n", [Es]), - error - end. - compiler_bug(Config) when is_list(Config) -> %% Check that the compiler returns an error if we try to %% assemble one of the bad '.S' files. - ?line Data = ?config(data_dir, Config), - ?line File = filename:join(Data, "stack"), - ?line error = compile:file(File, [asm,report_errors,binary,time]), + Data = proplists:get_value(data_dir, Config), + File = filename:join(Data, "compiler_bug"), + error = compile:file(File, [from_asm,report_errors,time]), + + %% Make sure that the error was reported by + %% the beam_validator module. + {error, + [{"compiler_bug", + [{beam_validator,_}]}], + []} = compile:file(File, [from_asm,return_errors,time]), ok. %% The following code is stupid but it should compile. stupid_but_valid(Config) when is_list(Config) -> AnAtom = nisse, - ?line try setelement(5, setelement(6, AnAtom, value), another_value) of - Term -> ?line ?t:fail({what_happened,Term}) - catch - error:badarg -> ok - end, + try setelement(5, setelement(6, AnAtom, value), another_value) of + Term -> ct:fail({what_happened,Term}) + catch + error:badarg -> ok + end, ok. xrange(Config) when is_list(Config) -> Errors = do_val(xrange, Config), - ?line - [{{t,sum_1,2}, - {{bif,'+',{f,0},[{x,-1},{x,1}],{x,0}},4, - {uninitialized_reg,{x,-1}}}}, - {{t,sum_2,2}, - {{bif,'+',{f,0},[{x,0},{x,1024}],{x,0}},4, - {uninitialized_reg,{x,1024}}}}, - {{t,sum_3,2}, - {{bif,'+',{f,0},[{x,0},{x,1}],{x,-1}},4, - {invalid_store,{x,-1},number}}}, - {{t,sum_4,2}, - {{bif,'+',{f,0},[{x,0},{x,1}],{x,1024}},4,limit}}] = Errors, + [{{t,sum_1,2}, + {{bif,'+',{f,0},[{x,-1},{x,1}],{x,0}},4, + {uninitialized_reg,{x,-1}}}}, + {{t,sum_2,2}, + {{bif,'+',{f,0},[{x,0},{x,1023}],{x,0}},4, + {uninitialized_reg,{x,1023}}}}, + {{t,sum_3,2}, + {{bif,'+',{f,0},[{x,0},{x,1}],{x,-1}},4, + {invalid_store,{x,-1},number}}}, + {{t,sum_4,2}, + {{bif,'+',{f,0},[{x,0},{x,1}],{x,1023}},4,limit}}] = Errors, ok. yrange(Config) when is_list(Config) -> Errors = do_val(yrange, Config), - ?line - [{{t,sum_1,2}, - {{move,{x,1},{y,-1}},5, - {invalid_store,{y,-1},term}}}, - {{t,sum_2,2}, - {{bif,'+',{f,0},[{x,0},{y,1024}],{x,0}},8, - {uninitialized_reg,{y,1024}}}}, - {{t,sum_3,2}, - {{move,{x,1},{y,1024}},5,limit}}, - {{t,sum_4,2}, - {{move,{x,1},{y,-1}},5, - {invalid_store,{y,-1},term}}}] = Errors, + [{{t,sum_1,2}, + {{move,{x,1},{y,-1}},5, + {invalid_store,{y,-1},term}}}, + {{t,sum_2,2}, + {{bif,'+',{f,0},[{x,0},{y,1024}],{x,0}},7, + {uninitialized_reg,{y,1024}}}}, + {{t,sum_3,2}, + {{move,{x,1},{y,1024}},5,limit}}, + {{t,sum_4,2}, + {{move,{x,1},{y,-1}},5, + {invalid_store,{y,-1},term}}}] = Errors, ok. stack(Config) when is_list(Config) -> Errors = do_val(stack, Config), - ?line [{{t,a,2},{return,11,{stack_frame,2}}}, - {{t,b,2},{{deallocate,2},4,{allocated,none}}}, - {{t,c,2},{{deallocate,2},12,{allocated,none}}}, - {{t,d,2}, - {{allocate,2,2},5,{existing_stack_frame,{size,2}}}}, - {{t,e,2},{{deallocate,5},6,{allocated,2}}}, - {{t,bad_1,0},{{allocate_zero,2,10},4,{{x,9},not_live}}}, - {{t,bad_2,0},{{move,{y,0},{x,0}},5,{unassigned,{y,0}}}}] = Errors, + [{{t,a,2},{return,9,{stack_frame,2}}}, + {{t,b,2},{{deallocate,2},4,{allocated,none}}}, + {{t,bad_1,0},{{allocate_zero,2,10},4,{{x,9},not_live}}}, + {{t,bad_2,0},{{move,{y,0},{x,0}},5,{unassigned,{y,0}}}}, + {{t,c,2},{{deallocate,2},10,{allocated,none}}}, + {{t,d,2}, + {{allocate,2,2},5,{existing_stack_frame,{size,2}}}}, + {{t,e,2},{{deallocate,5},6,{allocated,2}}}] = Errors, ok. call_last(Config) when is_list(Config) -> Errors = do_val(call_last, Config), - ?line [{{t,a,1},{{call_last,1,{f,8},2},11,{allocated,1}}}, - {{t,b,1}, - {{call_ext_last,2,{extfunc,lists,seq,2},2}, - 11, - {allocated,1}}}] = Errors, + [{{t,a,1},{{call_last,1,{f,8},2},9,{allocated,1}}}, + {{t,b,1}, + {{call_ext_last,2,{extfunc,lists,seq,2},2}, + 10, + {allocated,1}}}] = Errors, ok. merge_undefined(Config) when is_list(Config) -> Errors = do_val(merge_undefined, Config), - ?line [{{t,handle_call,2}, - {{call_ext,2,{extfunc,debug,filter,2}}, - 22, - {uninitialized_reg,{y,0}}}}] = Errors, + [{{t,handle_call,2}, + {{call_ext,2,{extfunc,debug,filter,2}}, + 22, + {uninitialized_reg,{y,0}}}}] = Errors, ok. uninit(Config) when is_list(Config) -> Errors = do_val(uninit, Config), - ?line - [{{t,sum_1,2}, - {{move,{y,0},{x,0}},5,{uninitialized_reg,{y,0}}}}, - {{t,sum_2,2}, - {{call,1,{f,10}},6,{uninitialized_reg,{y,0}}}}, - {{t,sum_3,2}, - {{bif,'+',{f,0},[{x,0},{y,0}],{x,0}}, - 7, - {unassigned,{y,0}}}}] = Errors, + [{{t,sum_1,2}, + {{move,{y,0},{x,0}},5,{uninitialized_reg,{y,0}}}}, + {{t,sum_2,2}, + {{call,1,{f,8}},5,{uninitialized_reg,{y,0}}}}, + {{t,sum_3,2}, + {{bif,'+',{f,0},[{x,0},{y,0}],{x,0}}, + 6, + {unassigned,{y,0}}}}] = Errors, ok. unsafe_catch(Config) when is_list(Config) -> Errors = do_val(unsafe_catch, Config), - ?line - [{{t,small,2}, - {{bs_put_integer,{f,0},{integer,16},1, - {field_flags,[aligned,unsigned,big]},{y,0}}, - 20, - {unassigned,{y,0}}}}] = Errors, + [{{t,small,2}, + {{bs_put_integer,{f,0},{integer,16},1, + {field_flags,[unsigned,big]},{y,0}}, + 20, + {unassigned,{y,0}}}}] = Errors, ok. dead_code(Config) when is_list(Config) -> [] = do_val(dead_code, Config), ok. -mult_labels(Config) when is_list(Config) -> - [] = do_val(erl_prim_loader, Config, ".beam"), - ok. - overwrite_catchtag(Config) when is_list(Config) -> Errors = do_val(overwrite_catchtag, Config), - ?line - [{{overwrite_catchtag,foo,1}, - {{move,{x,0},{y,0}},6,{catchtag,_}}}] = Errors, + [{{overwrite_catchtag,foo,1}, + {{move,{x,0},{y,0}},6,{catchtag,_}}}] = Errors, ok. overwrite_trytag(Config) when is_list(Config) -> Errors = do_val(overwrite_trytag, Config), - ?line - [{{overwrite_trytag,foo,1}, - {{kill,{y,2}},9,{trytag,_}}}] = Errors, + [{{overwrite_trytag,foo,1}, + {{kill,{y,2}},8,{trytag,_}}}] = Errors, ok. accessing_tags(Config) when is_list(Config) -> Errors = do_val(accessing_tags, Config), - ?line - [{{accessing_tags,foo,1}, - {{move,{y,0},{x,0}},6,{catchtag,_}}}, - {{accessing_tags,bar,1}, - {{move,{y,0},{x,0}},6,{trytag,_}}}] = Errors, + [{{accessing_tags,bar,1}, + {{move,{y,0},{x,0}},6,{trytag,_}}}, + {{accessing_tags,foo,1}, + {{move,{y,0},{x,0}},6,{catchtag,_}}}] = Errors, ok. bad_catch_try(Config) when is_list(Config) -> Errors = do_val(bad_catch_try, Config), - ?line [{{bad_catch_try,bad_1,1}, - {{'catch',{x,0},{f,3}}, - 5,{invalid_store,{x,0},{catchtag,[3]}}}}, - {{bad_catch_try,bad_2,1}, - {{catch_end,{x,9}}, - 8,{source_not_y_reg,{x,9}}}}, - {{bad_catch_try,bad_3,1}, - {{catch_end,{y,1}},9,{bad_type,{atom,kalle}}}}, - {{bad_catch_try,bad_4,1}, - {{'try',{x,0},{f,15}},5,{invalid_store,{x,0},{trytag,[15]}}}}, - {{bad_catch_try,bad_5,1}, - {{try_case,{y,1}},12,{bad_type,term}}}, - {{bad_catch_try,bad_6,1}, - {{try_end,{y,1}},8,{bad_type,{integer,1}}}}] = Errors, + [{{bad_catch_try,bad_1,1}, + {{'catch',{x,0},{f,3}}, + 5,{invalid_store,{x,0},{catchtag,[3]}}}}, + {{bad_catch_try,bad_2,1}, + {{catch_end,{x,9}}, + 8,{source_not_y_reg,{x,9}}}}, + {{bad_catch_try,bad_3,1}, + {{catch_end,{y,1}},9,{bad_type,{atom,kalle}}}}, + {{bad_catch_try,bad_4,1}, + {{'try',{x,0},{f,15}},5,{invalid_store,{x,0},{trytag,[15]}}}}, + {{bad_catch_try,bad_5,1}, + {{try_case,{y,1}},12,{bad_type,term}}}, + {{bad_catch_try,bad_6,1}, + {{move,{integer,1},{y,1}},7, + {invalid_store,{y,1},{integer,1}}}}] = Errors, ok. cons_guard(Config) when is_list(Config) -> Errors = do_val(cons, Config), - ?line - [{{cons,foo,1}, - {{get_list,{x,0},{x,1},{x,2}}, - 5, - {bad_type,{needed,cons},{actual,term}}}}] = Errors, + [{{cons,foo,1}, + {{get_list,{x,0},{x,1},{x,2}}, + 5, + {bad_type,{needed,cons},{actual,term}}}}] = Errors, ok. freg_range(Config) when is_list(Config) -> Errors = do_val(freg_range, Config), - ?line - [{{t,sum_1,2}, - {{bif,fadd,{f,0},[{fr,-1},{fr,1}],{fr,0}}, - 5, - {bad_source,{fr,-1}}}}, - {{t,sum_2,2}, - {{bif,fadd,{f,0},[{fr,0},{fr,1024}],{fr,0}}, - 6, - {uninitialized_reg,{fr,1024}}}}, - {{t,sum_3,2}, - {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,-1}}, - 7, - {bad_target,{fr,-1}}}}, - {{t,sum_4,2}, - {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,1024}}, - 7, - limit}}] = Errors, + [{{t,sum_1,2}, + {{bif,fadd,{f,0},[{fr,-1},{fr,1}],{fr,0}}, + 5, + {bad_source,{fr,-1}}}}, + {{t,sum_2,2}, + {{bif,fadd,{f,0},[{fr,0},{fr,1024}],{fr,0}}, + 6, + {uninitialized_reg,{fr,1024}}}}, + {{t,sum_3,2}, + {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,-1}}, + 7, + {bad_target,{fr,-1}}}}, + {{t,sum_4,2}, + {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,1024}}, + 7, + limit}}] = Errors, ok. freg_uninit(Config) when is_list(Config) -> Errors = do_val(freg_uninit, Config), - ?line - [{{t,sum_1,2}, - {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}}, - 6, - {uninitialized_reg,{fr,1}}}}, - {{t,sum_2,2}, - {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}}, - 9, - {uninitialized_reg,{fr,0}}}}] = Errors, + [{{t,sum_1,2}, + {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}}, + 6, + {uninitialized_reg,{fr,1}}}}, + {{t,sum_2,2}, + {{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}}, + 9, + {uninitialized_reg,{fr,0}}}}] = Errors, ok. freg_state(Config) when is_list(Config) -> Errors = do_val(freg_state, Config), - ?line - [{{t,sum_1,2}, - {{bif,fmul,{f,0},[{fr,0},{fr,1}],{fr,0}}, - 6, - {bad_floating_point_state,undefined}}}, - {{t,sum_2,2}, - {{fmove,{fr,0},{x,0}}, - 8, - {bad_floating_point_state,cleared}}}, - {{t,sum_3,2}, - {{bif,'-',{f,0},[{x,1},{x,0}],{x,1}}, - 8, - {unsafe_instruction,{float_error_state,cleared}}}}, - {{t,sum_4,2}, - {{fcheckerror,{f,0}}, - 4, - {bad_floating_point_state,undefined}}}, - {{t,sum_5,2}, - {fclearerror,5,{bad_floating_point_state,cleared}}}] = Errors, - ok. - -bin_match(Config) when is_list(Config) -> - Errors = do_val(bin_match, Config), - ?line - [{{t,t,1},{{bs_save,0},4,no_bs_match_state}}, - {{t,x,1},{{bs_restore,1},16,{no_save_point,1}}}] = Errors, + [{{t,sum_1,2}, + {{bif,fmul,{f,0},[{fr,0},{fr,1}],{fr,0}}, + 6, + {bad_floating_point_state,undefined}}}, + {{t,sum_2,2}, + {{fmove,{fr,0},{x,0}}, + 8, + {bad_floating_point_state,cleared}}}, + {{t,sum_3,2}, + {{bif,'-',{f,0},[{x,1},{x,0}],{x,1}}, + 8, + {unsafe_instruction,{float_error_state,cleared}}}}, + {{t,sum_4,2}, + {{fcheckerror,{f,0}}, + 4, + {bad_floating_point_state,undefined}}}, + {{t,sum_5,2}, + {fclearerror,5,{bad_floating_point_state,cleared}}}] = Errors, ok. bad_bin_match(Config) when is_list(Config) -> - [{{t,t,1},{return,5,{match_context,{x,0}}}}] = - do_val(bad_bin_match, Config), - ok. - -bin_aligned(Config) when is_list(Config) -> - Errors = do_val(bin_aligned, Config), - ?line - [{{t,decode,1}, - {{bs_put_integer,{f,0}, - {integer,5}, - 1, - {field_flags,[unsigned,big,aligned]}, - {integer,0}}, - 10, - {aligned_flag_set,{bits,3}}}}] = Errors, + [{{t,t,1},{return,5,{match_context,{x,0}}}}] = + do_val(bad_bin_match, Config), ok. bad_dsetel(Config) when is_list(Config) -> Errors = do_val(bad_dsetel, Config), - ?line - [{{t,t,1}, - {{set_tuple_element,{x,1},{x,0},1}, - 15, - illegal_context_for_set_tuple_element}}] = Errors, + [{{t,t,1}, + {{set_tuple_element,{x,1},{x,0},1}, + 17, + illegal_context_for_set_tuple_element}}] = Errors, ok. state_after_fault_in_catch(Config) when is_list(Config) -> Errors = do_val(state_after_fault_in_catch, Config), - [{{t,foo,1}, - {{move,{x,1},{x,0}},10,{uninitialized_reg,{x,1}}}}, - {{state_after_fault_in_catch,if_end,1}, + [{{state_after_fault_in_catch,badmatch,1}, {{move,{x,1},{x,0}},9,{uninitialized_reg,{x,1}}}}, {{state_after_fault_in_catch,case_end,1}, {{move,{x,1},{x,0}},9,{uninitialized_reg,{x,1}}}}, - {{state_after_fault_in_catch,badmatch,1}, - {{move,{x,1},{x,0}},9,{uninitialized_reg,{x,1}}}}] = Errors, + {{state_after_fault_in_catch,if_end,1}, + {{move,{x,1},{x,0}},9,{uninitialized_reg,{x,1}}}}, + {{t,foo,1}, + {{move,{x,1},{x,0}},10,{uninitialized_reg,{x,1}}}}] = Errors, ok. no_exception_in_catch(Config) when is_list(Config) -> Errors = do_val(no_exception_in_catch, Config), [{{no_exception_in_catch,nested_of_1,4}, - {{move,{x,3},{x,0}},91,{uninitialized_reg,{x,3}}}}] = Errors, + {{move,{x,3},{x,0}},87,{uninitialized_reg,{x,3}}}}] = Errors, ok. undef_label(Config) when is_list(Config) -> - Errors = do_val(undef_label, Config), + M = {undef_label, + [{t,1}], + [], + [{function,t,1,2, + [{label,1}, + {func_info,{atom,undef_label},{atom,t},1}, + {label,2}, + {test,is_eq_exact,{f,42},[{x,0},{atom,x}]}, + {move,{atom,ok},{x,0}}, + return]}, + {function,x,1,17, + [{label,3}, + {func_info,{atom,undef_label},{atom,x},1}, + {label,4}, + return]}], + 5}, + Errors = beam_val(M), [{{undef_label,t,1},{undef_labels,[42]}}, {{undef_label,x,1},{return,4,no_entry_label}}] = Errors, ok. illegal_instruction(Config) when is_list(Config) -> - Errors = do_val(illegal_instruction, Config), + M = {illegal_instruction, + [{t,1},{x,1},{y,0}], + [], + [{function,t,1,2, + [{label,1}, + {func_info,{atom,illegal_instruction},{atom,t},1}, + {label,2}, + {my_illegal_instruction,{x,0}}, + return]}, + {function,x,1,4, + [{label,3}, + bad_func_info, + {label,4}, + {my_illegal_instruction,{x,0}}, + return]}, + {function,y,0,17,[]}], + 5}, + Errors = beam_val(M), [{{illegal_instruction,t,1}, {{my_illegal_instruction,{x,0}},4,unknown_instruction}}, {{'_',x,1},{bad_func_info,1,illegal_instruction}}, @@ -384,9 +370,9 @@ illegal_instruction(Config) when is_list(Config) -> %% (Thanks to Kiran Khaladkar.) %% failing_gc_guard_bif(Config) when is_list(Config) -> - ?line ok = process_request(lists:seq(1, 36)), - ?line error = process_request([]), - ?line error = process_request(not_a_list), + ok = process_request(lists:seq(1, 36)), + error = process_request([]), + error = process_request(not_a_list), ok. process_request(ConfId) -> @@ -407,19 +393,156 @@ process_request_foo(_) -> process_request_bar(Pid, [Response]) when is_pid(Pid) -> Response. +map_field_lists(Config) -> + Errors = do_val(map_field_lists, Config), + [{{map_field_lists,x,1}, + {{test,has_map_fields,{f,1},{x,0}, + {list,[{atom,a},{atom,a}]}}, + 5, + keys_not_unique}}, + {{map_field_lists,y,1}, + {{test,has_map_fields,{f,3},{x,0},{list,[]}}, + 5, + empty_field_list}} + ] = Errors. + +%% Coverage and smoke test of beam_validator. +cover_bin_opt(_Config) -> + Ms = [beam_utils_SUITE, + bs_match_SUITE, + bs_bincomp_SUITE, + bs_bit_binaries_SUITE, + bs_utf_SUITE], + test_lib:p_run(fun try_bin_opt/1, Ms), + ok. + +try_bin_opt(Mod) -> + try + do_bin_opt(Mod) + catch + Class:Error -> + io:format("~p: ~p ~p\n~p\n", + [Mod,Class,Error,erlang:get_stacktrace()]), + error + end. + +do_bin_opt(Mod) -> + Beam = code:which(Mod), + {ok,{Mod,[{abstract_code, + {raw_abstract_v1,Abstr}}]}} = + beam_lib:chunks(Beam, [abstract_code]), + {ok,Mod,Asm} = compile:forms(Abstr, ['S']), + do_bin_opt(Mod, Asm). + +do_bin_opt(Mod, Asm) -> + do_bin_opt(fun enable_bin_opt/1, Mod, Asm), + do_bin_opt(fun remove_bs_start_match/1, Mod, Asm), + do_bin_opt(fun remove_bs_save/1, Mod, Asm), + do_bin_opt(fun destroy_ctxt/1, Mod, Asm), + do_bin_opt(fun destroy_save_point/1, Mod, Asm), + ok. + +do_bin_opt(Transform, Mod, Asm0) -> + Asm = Transform(Asm0), + case compile:forms(Asm, [from_asm,no_postopt,return]) of + {ok,[],Code,_Warnings} when is_binary(Code) -> + ok; + {error,Errors0,_} -> + %% beam_validator must return errors, not simply crash, + %% when illegal code is found. + ModString = atom_to_list(Mod), + [{ModString,Errors}] = Errors0, + _ = [verify_bin_opt_error(E) || E <- Errors], + ok + end. + +verify_bin_opt_error({beam_validator,_}) -> + ok. + +enable_bin_opt(Module) -> + transform_is(fun enable_bin_opt_body/1, Module). + +enable_bin_opt_body([_,{'%',{no_bin_opt,_Reason,_Anno}}|Is]) -> + enable_bin_opt_body(Is); +enable_bin_opt_body([I|Is]) -> + [I|enable_bin_opt_body(Is)]; +enable_bin_opt_body([]) -> + []. + +remove_bs_start_match(Module) -> + transform_remove(fun({test,bs_start_match2,_,_,_,_}) -> true; + (_) -> false + end, Module). + +remove_bs_save(Module) -> + transform_remove(fun({bs_save2,_,_}) -> true; + (_) -> false + end, Module). + +destroy_save_point(Module) -> + transform_i(fun do_destroy_save_point/1, Module). + +do_destroy_save_point({I,Ctx,_Point}) + when I =:= bs_save2; I =:= bs_restore2 -> + {I,Ctx,42}; +do_destroy_save_point(I) -> + I. + +destroy_ctxt(Module) -> + transform_i(fun do_destroy_ctxt/1, Module). + +do_destroy_ctxt({bs_save2=I,Ctx,Point}) -> + {I,destroy_reg(Ctx),Point}; +do_destroy_ctxt({bs_restore2=I,Ctx,Point}) -> + {I,destroy_reg(Ctx),Point}; +do_destroy_ctxt({bs_context_to_binary=I,Ctx}) -> + {I,destroy_reg(Ctx)}; +do_destroy_ctxt(I) -> + I. + +destroy_reg({Tag,N}) -> + case rand:uniform() of + R when R < 0.6 -> + {Tag,N+1}; + _ -> + {y,N+1} + end. %%%------------------------------------------------------------------------- -do_val(Name, Config) -> - do_val(Name, Config, ".S"). - -do_val(Name, Config, Type) -> - ?line Data = ?config(data_dir, Config), - ?line File = filename:join(Data, atom_to_list(Name)++Type), - ?line case beam_validator:file(File) of - {error,Errors} -> - ?line io:format("~p:~n~s", - [File,beam_validator:format_error(Errors)]), - Errors; - ok -> [] - end. +transform_remove(Remove, Module) -> + transform_is(fun(Is) -> [I || I <- Is, not Remove(I)] end, Module). + +transform_i(Transform, Module) -> + transform_is(fun(Is) -> [Transform(I) || I <- Is] end, Module). + +transform_is(Transform, {Mod,Exp,Imp,Fs0,Lc}) -> + Fs = [transform_is_1(Transform, F) || F <- Fs0], + {Mod,Exp,Imp,Fs,Lc}. + +transform_is_1(Transform, {function,N,A,E,Is0}) -> + Is = Transform(Is0), + {function,N,A,E,Is}. + +do_val(Mod, Config) -> + Data = proplists:get_value(data_dir, Config), + Base = atom_to_list(Mod), + File = filename:join(Data, Base), + case compile:file(File, [from_asm,no_postopt,return_errors]) of + {error,L,[]} -> + [{Base,Errors0}] = L, + Errors = [E || {beam_validator,E} <- Errors0], + _ = [io:put_chars(beam_validator:format_error(E)) || + E <- Errors], + Errors; + {ok,Mod} -> + [] + end. + +beam_val(M) -> + Name = atom_to_list(element(1, M)), + {error,[{Name,Errors0}]} = beam_validator:module(M, []), + Errors = [E || {beam_validator,E} <- Errors0], + _ = [io:put_chars(beam_validator:format_error(E)) || + E <- Errors], + Errors. diff --git a/lib/compiler/test/beam_validator_SUITE_data/bad_catch_try.S b/lib/compiler/test/beam_validator_SUITE_data/bad_catch_try.S index 2a53f0dd93..6035f23506 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/bad_catch_try.S +++ b/lib/compiler/test/beam_validator_SUITE_data/bad_catch_try.S @@ -63,11 +63,11 @@ {label,9}. {func_info,{atom,bad_catch_try},{atom,bad_3},1}. {label,10}. - {allocate,1,1}. + {allocate,2,1}. + {move,{atom,kalle},{y,1}}. {'catch',{y,0},{f,11}}. {call,1,{f,26}}. {label,11}. - {move,{atom,kalle},{y,1}}. {catch_end,{y,1}}. {test,is_tuple,{f,12},[{x,0}]}. {test,test_arity,{f,12},[{x,0},2]}. @@ -106,7 +106,7 @@ {label,17}. {func_info,{atom,bad_catch_try},{atom,bad_5},1}. {label,18}. - {allocate_zero,1,1}. + {allocate_zero,2,1}. {'try',{y,0},{f,19}}. {call,1,{f,26}}. {try_end,{y,0}}. @@ -131,7 +131,7 @@ {'try',{y,0},{f,23}}. {call,1,{f,26}}. {move,{integer,1},{y,1}}. - {try_end,{y,1}}. + {try_end,{y,0}}. {move,{atom,ok},{x,0}}. {jump,{f,24}}. {label,23}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/bad_dsetel.S b/lib/compiler/test/beam_validator_SUITE_data/bad_dsetel.S index 279b2fa97f..9630d73a93 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/bad_dsetel.S +++ b/lib/compiler/test/beam_validator_SUITE_data/bad_dsetel.S @@ -1,4 +1,4 @@ -{module, t}. %% version = 0 +{module, bad_dsetel}. %% version = 0 {exports, [{module_info,0},{module_info,1},{t,1}]}. @@ -21,7 +21,9 @@ {move,{integer,3},{x,0}}. {call_ext,3,{extfunc,erlang,setelement,3}}. {test_heap,6,1}. - {put_string,3,{string,"abc"},{x,1}}. + {put_list,{integer,99},nil,{x,1}}. + {put_list,{integer,98},{x,1},{x,1}}. + {put_list,{integer,97},{x,1},{x,1}}. {set_tuple_element,{x,1},{x,0},1}. {'%live',1}. {deallocate,0}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/bin_aligned.S b/lib/compiler/test/beam_validator_SUITE_data/bin_aligned.S deleted file mode 100644 index 2f353fbd25..0000000000 --- a/lib/compiler/test/beam_validator_SUITE_data/bin_aligned.S +++ /dev/null @@ -1,47 +0,0 @@ -{module, t}. %% version = 0 - -{exports, [{decode,1},{module_info,0},{module_info,1}]}. - -{attributes, []}. - -{labels, 7}. - - -{function, decode, 1, 2}. - {label,1}. - {func_info,{atom,t},{atom,decode},1}. - {label,2}. - {move,{integer,1},{x,1}}. - {bif,size,{f,0},[{x,0}],{x,2}}. - {bs_add,{f,0},[{x,1},{x,2},1],{x,1}}. - {bs_init2,{f,0},{x,1},0,1,{field_flags,[]},{x,1}}. - {bs_put_integer,{f,0}, - {integer,3}, - 1, - {field_flags,[aligned,unsigned,big]}, - {integer,0}}. - {bs_put_binary,{f,0},{atom,all},8,{field_flags,[unsigned,big]},{x,0}}. - {bs_put_integer,{f,0}, - {integer,5}, - 1, - {field_flags,[unsigned,big,aligned]}, - {integer,0}}. - {move,{x,1},{x,0}}. - return. - - -{function, module_info, 0, 4}. - {label,3}. - {func_info,{atom,t},{atom,module_info},0}. - {label,4}. - {move,{atom,t},{x,0}}. - {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. - - -{function, module_info, 1, 6}. - {label,5}. - {func_info,{atom,t},{atom,module_info},1}. - {label,6}. - {move,{x,0},{x,1}}. - {move,{atom,t},{x,0}}. - {call_ext_only,2,{extfunc,erlang,get_module_info,2}}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/bin_match.S b/lib/compiler/test/beam_validator_SUITE_data/bin_match.S deleted file mode 100644 index 96df0f7933..0000000000 --- a/lib/compiler/test/beam_validator_SUITE_data/bin_match.S +++ /dev/null @@ -1,64 +0,0 @@ -{module, bin_match}. %% version = 0 - -{exports, [{t,1}]}. - -{attributes, []}. - -{labels, 8}. - - -{function, t, 1, 2}. - {label,1}. - {func_info,{atom,t},{atom,t},1}. - {label,2}. -%% {test,bs_start_match,{f,1},[{x,0}]}. - {bs_save,0}. - {test,bs_get_integer, - {f,3}, - [{integer,8},1,{field_flags,[aligned,unsigned,big]},{x,1}]}. - {test,bs_get_integer, - {f,3}, - [{integer,8},1,{field_flags,[aligned,unsigned,big]},{x,2}]}. - {test,bs_test_tail,{f,3},[0]}. - {test_heap,3,3}. - {put_tuple,2,{x,0}}. - {put,{x,1}}. - {put,{x,2}}. - {'%live',1}. - return. - {label,3}. - {bs_restore,0}. - {test,bs_get_integer, - {f,1}, - [{integer,32},1,{field_flags,[aligned,unsigned,big]},{x,1}]}. - {test,bs_test_tail,{f,1},[0]}. - {move,{x,1},{x,0}}. - return. - -{function, x, 1, 5}. - {label,4}. - {func_info,{atom,t},{atom,x},1}. - {label,5}. - {test,bs_start_match,{f,4},[{x,0}]}. - {bs_save,0}. - {test,bs_get_integer, - {f,6}, - [{integer,8},1,{field_flags,[aligned,unsigned,big]},{x,1}]}. - {test,bs_get_integer, - {f,6}, - [{integer,8},1,{field_flags,[aligned,unsigned,big]},{x,2}]}. - {test,bs_test_tail,{f,6},[0]}. - {test_heap,3,3}. - {put_tuple,2,{x,0}}. - {put,{x,1}}. - {put,{x,2}}. - {'%live',1}. - return. - {label,6}. - {bs_restore,1}. - {test,bs_get_integer, - {f,4}, - [{integer,32},1,{field_flags,[aligned,unsigned,big]},{x,1}]}. - {test,bs_test_tail,{f,4},[0]}. - {move,{x,1},{x,0}}. - return. diff --git a/lib/compiler/test/beam_validator_SUITE_data/compiler_bug.S b/lib/compiler/test/beam_validator_SUITE_data/compiler_bug.S new file mode 100644 index 0000000000..ba27bf5c47 --- /dev/null +++ b/lib/compiler/test/beam_validator_SUITE_data/compiler_bug.S @@ -0,0 +1,38 @@ +{module, compiler_bug}. %% version = 0 + +{exports, [{module_info,0},{module_info,1},{sum,2}]}. + +{attributes, []}. + +{labels, 7}. + + +{function, sum, 2, 2}. + {label,1}. + {line,[{location,"compiler_bug.erl",4}]}. + {func_info,{atom,compiler_bug},{atom,sum},2}. + {label,2}. + {line,[{location,"compiler_bug.erl",5}]}. + {gc_bif,'+',{f,0},2,[{y,0},{y,1}],{x,0}}. + return. + + +{function, module_info, 0, 4}. + {label,3}. + {line,[]}. + {func_info,{atom,compiler_bug},{atom,module_info},0}. + {label,4}. + {move,{atom,compiler_bug},{x,0}}. + {line,[]}. + {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. + + +{function, module_info, 1, 6}. + {label,5}. + {line,[]}. + {func_info,{atom,compiler_bug},{atom,module_info},1}. + {label,6}. + {move,{x,0},{x,1}}. + {move,{atom,compiler_bug},{x,0}}. + {line,[]}. + {call_ext_only,2,{extfunc,erlang,get_module_info,2}}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/dead_code.S b/lib/compiler/test/beam_validator_SUITE_data/dead_code.S index f964f98fba..c114664ba0 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/dead_code.S +++ b/lib/compiler/test/beam_validator_SUITE_data/dead_code.S @@ -1,10 +1,10 @@ {module, dead_code}. %% version = 0 -{exports, [{execute,0},{module_info,0},{module_info,1}]}. +{exports, [{execute,0}]}. {attributes, []}. -{labels, 10}. +{labels, 6}. {function, execute, 0, 2}. @@ -12,7 +12,6 @@ {func_info,{atom,dead_code},{atom,execute},0}. {label,2}. {allocate,0,0}. - {'%live',0}. {call_ext,0,{extfunc,foo,fie,0}}. {test,is_ne,{f,4},[{x,0},{integer,0}]}. {test,is_ne,{f,4},[{x,0},{integer,1}]}. @@ -22,27 +21,7 @@ {case_end,{x,0}}. {label,4}. {move,{atom,ok},{x,0}}. - {'%live',1}. {deallocate,0}. return. - {'%','Moved code'}. {label,5}. {case_end,{x,0}}. - - -{function, module_info, 0, 7}. - {label,6}. - {func_info,{atom,dead_code},{atom,module_info},0}. - {label,7}. - {move,nil,{x,0}}. - {'%live',1}. - return. - - -{function, module_info, 1, 9}. - {label,8}. - {func_info,{atom,dead_code},{atom,module_info},1}. - {label,9}. - {move,nil,{x,0}}. - {'%live',1}. - return. diff --git a/lib/compiler/test/beam_validator_SUITE_data/erl_prim_loader.beam b/lib/compiler/test/beam_validator_SUITE_data/erl_prim_loader.beam Binary files differdeleted file mode 100644 index dd58a88e42..0000000000 --- a/lib/compiler/test/beam_validator_SUITE_data/erl_prim_loader.beam +++ /dev/null diff --git a/lib/compiler/test/beam_validator_SUITE_data/freg_range.S b/lib/compiler/test/beam_validator_SUITE_data/freg_range.S index ee583a923e..b3ebff3ade 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/freg_range.S +++ b/lib/compiler/test/beam_validator_SUITE_data/freg_range.S @@ -1,10 +1,10 @@ {module, freg_range}. %% version = 0 -{exports, [{module_info,0},{module_info,1},{prod,2},{sum,2},{sum_prod,3}]}. +{exports, [{sum_1,2},{sum_2,2},{sum_3,2},{sum_4,2}]}. {attributes, []}. -{labels, 8}. +{labels, 9}. {function, sum_1, 2, 2}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/freg_state.S b/lib/compiler/test/beam_validator_SUITE_data/freg_state.S index ff4d7548ae..7466763482 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/freg_state.S +++ b/lib/compiler/test/beam_validator_SUITE_data/freg_state.S @@ -1,6 +1,6 @@ {module, freg_state}. %% version = 0 -{exports, []}. +{exports, [{sum_1,2},{sum_2,2},{sum_3,2},{sum_4,2},{sum_5,2}]}. {attributes, []}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S b/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S index f8d805d9ec..71e833446a 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S +++ b/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S @@ -1,10 +1,10 @@ {module, freg_uninit}. %% version = 0 -{exports, []}. +{exports, [{sum_1,2},{sum_2,2}]}. {attributes, []}. -{labels, 8}. +{labels, 7}. {function, sum_1, 2, 2}. @@ -14,7 +14,6 @@ {fconv,{x,0},{fr,0}}. fclearerror. {bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}}. - {'%live',1}. return. @@ -26,7 +25,12 @@ {fconv,{x,1},{fr,1}}. fclearerror. {fcheckerror,{f,0}}. - {call,2,{f,8}}. + {call,2,{f,6}}. {bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}}. - {'%live',1}. + return. + +{function, foo, 2, 6}. + {label,5}. + {func_info,{atom,t},{atom,foo},2}. + {label,6}. return. diff --git a/lib/compiler/test/beam_validator_SUITE_data/illegal_instruction.S b/lib/compiler/test/beam_validator_SUITE_data/illegal_instruction.S deleted file mode 100644 index d6e92abc71..0000000000 --- a/lib/compiler/test/beam_validator_SUITE_data/illegal_instruction.S +++ /dev/null @@ -1,26 +0,0 @@ -{module, illegal_instruction}. %% version = 0 - -{exports, []}. - -{attributes, []}. - -{labels, 7}. - - -{function, t, 1, 2}. - {label,1}. - {func_info,{atom,illegal_instruction},{atom,t},1}. - {label,2}. - {my_illegal_instruction,{x,0}}. - return. - - -{function, x, 1, 4}. - {label,3}. - bad_func_info. - {label,4}. - {my_illegal_instruction,{x,0}}. - return. - -{function, y, 0, 17}. -
\ No newline at end of file diff --git a/lib/compiler/test/beam_validator_SUITE_data/map_field_lists.S b/lib/compiler/test/beam_validator_SUITE_data/map_field_lists.S new file mode 100644 index 0000000000..5e7ccc1e5d --- /dev/null +++ b/lib/compiler/test/beam_validator_SUITE_data/map_field_lists.S @@ -0,0 +1,29 @@ +{module, map_field_lists}. %% version = 0 + +{exports, [{x,1},{y,1}]}. + +{attributes, []}. + +{labels, 5}. + + +{function, x, 1, 2}. + {label,1}. + {line,[{location,"map_field_lists.erl",4}]}. + {func_info,{atom,map_field_lists},{atom,x},1}. + {label,2}. + {test,is_map,{f,1},[{x,0}]}. + {test,has_map_fields,{f,1},{x,0},{list,[{atom,a},{atom,a}]}}. + {move,{atom,ok},{x,0}}. + return. + + +{function, y, 1, 4}. + {label,3}. + {line,[{location,"map_field_lists.erl",7}]}. + {func_info,{atom,map_field_lists},{atom,y},1}. + {label,4}. + {test,is_map,{f,3},[{x,0}]}. + {test,has_map_fields,{f,3},{x,0},{list,[]}}. + {move,{atom,ok},{x,0}}. + return. diff --git a/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S b/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S index 3d76127824..481d55045d 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S +++ b/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S @@ -22,7 +22,8 @@ {label,4}. {allocate_heap,1,6,2}. {move,{x,1},{y,0}}. - {put_string,2,{string,"~p"},{x,0}}. + {put_list,{integer,112},nil,{x,0}}. + {put_list,{integer,126},{x,0},{x,0}}. {put_list,{y,0},nil,{x,1}}. {'%live',2}. {call_ext,2,{extfunc,io,format,2}}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/no_exception_in_catch.S b/lib/compiler/test/beam_validator_SUITE_data/no_exception_in_catch.S index e08a718a39..1a5b417a5f 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/no_exception_in_catch.S +++ b/lib/compiler/test/beam_validator_SUITE_data/no_exception_in_catch.S @@ -26,7 +26,7 @@ {call_ext,1,{extfunc,erlang,erase,1}}. {move,{atom,nested},{x,0}}. {call_ext,1,{extfunc,erlang,erase,1}}. - {bif,self,nofail,[],{x,0}}. + {bif,self,{f,0},[],{x,0}}. {'try',{y,8},{f,13}}. {'try',{y,7},{f,11}}. {'try',{y,6},{f,9}}. @@ -34,7 +34,7 @@ %% Because the following instructions can't possible throw an exception, %% label 7 used to get no state. Now the try_end itself will save the state. {move,{x,0},{y,4}}. - {bif,self,nofail,[],{x,0}}. + {bif,self,{f,0},[],{x,0}}. {'%live',1}. {try_end,{y,5}}. {test,is_eq_exact,{f,15},[{x,0},{y,4}]}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/stack.S b/lib/compiler/test/beam_validator_SUITE_data/stack.S index 244c22a2f9..e4356a9d00 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/stack.S +++ b/lib/compiler/test/beam_validator_SUITE_data/stack.S @@ -1,10 +1,10 @@ {module, stack}. %% version = 0 -{exports, [{a,2},{b,2},{c,2},{d,2},{e,2}]}. +{exports, [{a,2},{b,2},{c,2},{d,2},{e,2},{bad_1,0},{bad_2,0},{foo,0}]}. {attributes, []}. -{labels, 21}. +{labels, 17}. {function, a, 2, 2}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/state_after_fault_in_catch.S b/lib/compiler/test/beam_validator_SUITE_data/state_after_fault_in_catch.S index 8e27347ed5..c3656d6218 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/state_after_fault_in_catch.S +++ b/lib/compiler/test/beam_validator_SUITE_data/state_after_fault_in_catch.S @@ -14,7 +14,7 @@ {allocate,1,0}. {'catch',{y,0},{f,3}}. {move,{atom,apa},{x,0}}. - {call_ext,1,{extfunc,erlang,fault,1}}. + {call_ext,1,{extfunc,erlang,error,1}}. {label,3}. {catch_end,{y,0}}. {move,{x,1},{x,0}}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/undef_label.S b/lib/compiler/test/beam_validator_SUITE_data/undef_label.S deleted file mode 100644 index dd29066bf4..0000000000 --- a/lib/compiler/test/beam_validator_SUITE_data/undef_label.S +++ /dev/null @@ -1,22 +0,0 @@ -{module, undef_label}. %% version = 0 - -{exports, []}. - -{attributes, []}. - -{labels, 7}. - - -{function, t, 1, 2}. - {label,1}. - {func_info,{atom,undef_label},{atom,t},1}. - {label,2}. - {test,is_eq_exact,{f,42},[{x,0},{atom,x}]}. - {move,{atom,ok},{x,0}}. - return. - -{function, x, 1, 17}. - {label,3}. - {func_info,{atom,undef_label},{atom,x},1}. - {label,4}. - return. diff --git a/lib/compiler/test/beam_validator_SUITE_data/uninit.S b/lib/compiler/test/beam_validator_SUITE_data/uninit.S index 1a45c31411..9a66f4f7d6 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/uninit.S +++ b/lib/compiler/test/beam_validator_SUITE_data/uninit.S @@ -1,9 +1,11 @@ {module, uninit}. %% version = 0 -{exports, []}. +{exports, [{sum_1,2},{sum_2,2},{sum_3,2}]}. {attributes, []}. +{labels, 9}. + {function, sum_1, 2, 2}. {label,1}. {func_info,{atom,t},{atom,sum_1},2}. @@ -11,7 +13,7 @@ {allocate,1,2}. {move,{y,0},{x,0}}. {'%live',1}. - {call,1,{f,10}}. + {call,1,{f,8}}. {bif,'+',{f,0},[{x,0},{y,0}],{x,0}}. {'%live',1}. {deallocate,1}. @@ -23,7 +25,7 @@ {label,4}. {allocate,1,2}. {'%live',1}. - {call,1,{f,10}}. + {call,1,{f,8}}. {bif,'+',{f,0},[{x,0},{y,0}],{x,0}}. {'%live',1}. {deallocate,1}. @@ -35,14 +37,14 @@ {label,6}. {allocate_zero,1,2}. {'%live',1}. - {call,1,{f,10}}. + {call,1,{f,8}}. {bif,'+',{f,0},[{x,0},{y,0}],{x,0}}. {'%live',1}. {deallocate,1}. return. -{function, id, 1, 10}. - {label,9}. +{function, id, 1, 8}. + {label,7}. {func_info,{atom,t},{atom,id},1}. - {label,10}. + {label,8}. return. diff --git a/lib/compiler/test/beam_validator_SUITE_data/unsafe_catch.S b/lib/compiler/test/beam_validator_SUITE_data/unsafe_catch.S index 500ac11377..f7d3f805b3 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/unsafe_catch.S +++ b/lib/compiler/test/beam_validator_SUITE_data/unsafe_catch.S @@ -17,7 +17,7 @@ {bs_put_integer,{f,0}, {integer,8}, 1, - {field_flags,[aligned,unsigned,big]}, + {field_flags,[unsigned,big]}, {x,0}}. {move,{x,1},{y,0}}. {move,{x,2},{x,0}}. @@ -34,7 +34,7 @@ {bs_put_integer,{f,0}, {integer,16}, 1, - {field_flags,[aligned,unsigned,big]}, + {field_flags,[unsigned,big]}, {y,0}}. {move,{x,0},{y,0}}. {move,{x,1},{x,0}}. @@ -55,12 +55,12 @@ {bs_put_binary,{f,0}, {atom,all}, 8, - {field_flags,[aligned,unsigned,big]}, + {field_flags,[unsigned,big]}, {y,0}}. {bs_put_binary,{f,0}, {atom,all}, 8, - {field_flags,[aligned,unsigned,big]}, + {field_flags,[unsigned,big]}, {x,0}}. {move,{x,1},{x,0}}. {deallocate,2}. diff --git a/lib/compiler/test/beam_validator_SUITE_data/xrange.S b/lib/compiler/test/beam_validator_SUITE_data/xrange.S index 3abbdffbc2..a76408dde3 100644 --- a/lib/compiler/test/beam_validator_SUITE_data/xrange.S +++ b/lib/compiler/test/beam_validator_SUITE_data/xrange.S @@ -1,10 +1,10 @@ {module, xrange}. %% version = 0 -{exports, [{module_info,0},{module_info,1},{prod,2},{sum,2},{sum_prod,3}]}. +{exports, [{sum_1,2},{sum_2,2},{sum_3,2},{sum_4,2}]}. {attributes, []}. -{labels, 8}. +{labels, 9}. {function, sum_1, 2, 2}. @@ -20,7 +20,7 @@ {label,3}. {func_info,{atom,t},{atom,sum_2},2}. {label,4}. - {bif,'+',{f,0},[{x,0},{x,1024}],{x,0}}. + {bif,'+',{f,0},[{x,0},{x,1023}],{x,0}}. {'%live',1}. return. @@ -38,7 +38,7 @@ {label,7}. {func_info,{atom,t},{atom,sum_4},2}. {label,8}. - {bif,'+',{f,0},[{x,0},{x,1}],{x,1024}}. + {bif,'+',{f,0},[{x,0},{x,1}],{x,1023}}. {'%live',1}. return. diff --git a/lib/compiler/test/bif_SUITE.erl b/lib/compiler/test/bif_SUITE.erl new file mode 100644 index 0000000000..51bc71da81 --- /dev/null +++ b/lib/compiler/test/bif_SUITE.erl @@ -0,0 +1,65 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. 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% +%% +-module(bif_SUITE). + +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + beam_validator/1]). + +suite() -> + [{ct_hooks,[ts_install_cth]}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +groups() -> + [{p,[parallel], + [beam_validator + ]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%% Cover code in beam_validator. + +beam_validator(Config) -> + [false,Config] = food(Config), + + true = is_number(42.0), + false = is_port(Config), + + ok. + +food(Curriculum) -> + [try + is_bitstring(functions) + catch _ -> + 0 + end, Curriculum]. diff --git a/lib/compiler/test/bs_bincomp_SUITE.erl b/lib/compiler/test/bs_bincomp_SUITE.erl index 4450405695..4743821337 100644 --- a/lib/compiler/test/bs_bincomp_SUITE.erl +++ b/lib/compiler/test/bs_bincomp_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2013. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -25,9 +26,9 @@ init_per_group/2,end_per_group/2, byte_aligned/1,bit_aligned/1,extended_byte_aligned/1, extended_bit_aligned/1,mixed/1,filters/1,trim_coverage/1, - nomatch/1,sizes/1]). + nomatch/1,sizes/1,general_expressions/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -35,7 +36,7 @@ all() -> test_lib:recompile(?MODULE), [byte_aligned, bit_aligned, extended_byte_aligned, extended_bit_aligned, mixed, filters, trim_coverage, - nomatch, sizes]. + nomatch, sizes, general_expressions]. groups() -> []. @@ -54,110 +55,114 @@ end_per_group(_GroupName, Config) -> byte_aligned(Config) when is_list(Config) -> cs_init(), - ?line <<"abcdefg">> = cs(<< <<(X+32)>> || <<X>> <= <<"ABCDEFG">> >>), + <<"abcdefg">> = cs(<< <<(X+32)>> || <<X>> <= <<"ABCDEFG">> >>), <<1:32/little,2:32/little,3:32/little,4:32/little>> = cs(<< <<X:32/little>> || <<X:32>> <= <<1:32,2:32,3:32,4:32>> >>), - ?line cs(<<1:32/little,2:32/little,3:32/little,4:32/little>> = - << <<X:32/little>> || <<X:16>> <= <<1:16,2:16,3:16,4:16>> >>), + cs(<<1:32/little,2:32/little,3:32/little,4:32/little>> = + << <<X:32/little>> || <<X:16>> <= <<1:16,2:16,3:16,4:16>> >>), cs_end(). bit_aligned(Config) when is_list(Config) -> cs_init(), - ?line <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> = + <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> = cs(<< <<(X+32):7>> || <<X>> <= <<"ABCDEFG">> >>), - ?line <<"ABCDEFG">> = + <<"ABCDEFG">> = cs(<< <<(X-32)>> || <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> >>), - ?line <<1:31/little,2:31/little,3:31/little,4:31/little>> = + <<1:31/little,2:31/little,3:31/little,4:31/little>> = cs(<< <<X:31/little>> || <<X:31>> <= <<1:31,2:31,3:31,4:31>> >>), - ?line <<1:31/little,2:31/little,3:31/little,4:31/little>> = + <<1:31/little,2:31/little,3:31/little,4:31/little>> = cs(<< <<X:31/little>> || <<X:15>> <= <<1:15,2:15,3:15,4:15>> >>), cs_end(). extended_byte_aligned(Config) when is_list(Config) -> cs_init(), - ?line <<"abcdefg">> = cs(<< <<(X+32)>> || X <- "ABCDEFG" >>), - ?line "abcdefg" = [(X+32) || <<X>> <= <<"ABCDEFG">>], - ?line <<1:32/little,2:32/little,3:32/little,4:32/little>> = + <<"abcdefg">> = cs(<< <<(X+32)>> || X <- "ABCDEFG" >>), + "abcdefg" = [(X+32) || <<X>> <= <<"ABCDEFG">>], + <<1:32/little,2:32/little,3:32/little,4:32/little>> = cs(<< <<X:32/little>> || X <- [1,2,3,4] >>), - ?line [256,512,768,1024] = + [256,512,768,1024] = [X || <<X:16/little>> <= <<1:16,2:16,3:16,4:16>>], cs_end(). extended_bit_aligned(Config) when is_list(Config) -> cs_init(), - ?line <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> = + <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> = cs(<< <<(X+32):7>> || X <- "ABCDEFG" >>), - ?line "ABCDEFG" = [(X-32) || <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>>], - ?line <<1:31/little,2:31/little,3:31/little,4:31/little>> = + "ABCDEFG" = [(X-32) || <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>>], + <<1:31/little,2:31/little,3:31/little,4:31/little>> = cs(<< <<X:31/little>> || X <- [1,2,3,4] >>), - ?line [256,512,768,1024] = + [256,512,768,1024] = [X || <<X:15/little>> <= <<1:15,2:15,3:15,4:15>>], cs_end(). mixed(Config) when is_list(Config) -> cs_init(), - ?line <<2,3,3,4,4,5,5,6>> = + <<2,3,3,4,4,5,5,6>> = cs(<< <<(X+Y)>> || <<X>> <= <<1,2,3,4>>, <<Y>> <= <<1,2>> >>), - ?line <<2,3,3,4,4,5,5,6>> = + <<2,3,3,4,4,5,5,6>> = << <<(X+Y)>> || <<X>> <= <<1,2,3,4>>, Y <- [1,2] >>, - ?line <<2,3,3,4,4,5,5,6>> = + <<2,3,3,4,4,5,5,6>> = cs(<< <<(X+Y)>> || X <- [1,2,3,4], Y <- [1,2] >>), One = id([1,2,3,4]), Two = id([1,2]), - ?line <<2,3,3,4,4,5,5,6>> = + <<2,3,3,4,4,5,5,6>> = cs(<< <<(X+Y)>> || X <- One, Y <- Two >>), - ?line [2,3,3,4,4,5,5,6] = + [2,3,3,4,4,5,5,6] = [(X+Y) || <<X>> <= <<1,2,3,4>>, <<Y>> <= <<1,2>>], - ?line [2,3,3,4,4,5,5,6] = + [2,3,3,4,4,5,5,6] = [(X+Y) || <<X>> <= <<1,2,3,4>>, Y <- [1,2]], - ?line <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> = + <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> = cs(<< <<(X+Y):3>> || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, <<Y:3>> <= <<1:3,2:3>> >>), - ?line <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> = + <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> = cs(<< <<(X+Y):3>> || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, Y <- [1,2] >>), - ?line <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> = + <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> = cs(<< <<(X+Y):3>> || X <- [1,2,3,4], Y <- [1,2] >>), - ?line <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> = + <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> = cs_default(<< <<(X+Y):3>> || {X,Y} <- [{1,1},{1,2},{2,1},{2,2}, {3,1},{3,2},{4,1},{4,2}] >>), - ?line [2,3,3,4,4,5,5,6] = + [2,3,3,4,4,5,5,6] = [(X+Y) || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, <<Y:3>> <= <<1:3,2:3>>], - ?line [2,3,3,4,4,5,5,6] = + [2,3,3,4,4,5,5,6] = [(X+Y) || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, {_,Y} <- [{a,1},{b,2}]], cs_end(). filters(Config) when is_list(Config) -> cs_init(), - ?line <<"BDF">> = - cs_default(<< <<(X-32)>> || <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>>, - X rem 2 == 0>>), - ?line <<"abc">> = cs_default(<< <<(X+32)>> || X <- "ABCDEFG", - is_less_than(X, $D)>>), - ?line <<"efg">> = cs_default(<< <<(X+32)>> || X <- "ABCDEFG", - not is_less_than(X, $E)>>), - ?line <<"b">> = cs_default(<< <<(X+32)>> || X <- "ABCDEFG", - is_less_than(X, $D), - X rem 2 == 0>>), - ?line <<"eg">> = cs_default(<< <<(X+32)>> || X <- "ABCDEFG", - not is_less_than(X, $E), - X rem 2 == 1>>), + <<"BDF">> = + cs_default(<< <<(X-32)>> || + <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>>, + X rem 2 == 0>>), + <<"abc">> = cs_default(<< <<(X+32)>> || + X <- "ABCDEFG", + is_less_than(X, $D)>>), + <<"efg">> = cs_default(<< <<(X+32)>> || + X <- "ABCDEFG", + not is_less_than(X, $E)>>), + <<"b">> = cs_default(<< <<(X+32)>> || + X <- "ABCDEFG", + is_less_than(X, $D), + X rem 2 == 0>>), + <<"eg">> = cs_default(<< <<(X+32)>> || + X <- "ABCDEFG", + not is_less_than(X, $E), + X rem 2 == 1>>), %% Filtering by a non-matching pattern. - ?line <<"abd">> = cs_default(<< <<X:8>> || - <<0:1,X:7>> <= <<$a:8,$b:8,1:1,$c:7,$d:8, - 1:1,$e:7,0:4>> >>), + <<"abd">> = cs_default(<< <<X:8>> || + <<0:1,X:7>> <= <<$a:8,$b:8,1:1,$c:7,$d:8, + 1:1,$e:7,0:4>> >>), - ?line <<42,42>> = cs_default(<< <<42:8>> || - 42 <- [1,2,3,42,43,42] >>), + <<42,42>> = cs_default(<< <<42:8>> || 42 <- [1,2,3,42,43,42] >>), cs_end(). is_less_than(X, C) when X < C -> true; is_less_than(_, _) -> false. trim_coverage(Config) when is_list(Config) -> - ?line <<0,0,0,2,0,0,5,48,0,11,219,174,0,0,0,0>> = coverage_materialiv(a, b, {1328,777134}), - ?line <<67,40,0,0,66,152,0,0,69,66,64,0>> = coverage_trimmer([42,19,777]), - ?line <<0,0,2,43,0,0,3,9,0,0,0,3,64,8,0,0,0,0,0,0, + <<0,0,0,2,0,0,5,48,0,11,219,174,0,0,0,0>> = coverage_materialiv(a, b, {1328,777134}), + <<67,40,0,0,66,152,0,0,69,66,64,0>> = coverage_trimmer([42,19,777]), + <<0,0,2,43,0,0,3,9,0,0,0,3,64,8,0,0,0,0,0,0, 64,68,0,0,0,0,0,0,192,171,198,0,0,0,0,0>> = coverage_lightfv(555, 777, {3.0,40.0,-3555.0}), ok. @@ -185,111 +190,153 @@ coverage_trimmer(Params) -> coverage_summer(A, B, C, D) -> A+B+C+D. nomatch(Config) when is_list(Config) -> - ?line <<>> = << <<X:8>> || X = {_,_} = [_|_] <- [1,2,3] >>, + <<>> = << <<X:8>> || X = {_,_} = [_|_] <- [1,2,3] >>, ok. sizes(Config) when is_list(Config) -> - ?line cs_init(), + cs_init(), Fun0 = fun(List) -> cs(<< <<E:8>> || E <- List >>) end, - ?line <<>> = Fun0([]), - ?line <<1>> = Fun0([1]), - ?line <<1,2>> = Fun0([1,2]), - ?line <<1,2,3>> = Fun0([1,2,3]), + <<>> = Fun0([]), + <<1>> = Fun0([1]), + <<1,2>> = Fun0([1,2]), + <<1,2,3>> = Fun0([1,2,3]), Fun1 = fun(List) -> cs(<< <<E:16>> || E <- List >>) end, - ?line <<>> = Fun1([]), - ?line <<1:16>> = Fun1([1]), - ?line <<1:16,2:16>> = Fun1([1,2]), - ?line <<1:16,2:16,3:16>> = Fun1([1,2,3]), + <<>> = Fun1([]), + <<1:16>> = Fun1([1]), + <<1:16,2:16>> = Fun1([1,2]), + <<1:16,2:16,3:16>> = Fun1([1,2,3]), Fun2 = fun(List) -> cs(<< <<E:4>> || E <- List >>) end, - ?line <<>> = Fun2([]), - ?line <<1:4>> = Fun2([1]), - ?line <<1:4,13:4>> = Fun2([1,13]), - ?line <<1:4,13:4,7:4>> = Fun2([1,13,7]), - ?line <<0:1000/unit:8>> = Fun2(lists:duplicate(2000, 0)), + <<>> = Fun2([]), + <<1:4>> = Fun2([1]), + <<1:4,13:4>> = Fun2([1,13]), + <<1:4,13:4,7:4>> = Fun2([1,13,7]), + <<0:1000/unit:8>> = Fun2(lists:duplicate(2000, 0)), Fun3 = fun(List) -> cs(<< <<E:3>> || E <- List >>) end, - ?line <<>> = Fun3([]), - ?line <<40,177,29:5>> = Fun3([1,2,1,3,0,7,5]), - ?line <<0:512/unit:3>> = Fun3(lists:duplicate(512, 0)), + <<>> = Fun3([]), + <<40,177,29:5>> = Fun3([1,2,1,3,0,7,5]), + <<0:512/unit:3>> = Fun3(lists:duplicate(512, 0)), Fun4 = fun(List, Size) -> cs(<< <<E:Size>> || E <- List >>) end, - ?line <<>> = Fun4([], 8), - ?line <<42:6>> = Fun4([42], 6), - ?line <<42:16>> = Fun4([42], 16), + <<>> = Fun4([], 8), + <<42:6>> = Fun4([42], 6), + <<42:16>> = Fun4([42], 16), Fun5 = fun(List, Sz1, Sz2, Sz3) -> cs(<< <<E:Sz1,(E+1):Sz2/unit:8,(E+2):Sz3/unit:8>> || E <- List >>) end, - ?line <<>> = Fun5([], 1, 1, 1), - ?line <<7:3,8:40,9:56>> = Fun5([7], 3, 5, 7), + <<>> = Fun5([], 1, 1, 1), + <<7:3,8:40,9:56>> = Fun5([7], 3, 5, 7), Fun6 = fun(List, Size) -> cs(<< <<E:8,(E+1):Size>> || E <- List >>) end, - ?line <<>> = Fun6([], 42), - ?line <<42,43:20>> = Fun6([42], 20), + <<>> = Fun6([], 42), + <<42,43:20>> = Fun6([42], 20), %% Binary generators. Fun10 = fun(Bin) -> cs(<< <<E:16>> || <<E:8>> <= Bin >>) end, - ?line <<>> = Fun10(<<>>), - ?line <<1:16>> = Fun10(<<1>>), - ?line <<1:16,2:16>> = Fun10(<<1,2>>), + <<>> = Fun10(<<>>), + <<1:16>> = Fun10(<<1>>), + <<1:16,2:16>> = Fun10(<<1,2>>), Fun11 = fun(Bin) -> cs(<< <<E:8>> || <<E:16>> <= Bin >>) end, - ?line <<>> = Fun11(<<>>), - ?line <<1>> = Fun11(<<1:16>>), - ?line <<1,2>> = Fun11(<<1:16,2:16>>), - ?line <<1,2>> = Fun11(<<1:16,2:16,0:1>>), - ?line <<1,2>> = Fun11(<<1:16,2:16,0:7>>), - ?line <<1,2>> = Fun11(<<1:16,2:16,42:8>>), - ?line <<1,2>> = Fun11(<<1:16,2:16,42:9>>), - ?line <<1,2>> = Fun11(<<1:16,2:16,255:15>>), + <<>> = Fun11(<<>>), + <<1>> = Fun11(<<1:16>>), + <<1,2>> = Fun11(<<1:16,2:16>>), + <<1,2>> = Fun11(<<1:16,2:16,0:1>>), + <<1,2>> = Fun11(<<1:16,2:16,0:7>>), + <<1,2>> = Fun11(<<1:16,2:16,42:8>>), + <<1,2>> = Fun11(<<1:16,2:16,42:9>>), + <<1,2>> = Fun11(<<1:16,2:16,255:15>>), Fun12 = fun(Bin, Sz1, Sz2) -> cs(<< <<E:Sz1>> || <<E:Sz2>> <= Bin >>) end, - ?line <<>> = Fun12(<<>>, 1, 1), - ?line Binary = list_to_binary(lists:seq(0, 255)), - ?line Binary = Fun12(Binary, 1, 1), - ?line Binary = Fun12(Binary, 4, 4), - ?line Binary = Fun12(Binary, 8, 8), - ?line <<17:9,19:9>> = Fun12(<<17:6,19:6>>, 9, 6), + <<>> = Fun12(<<>>, 1, 1), + Binary = list_to_binary(lists:seq(0, 255)), + Binary = Fun12(Binary, 1, 1), + Binary = Fun12(Binary, 4, 4), + Binary = Fun12(Binary, 8, 8), + <<17:9,19:9>> = Fun12(<<17:6,19:6>>, 9, 6), Fun13 = fun(Sz) -> cs_default(<< <<C:8>> || <<C:4>> <= <<1:4,2:4,3:4,0:Sz>> >>) end, - ?line <<1,2,3>> = Fun13(0), - ?line <<1,2,3,0>> = Fun13(4), - ?line <<1,2,3,0>> = Fun13(5), - ?line <<1,2,3,0>> = Fun13(6), - ?line <<1,2,3,0>> = Fun13(7), - ?line <<1,2,3,0,0>> = Fun13(8), + <<1,2,3>> = Fun13(0), + <<1,2,3,0>> = Fun13(4), + <<1,2,3,0>> = Fun13(5), + <<1,2,3,0>> = Fun13(6), + <<1,2,3,0>> = Fun13(7), + <<1,2,3,0,0>> = Fun13(8), <<0:3>> = cs_default(<< <<0:S>> || S <- [0,1,2] >>), <<0:3>> = cs_default(<< <<0:S>> || <<S>> <= <<0,1,2>> >>), - ?line {'EXIT',_} = (catch << <<C:4>> || <<C:8>> <= {1,2,3} >>), + {'EXIT',_} = (catch << <<C:4>> || <<C:8>> <= {1,2,3} >>), - ?line cs_end(), + cs_end(), ok. +-define(BAD(E), {'EXIT',{badarg,_}} = (catch << (E) || _ <- [1,2,3] >>)). +-define(BAD_V(E), {'EXIT',{badarg,_}} = (catch << (E) || I <- [1,2,3] >>)). + +general_expressions(_) -> + <<1,2,3>> = << begin <<1,2,3>> end || _ <- [1] >>, + <<"abc">> = << begin <<"abc">> end || _ <- [1] >>, + <<1,2,3>> = << begin + I = <<(I0+1)>>, + id(I) + end || <<I0>> <= <<0,1,2>> >>, + <<1,2,3>> = << I || I <- [<<1,2>>,<<3>>] >>, + <<1,2,3>> = << (id(<<I>>)) || I <- [1,2,3] >>, + <<2,4>> = << case I rem 2 of + 0 -> <<I>>; + 1 -> <<>> + end || I <- [1,2,3,4,5] >>, + <<2,3,4,5,6,7>> = << << (id(<<J>>)) || J <- [2*I,2*I+1] >> || + I <- [1,2,3] >>, + <<1,2,2,3,4,4>> = << if + I rem 2 =:= 0 -> <<I,I>>; + true -> <<I>> + end || I <- [1,2,3,4] >>, + self() ! <<42>>, + <<42>> = << receive B -> B end || _ <- [1] >>, + <<10,5,3>> = << try + <<(10 div I)>> + catch _:_ -> + <<>> + end || I <- [0,1,2,3] >>, + + %% Failing expressions. + ?BAD(bad_atom), + ?BAD(42), + ?BAD(42.0), + ?BAD_V({ok,I}), + ?BAD_V([I]), + ?BAD_V(fun() -> I end), + + ok. + +-undef(BAD). + cs_init() -> erts_debug:set_internal_state(available_internal_state, true), ok. diff --git a/lib/compiler/test/bs_bit_binaries_SUITE.erl b/lib/compiler/test/bs_bit_binaries_SUITE.erl index 8609a490f5..208d8c5487 100644 --- a/lib/compiler/test/bs_bit_binaries_SUITE.erl +++ b/lib/compiler/test/bs_bit_binaries_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2013. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -28,7 +29,7 @@ big_binary_to_and_from_list/1,send_and_receive/1, send_and_receive_alot/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -37,7 +38,7 @@ all() -> [{group,p}]. groups() -> - [{p,test_lib:parallel(), + [{p,[parallel], [misc,horrid_match,test_bitstr,test_bit_size, asymmetric_tests,big_asymmetric_tests, binary_to_and_from_list,big_binary_to_and_from_list, @@ -58,9 +59,9 @@ end_per_group(_GroupName, Config) -> misc(Config) when is_list(Config) -> - ?line <<1:100>> = <<1:100>>, - ?line {ok,ok} = {match(7),match(9)}, - ?line {ok,ok} = {match1(15),match1(31)}, + <<1:100>> = <<1:100>>, + {ok,ok} = {match(7),match(9)}, + {ok,ok} = {match1(15),match1(31)}, ok. @@ -73,75 +74,75 @@ match1(N) -> ok. test_bit_size(Config) when is_list(Config) -> - ?line 101 = erlang:bit_size(<<1:101>>), - ?line 1001 = erlang:bit_size(<<1:1001>>), - ?line 1001 = erlang:bit_size(<<-10:1001>>), - ?line 80 = erlang:bit_size(<<1:80>>), - ?line 800 = erlang:bit_size(<<1:800>>), - ?line Bin = <<0:16#1000000>>, - ?line BigBin = list_to_bitstring([Bin||_ <- lists:seq(1,16#10)]++[<<1:1>>]), - ?line 16#10000001 = erlang:bit_size(BigBin), + 101 = erlang:bit_size(<<1:101>>), + 1001 = erlang:bit_size(<<1:1001>>), + 1001 = erlang:bit_size(<<-10:1001>>), + 80 = erlang:bit_size(<<1:80>>), + 800 = erlang:bit_size(<<1:800>>), + Bin = <<0:16#1000000>>, + BigBin = list_to_bitstring([Bin||_ <- lists:seq(1,16#10)]++[<<1:1>>]), + 16#10000001 = erlang:bit_size(BigBin), %% Only run these on computers with lots of memory %% HugeBin = list_to_bitstring([BigBin||_ <- lists:seq(1,16#10)]++[<<1:1>>]), %% 16#100000011 = erlang:bit_size(HugeBin), - ?line 0 = erlang:bit_size(<<>>), + 0 = erlang:bit_size(<<>>), ok. horrid_match(Config) when is_list(Config) -> - ?line <<1:4,B:24/bitstring>> = <<1:4,42:24/little>>, - ?line <<42:24/little>> = B, + <<1:4,B:24/bitstring>> = <<1:4,42:24/little>>, + <<42:24/little>> = B, ok. test_bitstr(Config) when is_list(Config) -> - ?line <<1:7,B/bitstring>> = <<1:7,<<1:1,6>>/bitstring>>, - ?line <<1:1,6>> = B, - ?line B = <<1:1,6>>, + <<1:7,B/bitstring>> = <<1:7,<<1:1,6>>/bitstring>>, + <<1:1,6>> = B, + B = <<1:1,6>>, ok. asymmetric_tests(Config) when is_list(Config) -> - ?line <<1:12>> = <<0,1:4>>, - ?line <<0,1:4>> = <<1:12>>, - ?line <<1:1,X/bitstring>> = <<128,255,0,0:2>>, - ?line <<1,254,0,0:1>> = X, - ?line X = <<1,254,0,0:1>>, - ?line <<1:1,X1:25/bitstring>> = <<128,255,0,0:2>>, - ?line <<1,254,0,0:1>> = X1, - ?line X1 = <<1,254,0,0:1>>, + <<1:12>> = <<0,1:4>>, + <<0,1:4>> = <<1:12>>, + <<1:1,X/bitstring>> = <<128,255,0,0:2>>, + <<1,254,0,0:1>> = X, + X = <<1,254,0,0:1>>, + <<1:1,X1:25/bitstring>> = <<128,255,0,0:2>>, + <<1,254,0,0:1>> = X1, + X1 = <<1,254,0,0:1>>, ok. big_asymmetric_tests(Config) when is_list(Config) -> - ?line <<1:875,1:12>> = <<1:875,0,1:4>>, - ?line <<1:875,0,1:4>> = <<1:875,1:12>>, - ?line <<1:1,X/bitstring>> = <<128,255,0,0:2,1:875>>, - ?line <<1,254,0,0:1,1:875>> = X, - ?line X = <<1,254,0,0:1,1:875>>, - ?line <<1:1,X1:900/bitstring>> = <<128,255,0,0:2,1:875>>, - ?line <<1,254,0,0:1,1:875>> = X1, - ?line X1 = <<1,254,0,0:1,1:875>>, + <<1:875,1:12>> = <<1:875,0,1:4>>, + <<1:875,0,1:4>> = <<1:875,1:12>>, + <<1:1,X/bitstring>> = <<128,255,0,0:2,1:875>>, + <<1,254,0,0:1,1:875>> = X, + X = <<1,254,0,0:1,1:875>>, + <<1:1,X1:900/bitstring>> = <<128,255,0,0:2,1:875>>, + <<1,254,0,0:1,1:875>> = X1, + X1 = <<1,254,0,0:1,1:875>>, ok. binary_to_and_from_list(Config) when is_list(Config) -> - ?line <<1,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1,2,3,4,1:1>>)), - ?line [1,2,3,4,<<1:1>>] = bitstring_to_list(<<1,2,3,4,1:1>>), - ?line <<1:1,1,2,3,4>> = list_to_bitstring([<<1:1>>,1,2,3,4]), - ?line [128,129,1,130,<<0:1>>] = bitstring_to_list(<<1:1,1,2,3,4>>), + <<1,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1,2,3,4,1:1>>)), + [1,2,3,4,<<1:1>>] = bitstring_to_list(<<1,2,3,4,1:1>>), + <<1:1,1,2,3,4>> = list_to_bitstring([<<1:1>>,1,2,3,4]), + [128,129,1,130,<<0:1>>] = bitstring_to_list(<<1:1,1,2,3,4>>), ok. big_binary_to_and_from_list(Config) when is_list(Config) -> - ?line <<1:800,2,3,4,1:1>> = + <<1:800,2,3,4,1:1>> = list_to_bitstring(bitstring_to_list(<<1:800,2,3,4,1:1>>)), - ?line [1,2,3,4|_Rest1] = bitstring_to_list(<<1,2,3,4,1:800,1:1>>), - ?line <<1:801,1,2,3,4>> = list_to_bitstring([<<1:801>>,1,2,3,4]), + [1,2,3,4|_Rest1] = bitstring_to_list(<<1,2,3,4,1:800,1:1>>), + <<1:801,1,2,3,4>> = list_to_bitstring([<<1:801>>,1,2,3,4]), ok. send_and_receive(Config) when is_list(Config) -> - ?line Bin = <<1,2:7>>, + Bin = <<1,2:7>>, Pid = spawn_link(fun() -> receiver(Bin) end), - ?line Pid ! {self(),<<1:7,8:5,Bin/bitstring>>}, - ?line receive - ok -> - ok - end. + Pid ! {self(),<<1:7,8:5,Bin/bitstring>>}, + receive + ok -> + ok + end. receiver(Bin) -> receive diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl index ce39de2a82..ff18f8b41c 100644 --- a/lib/compiler/test/bs_construct_SUITE.erl +++ b/lib/compiler/test/bs_construct_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -30,16 +31,18 @@ nasty_literals/1,coerce_to_float/1,side_effect/1, opt/1,otp_7556/1,float_arith/1,otp_8054/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> test_lib:recompile(?MODULE), [{group,p}]. groups() -> - [{p,test_lib:parallel(), + [{p,[parallel], [two,test1,fail,float_bin,in_guard,in_catch, nasty_literals,side_effect,opt,otp_7556,float_arith, otp_8054]}]. @@ -59,12 +62,9 @@ end_per_group(_GroupName, Config) -> init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = test_server:timetrap(?t:minutes(1)), - [{watchdog,Dog}|Config]. + Config. end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. two(Config) when is_list(Config) -> @@ -85,7 +85,7 @@ id(I) -> I. -define(T(B, L), {B, ??B, L}). -define(N(B), {B, ??B, unknown}). --define(FAIL(Expr), ?line {'EXIT',{badarg,_}} = (catch Expr)). +-define(FAIL(Expr), {'EXIT',{badarg,_}} = (catch Expr)). l(I_13, I_big1, I_16, Bin) -> [ @@ -199,7 +199,7 @@ one_test({C_bin, E_bin, Str, Bytes}) when is_list(Bytes) -> true -> io:format("ERROR: Compiled: ~p. Expected ~p. Got ~p.~n", [Str, Bytes, bitstring_to_list(C_bin)]), - test_server:fail(comp) + ct:fail(comp) end, if E_bin == Bin -> @@ -207,7 +207,7 @@ one_test({C_bin, E_bin, Str, Bytes}) when is_list(Bytes) -> true -> io:format("ERROR: Interpreted: ~p. Expected ~p. Got ~p.~n", [Str, Bytes, bitstring_to_list(E_bin)]), - test_server:fail(comp) + ct:fail(comp) end; one_test({C_bin, E_bin, Str, Result}) -> io:format(" ~s ~p~n", [Str, C_bin]), @@ -228,7 +228,7 @@ one_test({C_bin, E_bin, Str, Result}) -> io:format("ERROR: Compiled not equal to interpreted:" "~n ~p, ~p.~n", [bitstring_to_list(C_bin), bitstring_to_list(E_bin)]), - test_server:fail(comp); + ct:fail(comp); 0 -> ok; %% For situations where the final bits may not matter, like @@ -260,15 +260,15 @@ equal_lists(A, B, R) -> end. test1(Config) when is_list(Config) -> - ?line I_13 = i(13), - ?line I_big1 = big(1), - ?line I_16 = i(16), - ?line Bin = i(<<16#A5,16#5A,16#C3>>), - ?line Vars = lists:sort([{'I_13',I_13}, - {'I_big1',I_big1}, - {'I_16',I_16}, - {'Bin',Bin}]), - ?line lists:foreach(fun one_test/1, eval_list(l(I_13, I_big1, I_16, Bin), Vars)). + I_13 = i(13), + I_big1 = big(1), + I_16 = i(16), + Bin = i(<<16#A5,16#5A,16#C3>>), + Vars = lists:sort([{'I_13',I_13}, + {'I_big1',I_big1}, + {'I_16',I_16}, + {'Bin',Bin}]), + lists:foreach(fun one_test/1, eval_list(l(I_13, I_big1, I_16, Bin), Vars)). fail(Config) when is_list(Config) -> I_minus_777 = i(-777), @@ -277,68 +277,68 @@ fail(Config) when is_list(Config) -> %% One negative field size, but the sum of field sizes will be 1 byte. %% Make sure that we reject that properly. - ?line {'EXIT',{badarg,_}} = (catch <<I_minus_777:2048/unit:8, - 57:I_minus_2047/unit:8>>), + {'EXIT',{badarg,_}} = (catch <<I_minus_777:2048/unit:8, + 57:I_minus_2047/unit:8>>), %% Same thing, but use literals. - ?line {'EXIT',{badarg,_}} = (catch <<I_minus_777:2048/unit:8, - 57:(-2047)/unit:8>>), + {'EXIT',{badarg,_}} = (catch <<I_minus_777:2048/unit:8, + 57:(-2047)/unit:8>>), %% Not numbers. - ?line {'EXIT',{badarg,_}} = (catch <<45:(i(not_a_number))>>), - ?line {'EXIT',{badarg,_}} = (catch <<13:8,45:(i(not_a_number))>>), + {'EXIT',{badarg,_}} = (catch <<45:(i(not_a_number))>>), + {'EXIT',{badarg,_}} = (catch <<13:8,45:(i(not_a_number))>>), %% Unaligned sizes. BadSz = i(7), Bitstr = i(<<42:17>>), - ?line {'EXIT',{badarg,_}} = (catch <<Bitstr:4/binary>>), - ?line {'EXIT',{badarg,_}} = (catch <<Bitstr:BadSz/binary>>), + {'EXIT',{badarg,_}} = (catch <<Bitstr:4/binary>>), + {'EXIT',{badarg,_}} = (catch <<Bitstr:BadSz/binary>>), - ?line [] = [X || {X} <- [], X == <<Bitstr:BadSz/binary>>], - ?line [] = [X || {X} <- [], X == <<Bitstr:4/binary>>], + [] = [X || {X} <- [], X == <<Bitstr:BadSz/binary>>], + [] = [X || {X} <- [], X == <<Bitstr:4/binary>>], %% Literals with incorrect type. - ?line {'EXIT',{badarg,_}} = (catch <<42.0/integer>>), - ?line {'EXIT',{badarg,_}} = (catch <<42/binary>>), - ?line {'EXIT',{badarg,_}} = (catch <<an_atom/integer>>), + {'EXIT',{badarg,_}} = (catch <<42.0/integer>>), + {'EXIT',{badarg,_}} = (catch <<42/binary>>), + {'EXIT',{badarg,_}} = (catch <<an_atom/integer>>), ok. float_bin(Config) when is_list(Config) -> %% Some more coverage. - ?line {<<1,2,3>>,7.0} = float_bin_1(4), + {<<1,2,3>>,7.0} = float_bin_1(4), F = 42.0, - ?line <<42,0,0,0,0,0,0,69,64>> = <<(id(42)),F/little-float>>, + <<42,0,0,0,0,0,0,69,64>> = <<(id(42)),F/little-float>>, ok. float_bin_1(F) -> {<<1,2,3>>,F+3.0}. in_guard(Config) when is_list(Config) -> - ?line 1 = in_guard_1(<<16#74ad:16>>, 16#e95, 5), - ?line 2 = in_guard_1(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>), - ?line 3 = in_guard_1(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415), - ?line 3 = in_guard_1(<<16#FBCD:14,3/float,3:2>>, 16#FBCD, 3), - ?line 3 = in_guard_1(<<16#FBCD:14,(2 bsl 226)/float,3:2>>, 16#FBCD, 2 bsl 226), - ?line nope = in_guard_1(<<1>>, 42, b), - ?line nope = in_guard_1(<<1>>, a, b), - ?line nope = in_guard_1(<<1,2>>, 1, 1), - ?line nope = in_guard_1(<<4,5>>, 1, 2.71), - ?line nope = in_guard_1(<<4,5>>, 1, <<12,13>>), - - ?line 1 = in_guard_2(<<0,56>>, 7, blurf), - ?line 2 = in_guard_2(<<1,255>>, 511, blurf), - ?line 3 = in_guard_2(<<0,3>>, 0, blurf), - ?line 4 = in_guard_2(<<>>, 1, {<<7:16>>}), - ?line nope = in_guard_2(<<4,5>>, 1, blurf), - - ?line 42 = in_guard_3(<<1,2,3,42>>, <<1,2,3>>), - ?line 42 = in_guard_3(<<1,2,3,42>>, <<1,2,3>>), - ?line nope = in_guard_3(<<>>, <<>>), - - ?line ok = in_guard_4(<<15:4>>, 255), - ?line nope = in_guard_4(<<15:8>>, 255), + 1 = in_guard_1(<<16#74ad:16>>, 16#e95, 5), + 2 = in_guard_1(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>), + 3 = in_guard_1(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415), + 3 = in_guard_1(<<16#FBCD:14,3/float,3:2>>, 16#FBCD, 3), + 3 = in_guard_1(<<16#FBCD:14,(2 bsl 226)/float,3:2>>, 16#FBCD, 2 bsl 226), + nope = in_guard_1(<<1>>, 42, b), + nope = in_guard_1(<<1>>, a, b), + nope = in_guard_1(<<1,2>>, 1, 1), + nope = in_guard_1(<<4,5>>, 1, 2.71), + nope = in_guard_1(<<4,5>>, 1, <<12,13>>), + + 1 = in_guard_2(<<0,56>>, 7, blurf), + 2 = in_guard_2(<<1,255>>, 511, blurf), + 3 = in_guard_2(<<0,3>>, 0, blurf), + 4 = in_guard_2(<<>>, 1, {<<7:16>>}), + nope = in_guard_2(<<4,5>>, 1, blurf), + + 42 = in_guard_3(<<1,2,3,42>>, <<1,2,3>>), + 42 = in_guard_3(<<1,2,3,42>>, <<1,2,3>>), + nope = in_guard_3(<<>>, <<>>), + + ok = in_guard_4(<<15:4>>, 255), + nope = in_guard_4(<<15:8>>, 255), ok. in_guard_1(Bin, A, B) when <<A:13,B:3>> == Bin -> 1; @@ -360,10 +360,10 @@ in_guard_4(Bin, A) when <<A:4>> =:= Bin -> ok; in_guard_4(_, _) -> nope. in_catch(Config) when is_list(Config) -> - ?line <<42,0,5>> = small(42, 5), - ?line <<255>> = small(255, <<1,2,3,4,5,6,7,8,9>>), - ?line <<1,2>> = small(<<7,8,9,10>>, 258), - ?line <<>> = small(<<1,2,3,4,5>>, <<7,8,9,10>>), + <<42,0,5>> = small(42, 5), + <<255>> = small(255, <<1,2,3,4,5,6,7,8,9>>), + <<1,2>> = small(<<7,8,9,10>>, 258), + <<>> = small(<<1,2,3,4,5>>, <<7,8,9,10>>), <<15,240,0,42>> = small2(255, 42), <<7:20>> = small2(<<1,2,3>>, 7), @@ -412,20 +412,20 @@ small2(A, B) -> nasty_literals(Config) when is_list(Config) -> case erlang:system_info(endian) of big -> - ?line [0,42] = binary_to_list(id(<<42:16/native>>)); + [0,42] = binary_to_list(id(<<42:16/native>>)); little -> - ?line [42,0] = binary_to_list(id(<<42:16/native>>)) + [42,0] = binary_to_list(id(<<42:16/native>>)) end, - ?line Bin0 = id(<<1,2,3,0:10000000,4,5,6>>), - ?line 1250006 = size(Bin0), - ?line <<1,2,3,0:10000000,4,5,6>> = Bin0, + Bin0 = id(<<1,2,3,0:10000000,4,5,6>>), + 1250006 = size(Bin0), + <<1,2,3,0:10000000,4,5,6>> = Bin0, - ?line Bin1 = id(<<0:10000000,7,8,-1:10000000,9,10,0:10000000>>), - ?line 3750004 = size(Bin1), - ?line <<0:10000000,7,8,-1:10000000/signed,9,10,0:10000000>> = Bin1, + Bin1 = id(<<0:10000000,7,8,-1:10000000,9,10,0:10000000>>), + 3750004 = size(Bin1), + <<0:10000000,7,8,-1:10000000/signed,9,10,0:10000000>> = Bin1, - ?line <<255,255,0,0,0>> = id(<<255,255,0,0,0>>), + <<255,255,0,0,0>> = id(<<255,255,0,0,0>>), %% Coverage. I = 16#7777FFFF7777FFFF7777FFFF7777FFFF7777FFFF7777FFFF, @@ -434,18 +434,18 @@ nasty_literals(Config) when is_list(Config) -> ok. -define(COF(Int0), - ?line (fun(Int) -> - true = <<Int:32/float>> =:= <<(float(Int)):32/float>>, - true = <<Int:64/float>> =:= <<(float(Int)):64/float>> - end)(nonliteral(Int0)), - ?line true = <<Int0:32/float>> =:= <<(float(Int0)):32/float>>, - ?line true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>). + (fun(Int) -> + true = <<Int:32/float>> =:= <<(float(Int)):32/float>>, + true = <<Int:64/float>> =:= <<(float(Int)):64/float>> + end)(nonliteral(Int0)), + true = <<Int0:32/float>> =:= <<(float(Int0)):32/float>>, + true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>). -define(COF64(Int0), - ?line (fun(Int) -> - true = <<Int:64/float>> =:= <<(float(Int)):64/float>> - end)(nonliteral(Int0)), - ?line true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>). + (fun(Int) -> + true = <<Int:64/float>> =:= <<(float(Int)):64/float>> + end)(nonliteral(Int0)), + true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>). nonliteral(X) -> X. @@ -466,7 +466,7 @@ coerce_to_float(Config) when is_list(Config) -> side_effect(Config) when is_list(Config) -> {'EXIT',{badarg,_}} = (catch side_effect_1(a)), {'EXIT',{badarg,_}} = (catch side_effect_1(<<>>)), - ?line ok = side_effect_1(42), + ok = side_effect_1(42), ok. side_effect_1(A) -> @@ -476,32 +476,32 @@ side_effect_1(A) -> -record(otp_7029, {a,b}). opt(Config) when is_list(Config) -> - ?line 42 = otp_7029(#otp_7029{a = <<>>,b = 42}), + 42 = otp_7029(#otp_7029{a = <<>>,b = 42}), N = 16, - ?line <<1,3,65>> = id(<<1,833:N>>), - ?line <<1,66,3>> = id(<<1,834:N/little>>), - ?line <<1,65,136,0,0>> = id(<<1,17.0:32/float>>), - ?line <<1,64,8,0,0,0,0,0,0>> = id(<<1,3.0:N/float-unit:4>>), - ?line <<1,0,0,0,0,0,0,8,64>> = id(<<1,3.0:N/little-float-unit:4>>), - ?line {'EXIT',{badarg,_}} = (catch id(<<3.1416:N/float>>)), + <<1,3,65>> = id(<<1,833:N>>), + <<1,66,3>> = id(<<1,834:N/little>>), + <<1,65,136,0,0>> = id(<<1,17.0:32/float>>), + <<1,64,8,0,0,0,0,0,0>> = id(<<1,3.0:N/float-unit:4>>), + <<1,0,0,0,0,0,0,8,64>> = id(<<1,3.0:N/little-float-unit:4>>), + {'EXIT',{badarg,_}} = (catch id(<<3.1416:N/float>>)), B = <<1,2,3,4,5>>, - ?line <<0,1,2,3,4,5>> = id(<<0,B/binary>>), - ?line <<1,2,3,4,5,19>> = id(<<B:5/binary,19>>), - ?line <<1,2,3,42>> = id(<<B:3/binary,42>>), + <<0,1,2,3,4,5>> = id(<<0,B/binary>>), + <<1,2,3,4,5,19>> = id(<<B:5/binary,19>>), + <<1,2,3,42>> = id(<<B:3/binary,42>>), - ?line {'EXIT',_} = (catch <<<<23,56,0,2>>:(2.5)/binary>>), - ?line {'EXIT',_} = (catch <<<<23,56,0,2>>:(-16)/binary>>), - ?line {'EXIT',_} = (catch <<<<23,56,0,2>>:(anka)>>), - ?line {'EXIT',_} = (catch <<<<23,56,0,2>>:64/float>>), - ?line {'EXIT',_} = (catch <<<<23,56,0,2:7>>/binary>>), + {'EXIT',_} = (catch <<<<23,56,0,2>>:(2.5)/binary>>), + {'EXIT',_} = (catch <<<<23,56,0,2>>:(-16)/binary>>), + {'EXIT',_} = (catch <<<<23,56,0,2>>:(anka)>>), + {'EXIT',_} = (catch <<<<23,56,0,2>>:64/float>>), + {'EXIT',_} = (catch <<<<23,56,0,2:7>>/binary>>), %% Test constant propagation - there should be a warning. BadSz = 2.5, {'EXIT',_} = (catch <<<<N,56,0,2>>:BadSz/binary>>), case id(false) of - true -> ?line opt_dont_call_me(); + true -> opt_dont_call_me(); false -> ok end, @@ -529,7 +529,7 @@ otp_7556(Bin, A, B, C) -> %% for a binary construction with a later allocation). float_arith(Config) when is_list(Config) -> - ?line {<<1,2,3,64,69,0,0,0,0,0,0>>,21.0} = do_float_arith(<<1,2,3>>, 42, 2), + {<<1,2,3,64,69,0,0,0,0,0,0>>,21.0} = do_float_arith(<<1,2,3>>, 42, 2), ok. do_float_arith(Bin0, X, Y) -> @@ -537,7 +537,7 @@ do_float_arith(Bin0, X, Y) -> {Bin,X / Y}. otp_8054(Config) when is_list(Config) -> - ?line <<"abc">> = otp_8054_1([null,1,2,3], <<"abc">>), + <<"abc">> = otp_8054_1([null,1,2,3], <<"abc">>), ok. otp_8054_1([H|T], Bin) -> diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl index 10e3451e8f..224abf6c29 100644 --- a/lib/compiler/test/bs_match_SUITE.erl +++ b/lib/compiler/test/bs_match_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -23,43 +24,52 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, - fun_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1, - bin_tail/1,save_restore/1,shadowed_size_var/1, + size_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1, + bin_tail/1,save_restore/1, partitioned_bs_match/1,function_clause/1, unit/1,shared_sub_bins/1,bin_and_float/1, - dec_subidentifiers/1,skip_optional_tag/1, + dec_subidentifiers/1,skip_optional_tag/1,decode_integer/1, wfbm/1,degenerated_match/1,bs_sum/1,coverage/1, multiple_uses/1,zero_label/1,followed_by_catch/1, matching_meets_construction/1,simon/1,matching_and_andalso/1, otp_7188/1,otp_7233/1,otp_7240/1,otp_7498/1, match_string/1,zero_width/1,bad_size/1,haystack/1, cover_beam_bool/1,matched_out_size/1,follow_fail_branch/1, - no_partition/1,calling_a_binary/1,binary_in_map/1]). + no_partition/1,calling_a_binary/1,binary_in_map/1, + match_string_opt/1,select_on_integer/1, + map_and_binary/1,unsafe_branch_caching/1, + bad_literals/1,good_literals/1,constant_propagation/1]). -export([coverage_id/1,coverage_external_ignore/2]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("syntax_tools/include/merl.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> test_lib:recompile(?MODULE), [{group,p}]. groups() -> - [{p,test_lib:parallel(), - [fun_shadow,int_float,otp_5269,null_fields,wiger, - bin_tail,save_restore,shadowed_size_var, + [{p,[parallel], + [size_shadow,int_float,otp_5269,null_fields,wiger, + bin_tail,save_restore, partitioned_bs_match,function_clause,unit, shared_sub_bins,bin_and_float,dec_subidentifiers, - skip_optional_tag,wfbm,degenerated_match,bs_sum, + skip_optional_tag,decode_integer,wfbm,degenerated_match,bs_sum, coverage,multiple_uses,zero_label,followed_by_catch, matching_meets_construction,simon, matching_and_andalso,otp_7188,otp_7233,otp_7240, otp_7498,match_string,zero_width,bad_size,haystack, cover_beam_bool,matched_out_size,follow_fail_branch, - no_partition,calling_a_binary,binary_in_map]}]. + no_partition,calling_a_binary,binary_in_map, + match_string_opt,select_on_integer, + map_and_binary,unsafe_branch_caching, + bad_literals,good_literals,constant_propagation]}]. init_per_suite(Config) -> @@ -76,108 +86,133 @@ end_per_group(_GroupName, Config) -> init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = test_server:timetrap(?t:minutes(1)), - [{watchdog,Dog}|Config]. + Config. end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. -fun_shadow(Config) when is_list(Config) -> - %% OTP-5270 - ?line 7 = fun_shadow_1(), - ?line 7 = fun_shadow_2(8), - ?line 7 = fun_shadow_3(), - ?line no = fun_shadow_4(8), +size_shadow(Config) when is_list(Config) -> + %% Originally OTP-5270. + 7 = size_shadow_1(), + 7 = size_shadow_2(8), + 7 = size_shadow_3(), + no = size_shadow_4(8), + Any = {any,term,goes}, + {2577,Any,-175,whatever} = + (size_shadow_5(Any, 12))(<<2577:12>>, -175, whatever), + {7777,Any,42,whatever} = + (size_shadow_6(Any, 13))(42, <<7777:13>>, whatever), + {<<45>>,<<>>} = size_shadow_7({int,1}, <<1:16,45>>), + {'EXIT',{function_clause,_}} = + (catch size_shadow_7({int,42}, <<1:16,45>>)), ok. -fun_shadow_1() -> +size_shadow_1() -> L = 8, F = fun(<<L:L,B:L>>) -> B end, F(<<16:8, 7:16>>). -fun_shadow_2(L) -> +size_shadow_2(L) -> F = fun(<<L:L,B:L>>) -> B end, F(<<16:8, 7:16>>). -fun_shadow_3() -> +size_shadow_3() -> L = 8, F = fun(<<L:L,B:L,L:L>>) -> B end, F(<<16:8, 7:16,16:16>>). -fun_shadow_4(L) -> +size_shadow_4(L) -> F = fun(<<L:L,B:L,L:L>>) -> B; (_) -> no end, F(<<16:8, 7:16,15:16>>). +size_shadow_5(X, Y) -> + fun (<< A:Y >>, Y, B) -> fum(A, X, Y, B) end. + +size_shadow_6(X, Y) -> + fun (Y, << A:Y >>, B) -> fum(A, X, Y, B) end. + +fum(A, B, C, D) -> + {A,B,C,D}. + +size_shadow_7({int,N}, <<N:16,B:N/binary,T/binary>>) -> + {B,T}. + + int_float(Config) when is_list(Config) -> %% OTP-5323 - ?line <<103133.0:64/float>> = <<103133:64/float>>, - ?line <<103133:64/float>> = <<103133:64/float>>, - ok. + <<103133.0:64/float>> = <<103133:64/float>>, + <<103133:64/float>> = <<103133:64/float>>, + + %% Coverage of error cases in sys_pre_expand:coerce_to_float/2. + case id(default) of + <<(1 bsl 1024):64/float>> -> + ct:fail(should_not_match); + default -> + ok + end. %% Stolen from erl_eval_SUITE and modified. %% OTP-5269. Bugs in the bit syntax. otp_5269(Config) when is_list(Config) -> - ?line check(fun() -> L = 8, - F = fun(<<A:L,B:A>>) -> B end, - F(<<16:8, 7:16>>) + check(fun() -> L = 8, + F = fun(<<A:L,B:A>>) -> B end, + F(<<16:8, 7:16>>) end, 7), - ?line check(fun() -> L = 8, <<A:L,B:A>> = <<16:8, 7:16>>, B end, - 7), - ?line check(fun() -> U = 8, (fun(<<U:U>>) -> U end)(<<32:8>>) end, - 32), - ?line check(fun() -> U = 8, [U || <<U:U>> <- [<<32:8>>]] end, - [32]), - ?line check(fun() -> [X || <<A:8, - B:A>> <- [<<16:8,19:16>>], - <<X:8>> <- [<<B:8>>]] end, - [19]), - ?line check(fun() -> A = 4, B = 28, bit_size(<<13:(A+(X=B))>>), X end, - 28), - ?line check(fun() -> - <<Size,B:Size/binary,Rest/binary>> = <<2,"AB","CD">>, - {Size,B,Rest} - end, - {2,<<"AB">>,<<"CD">>}), - ?line check(fun() -> X = 32, - [X || <<X:X>> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end, - %% "binsize variable" ^ - [1,2]), - ?line check(fun() -> - (fun (<<A:1/binary, B:8/integer, _C:B/binary>>) -> - case A of - B -> wrong; - _ -> ok - end - end)(<<1,2,3,4>>) end, - ok), + check(fun() -> L = 8, <<A:L,B:A>> = <<16:8, 7:16>>, B end, + 7), + check(fun() -> U = 8, (fun(<<U:U>>) -> U end)(<<32:8>>) end, + 32), + check(fun() -> U = 8, [U || <<U:U>> <- [<<32:8>>]] end, + [32]), + check(fun() -> [X || <<A:8, + B:A>> <- [<<16:8,19:16>>], + <<X:8>> <- [<<B:8>>]] end, + [19]), + check(fun() -> A = 4, B = 28, bit_size(<<13:(A+(X=B))>>), X end, + 28), + check(fun() -> + <<Size,B:Size/binary,Rest/binary>> = <<2,"AB","CD">>, + {Size,B,Rest} + end, + {2,<<"AB">>,<<"CD">>}), + check(fun() -> X = 32, + [X || <<X:X>> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end, + %% "binsize variable" ^ + [1,2]), + check(fun() -> + (fun (<<A:1/binary, B:8/integer, _C:B/binary>>) -> + case A of + B -> wrong; + _ -> ok + end + end)(<<1,2,3,4>>) end, + ok), ok. null_fields(Config) when is_list(Config) -> - ?line check(fun() -> - W = id(0), - F = fun(<<_:W>>) -> tail; - (<<>>) -> empty - end, - F(<<>>) - end, tail), - ?line check(fun() -> - F = fun(<<_/binary>>) -> tail; - (<<>>) -> empty - end, - F(<<>>) - end, tail), + check(fun() -> + W = id(0), + F = fun(<<_:W>>) -> tail; + (<<>>) -> empty + end, + F(<<>>) + end, tail), + check(fun() -> + F = fun(<<_/binary>>) -> tail; + (<<>>) -> empty + end, + F(<<>>) + end, tail), ok. wiger(Config) when is_list(Config) -> - ?line ok1 = wcheck(<<3>>), - ?line ok2 = wcheck(<<1,2,3>>), - ?line ok3 = wcheck(<<4>>), - ?line {error,<<1,2,3,4>>} = wcheck(<<1,2,3,4>>), - ?line {error,<<>>} = wcheck(<<>>), + ok1 = wcheck(<<3>>), + ok2 = wcheck(<<1,2,3>>), + ok3 = wcheck(<<4>>), + {error,<<1,2,3,4>>} = wcheck(<<1,2,3,4>>), + {error,<<>>} = wcheck(<<>>), ok. wcheck(<<A>>) when A==3-> @@ -191,24 +226,24 @@ wcheck(Other) -> bin_tail(Config) when is_list(Config) -> S = <<"abcde">>, - ?line $a = bin_tail_c(S, 0), - ?line $c = bin_tail_c(S, 2), - ?line $e = bin_tail_c(S, 4), - ?line {'EXIT',_} = (catch bin_tail_c(S, 5)), - ?line {'EXIT',_} = (catch bin_tail_c_var(S, 5)), - - ?line $a = bin_tail_d(S, 0), - ?line $b = bin_tail_d(S, 8), - ?line $d = bin_tail_d(S, 3*8), - ?line {'EXIT',_} = (catch bin_tail_d_dead(S, 1)), - ?line {'EXIT',_} = (catch bin_tail_d_dead(S, 9)), - ?line {'EXIT',_} = (catch bin_tail_d_dead(S, 5*8)), - ?line {'EXIT',_} = (catch bin_tail_d_var(S, 1)), - - ?line ok = bin_tail_e(<<2:2,0:1,1:5>>), - ?line ok = bin_tail_e(<<2:2,1:1,1:5,42:64>>), - ?line error = bin_tail_e(<<3:2,1:1,1:5,42:64>>), - ?line error = bin_tail_e(<<>>), + $a = bin_tail_c(S, 0), + $c = bin_tail_c(S, 2), + $e = bin_tail_c(S, 4), + {'EXIT',_} = (catch bin_tail_c(S, 5)), + {'EXIT',_} = (catch bin_tail_c_var(S, 5)), + + $a = bin_tail_d(S, 0), + $b = bin_tail_d(S, 8), + $d = bin_tail_d(S, 3*8), + {'EXIT',_} = (catch bin_tail_d_dead(S, 1)), + {'EXIT',_} = (catch bin_tail_d_dead(S, 9)), + {'EXIT',_} = (catch bin_tail_d_dead(S, 5*8)), + {'EXIT',_} = (catch bin_tail_d_var(S, 1)), + + ok = bin_tail_e(<<2:2,0:1,1:5>>), + ok = bin_tail_e(<<2:2,1:1,1:5,42:64>>), + error = bin_tail_e(<<3:2,1:1,1:5,42:64>>), + error = bin_tail_e(<<>>), ok. bin_tail_c(Bin, Offset) -> @@ -267,34 +302,34 @@ bin_tail_e_var(Bin) -> end. save_restore(Config) when is_list(Config) -> - ?line 0 = save_restore_1(<<0:2,42:6>>), - ?line {1,3456} = save_restore_1(<<1:2,3456:14>>), - ?line {2,7981234} = save_restore_1(<<2:2,7981234:30>>), - ?line {3,763967493838} = save_restore_1(<<0:2,763967493838:62>>), + 0 = save_restore_1(<<0:2,42:6>>), + {1,3456} = save_restore_1(<<1:2,3456:14>>), + {2,7981234} = save_restore_1(<<2:2,7981234:30>>), + {3,763967493838} = save_restore_1(<<0:2,763967493838:62>>), A = <<" x">>, B = <<".x">>, C = <<"-x">>, - ?line {" ",<<"x">>} = lll(A), - ?line {" ",<<"x">>} = mmm(A), - ?line {" ",<<"x">>} = nnn(A), - ?line {" ",<<"x">>} = ooo(A), + {" ",<<"x">>} = lll(A), + {" ",<<"x">>} = mmm(A), + {" ",<<"x">>} = nnn(A), + {" ",<<"x">>} = ooo(A), - ?line {".",<<"x">>} = lll(B), - ?line {".",<<"x">>} = mmm(B), - ?line {".",<<"x">>} = nnn(B), - ?line {".",<<"x">>} = ooo(B), + {".",<<"x">>} = lll(B), + {".",<<"x">>} = mmm(B), + {".",<<"x">>} = nnn(B), + {".",<<"x">>} = ooo(B), - ?line {"-",<<"x">>} = lll(C), - ?line {"-",<<"x">>} = mmm(C), - ?line {"-",<<"x">>} = nnn(C), - ?line {"-",<<"x">>} = ooo(C), + {"-",<<"x">>} = lll(C), + {"-",<<"x">>} = mmm(C), + {"-",<<"x">>} = nnn(C), + {"-",<<"x">>} = ooo(C), Bin = <<-1:64>>, case bad_float_unpack_match(Bin) of -1 -> ok; - _Other -> ?line ?t:fail(bad_return_value_probably_NaN) + _Other -> ct:fail(bad_return_value_probably_NaN) end. save_restore_1(Bin) -> @@ -322,29 +357,19 @@ bad_float_unpack_match(<<F:64/float>>) -> F; bad_float_unpack_match(<<I:64/integer-signed>>) -> I. -shadowed_size_var(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Dir = filename:dirname(code:which(?MODULE)), - ?line Core = filename:join(Dir, "bs_shadowed_size_var"), - ?line Opts = [from_core,{outdir,PrivDir}|test_lib:opt_opts(?MODULE)], - ?line io:format("~p", [Opts]), - ?line {ok,Mod} = c:c(Core, Opts), - ?line [42|<<"abcde">>] = Mod:filter_essentials([<<42:32>>|<<5:32,"abcde">>]), - ok. - partitioned_bs_match(Config) when is_list(Config) -> - ?line <<1,2,3>> = partitioned_bs_match(blurf, <<42,1,2,3>>), - ?line error = partitioned_bs_match(10, <<7,8,15,13>>), - ?line error = partitioned_bs_match(100, {a,tuple,is,'not',a,binary}), - ?line ok = partitioned_bs_match(0, <<>>), - ?line fc(partitioned_bs_match, [-1,blurf], + <<1,2,3>> = partitioned_bs_match(blurf, <<42,1,2,3>>), + error = partitioned_bs_match(10, <<7,8,15,13>>), + error = partitioned_bs_match(100, {a,tuple,is,'not',a,binary}), + ok = partitioned_bs_match(0, <<>>), + fc(partitioned_bs_match, [-1,blurf], catch partitioned_bs_match(-1, blurf)), - ?line fc(partitioned_bs_match, [-1,<<1,2,3>>], + fc(partitioned_bs_match, [-1,<<1,2,3>>], catch partitioned_bs_match(-1, <<1,2,3>>)), - ?line {17,<<1,2,3>>} = partitioned_bs_match_2(1, <<17,1,2,3>>), - ?line {7,<<1,2,3>>} = partitioned_bs_match_2(7, <<17,1,2,3>>), + {17,<<1,2,3>>} = partitioned_bs_match_2(1, <<17,1,2,3>>), + {7,<<1,2,3>>} = partitioned_bs_match_2(7, <<17,1,2,3>>), - ?line fc(partitioned_bs_match_2, [4,<<0:17>>], + fc(partitioned_bs_match_2, [4,<<0:17>>], catch partitioned_bs_match_2(4, <<0:17>>)), anything = partitioned_bs_match_3(anything, <<42>>), @@ -368,11 +393,20 @@ partitioned_bs_match_3(Var, <<_>>) -> Var; partitioned_bs_match_3(1, 2) -> ok. function_clause(Config) when is_list(Config) -> - ?line ok = function_clause_1(<<0,7,0,7,42>>), - ?line fc(function_clause_1, [<<0,1,2,3>>], - catch function_clause_1(<<0,1,2,3>>)), - ?line fc(function_clause_1, [<<0,1,2,3>>], - catch function_clause_1(<<0,7,0,1,2,3>>)), + ok = function_clause_1(<<0,7,0,7,42>>), + fc(function_clause_1, [<<0,1,2,3>>], + catch function_clause_1(<<0,1,2,3>>)), + fc(function_clause_1, [<<0,1,2,3>>], + catch function_clause_1(<<0,7,0,1,2,3>>)), + + ok = function_clause_2(<<0,7,0,7,42>>), + ok = function_clause_2(<<255>>), + ok = function_clause_2(<<13:4>>), + fc(function_clause_2, [<<0,1,2,3>>], + catch function_clause_2(<<0,1,2,3>>)), + fc(function_clause_2, [<<0,1,2,3>>], + catch function_clause_2(<<0,7,0,1,2,3>>)), + ok. function_clause_1(<<0:8,7:8,T/binary>>) -> @@ -380,26 +414,33 @@ function_clause_1(<<0:8,7:8,T/binary>>) -> function_clause_1(<<_:8>>) -> ok. +function_clause_2(<<0:8,7:8,T/binary>>) -> + function_clause_2(T); +function_clause_2(<<_:8>>) -> + ok; +function_clause_2(<<_:4>>) -> + ok. + unit(Config) when is_list(Config) -> - ?line 42 = peek1(<<42>>), - ?line 43 = peek1(<<43,1,2>>), - ?line 43 = peek1(<<43,1,2,(-1):1>>), - ?line 43 = peek1(<<43,1,2,(-1):2>>), - ?line 43 = peek1(<<43,1,2,(-1):7>>), - - ?line 99 = peek8(<<99>>), - ?line 100 = peek8(<<100,101>>), - ?line fc(peek8, [<<100,101,0:1>>], catch peek8(<<100,101,0:1>>)), - - ?line 37484 = peek16(<<37484:16>>), - ?line 37489 = peek16(<<37489:16,5566:16>>), - ?line fc(peek16, [<<8>>], catch peek16(<<8>>)), - ?line fc(peek16, [<<42:15>>], catch peek16(<<42:15>>)), - ?line fc(peek16, [<<1,2,3,4,5>>], catch peek16(<<1,2,3,4,5>>)), - - ?line 127 = peek7(<<127:7>>), - ?line 100 = peek7(<<100:7,19:7>>), - ?line fc(peek7, [<<1,2>>], catch peek7(<<1,2>>)), + 42 = peek1(<<42>>), + 43 = peek1(<<43,1,2>>), + 43 = peek1(<<43,1,2,(-1):1>>), + 43 = peek1(<<43,1,2,(-1):2>>), + 43 = peek1(<<43,1,2,(-1):7>>), + + 99 = peek8(<<99>>), + 100 = peek8(<<100,101>>), + fc(peek8, [<<100,101,0:1>>], catch peek8(<<100,101,0:1>>)), + + 37484 = peek16(<<37484:16>>), + 37489 = peek16(<<37489:16,5566:16>>), + fc(peek16, [<<8>>], catch peek16(<<8>>)), + fc(peek16, [<<42:15>>], catch peek16(<<42:15>>)), + fc(peek16, [<<1,2,3,4,5>>], catch peek16(<<1,2,3,4,5>>)), + + 127 = peek7(<<127:7>>), + 100 = peek7(<<100:7,19:7>>), + fc(peek7, [<<1,2>>], catch peek7(<<1,2>>)), ok. peek1(<<B:8,_/bitstring>>) -> B. @@ -411,7 +452,7 @@ peek8(<<B:8,_/binary>>) -> B. peek16(<<B:16,_/binary-unit:16>>) -> B. shared_sub_bins(Config) when is_list(Config) -> - ?line {15,[<<>>,<<5>>,<<4,5>>,<<3,4,5>>,<<2,3,4,5>>]} = sum(<<1,2,3,4,5>>, [], 0), + {15,[<<>>,<<5>>,<<4,5>>,<<3,4,5>>,<<2,3,4,5>>]} = sum(<<1,2,3,4,5>>, [], 0), ok. sum(<<B,T/binary>>, Acc, Sum) -> @@ -420,7 +461,7 @@ sum(<<>>, Last, Sum) -> {Sum,Last}. bin_and_float(Config) when is_list(Config) -> - ?line 14.0 = bin_and_float(<<1.0/float,2.0/float,3.0/float>>, 0.0), + 14.0 = bin_and_float(<<1.0/float,2.0/float,3.0/float>>, 0.0), ok. bin_and_float(<<X/float,Y/float,Z/float,T/binary>>, Sum) when is_float(X), @@ -430,10 +471,10 @@ bin_and_float(<<X/float,Y/float,Z/float,T/binary>>, Sum) when is_float(X), bin_and_float(<<>>, Sum) -> Sum. dec_subidentifiers(Config) when is_list(Config) -> - ?line {[],<<1,2,3>>} = + {[],<<1,2,3>>} = do_dec_subidentifiers(<<1:1,42:7,1:1,99:7,1,2,3>>, 0, [], 2), - ?line {[5389],<<1,2,3>>} = do_dec_subidentifiers(<<1:1,42:7,0:1,13:7,1,2,3>>, 0, [], 2), - ?line {[3,2,1],not_a_binary} = dec_subidentifiers(not_a_binary, any, [1,2,3], 0), + {[5389],<<1,2,3>>} = do_dec_subidentifiers(<<1:1,42:7,0:1,13:7,1,2,3>>, 0, [], 2), + {[3,2,1],not_a_binary} = dec_subidentifiers(not_a_binary, any, [1,2,3], 0), ok. do_dec_subidentifiers(Buffer, Av, Al, Len) -> @@ -484,23 +525,40 @@ skip_optional_tag(<<Tag,RestTag/binary>>, <<Tag,Rest/binary>>) -> skip_optional_tag(RestTag, Rest); skip_optional_tag(_, _) -> missing. +decode_integer(_Config) -> + {10795,<<43>>,whatever} = decode_integer(1, <<42,43>>, whatever), + {-28909,<<19>>,whatever} = decode_integer(1, <<143,19>>, whatever), + ok. + +decode_integer(Len, <<B1:1,B2:7,Bs/binary>>, RemovedBytes) when B1 == 0 -> + Bin = <<_Skip:Len/unit:8, Buffer2/binary>> = <<B1:1,B2:7,Bs/binary>>, + Size = byte_size(Bin), + <<Int:Size/unit:8>> = Bin, + {Int,Buffer2,RemovedBytes}; +decode_integer(Len, <<B1:1,B2:7,Bs/binary>>, RemovedBytes) -> + Bin = <<_Skip:Len/unit:8,Buffer2/binary>> = <<B1:1,B2:7,Bs/binary>>, + Size = byte_size(Bin), + <<N:Size/unit:8>> = <<B2,Bs/binary>>, + Int = N - (1 bsl (8 * size(Bin) -1)), + {Int,Buffer2,RemovedBytes}. + -define(DATELEN, 16). wfbm(Config) when is_list(Config) -> %% check_for_dot_or_space and get_tail is from wfbm4 by Steve Vinoski, %% with modifications. - ?line {nomatch,0} = check_for_dot_or_space(<<" ">>), - ?line {nomatch,0} = check_for_dot_or_space(<<" abc">>), - ?line {ok,<<"abcde">>} = check_for_dot_or_space(<<"abcde 34555">>), - ?line {nomatch,0} = check_for_dot_or_space(<<".gurka">>), - ?line {nomatch,1} = check_for_dot_or_space(<<"g.urka">>), - - ?line nomatch = get_tail(<<>>), - ?line {ok,<<"2007/10/23/blurf">>} = get_tail(<<"200x/2007/10/23/blurf ">>), - ?line {skip,?DATELEN+5} = get_tail(<<"200x/2007/10/23/blurf.">>), - ?line nomatch = get_tail(<<"200y.2007.10.23.blurf ">>), - ?line {'EXIT',_} = (catch get_tail({no,binary,at,all})), - ?line {'EXIT',_} = (catch get_tail(no_binary)), + {nomatch,0} = check_for_dot_or_space(<<" ">>), + {nomatch,0} = check_for_dot_or_space(<<" abc">>), + {ok,<<"abcde">>} = check_for_dot_or_space(<<"abcde 34555">>), + {nomatch,0} = check_for_dot_or_space(<<".gurka">>), + {nomatch,1} = check_for_dot_or_space(<<"g.urka">>), + + nomatch = get_tail(<<>>), + {ok,<<"2007/10/23/blurf">>} = get_tail(<<"200x/2007/10/23/blurf ">>), + {skip,?DATELEN+5} = get_tail(<<"200x/2007/10/23/blurf.">>), + nomatch = get_tail(<<"200y.2007.10.23.blurf ">>), + {'EXIT',_} = (catch get_tail({no,binary,at,all})), + {'EXIT',_} = (catch get_tail(no_binary)), ok. check_for_dot_or_space(Bin) -> @@ -533,13 +591,13 @@ get_tail(Bin) -> end. degenerated_match(Config) when is_list(Config) -> - ?line error = degenerated_match_1(<<>>), - ?line 1 = degenerated_match_1(<<1:1>>), - ?line 2 = degenerated_match_1(<<42,43>>), + error = degenerated_match_1(<<>>), + 1 = degenerated_match_1(<<1:1>>), + 2 = degenerated_match_1(<<42,43>>), - ?line error = degenerated_match_2(<<>>), - ?line no_split = degenerated_match_2(<<1,2>>), - ?line {<<1,2,3,4>>,<<5>>} = degenerated_match_2(<<1,2,3,4,5>>), + error = degenerated_match_2(<<>>), + no_split = degenerated_match_2(<<1,2>>), + {<<1,2,3,4>>,<<5>>} = degenerated_match_2(<<1,2,3,4,5>>), ok. @@ -556,25 +614,25 @@ degenerated_match_2(Bin) -> end. bs_sum(Config) when is_list(Config) -> - ?line 0 = bs_sum_1([]), - ?line 0 = bs_sum_1(<<>>), - ?line 42 = bs_sum_1([42]), - ?line 1 = bs_sum_1(<<1>>), - ?line 10 = bs_sum_1([1,2,3,4]), - ?line 15 = bs_sum_1(<<1,2,3,4,5>>), - ?line 21 = bs_sum_1([1,2,3|<<4,5,6>>]), - ?line 15 = bs_sum_1([1,2,3|{4,5}]), - ?line 6 = bs_sum_1([1,2,3|zero]), - ?line 6 = bs_sum_1([1,2,3|0]), - ?line 7 = bs_sum_1([1,2,3|one]), - - ?line fc(catch bs_sum_1({too,big,tuple})), - ?line fc(catch bs_sum_1([1,2,3|{too,big,tuple}])), - - ?line [] = sneaky_alias(<<>>), - ?line [559,387655] = sneaky_alias(id(<<559:32,387655:32>>)), - ?line fc(sneaky_alias, [<<1>>], catch sneaky_alias(id(<<1>>))), - ?line fc(sneaky_alias, [[1,2,3,4]], catch sneaky_alias(lists:seq(1, 4))), + 0 = bs_sum_1([]), + 0 = bs_sum_1(<<>>), + 42 = bs_sum_1([42]), + 1 = bs_sum_1(<<1>>), + 10 = bs_sum_1([1,2,3,4]), + 15 = bs_sum_1(<<1,2,3,4,5>>), + 21 = bs_sum_1([1,2,3|<<4,5,6>>]), + 15 = bs_sum_1([1,2,3|{4,5}]), + 6 = bs_sum_1([1,2,3|zero]), + 6 = bs_sum_1([1,2,3|0]), + 7 = bs_sum_1([1,2,3|one]), + + fc(catch bs_sum_1({too,big,tuple})), + fc(catch bs_sum_1([1,2,3|{too,big,tuple}])), + + [] = sneaky_alias(<<>>), + [559,387655] = sneaky_alias(id(<<559:32,387655:32>>)), + fc(sneaky_alias, [<<1>>], catch sneaky_alias(id(<<1>>))), + fc(sneaky_alias, [[1,2,3,4]], catch sneaky_alias(lists:seq(1, 4))), ok. bs_sum_1(<<H,T/binary>>) -> H+bs_sum_1(T); @@ -590,31 +648,31 @@ sneaky_alias(<<>>=L) -> binary_to_list(L); sneaky_alias(<<From:32,L/binary>>) -> [From|sneaky_alias(L)]. coverage(Config) when is_list(Config) -> - ?line 0 = coverage_fold(fun(B, A) -> A+B end, 0, <<>>), - ?line 6 = coverage_fold(fun(B, A) -> A+B end, 0, <<1,2,3>>), - ?line fc(catch coverage_fold(fun(B, A) -> + 0 = coverage_fold(fun(B, A) -> A+B end, 0, <<>>), + 6 = coverage_fold(fun(B, A) -> A+B end, 0, <<1,2,3>>), + fc(catch coverage_fold(fun(B, A) -> A+B end, 0, [a,b,c])), - ?line {<<42.0:64/float>>,float} = coverage_build(<<>>, <<42>>, float), - ?line {<<>>,not_a_tuple} = coverage_build(<<>>, <<>>, not_a_tuple), - ?line {<<16#76,"abc",16#A9,"abc">>,{x,42,43}} = + {<<42.0:64/float>>,float} = coverage_build(<<>>, <<42>>, float), + {<<>>,not_a_tuple} = coverage_build(<<>>, <<>>, not_a_tuple), + {<<16#76,"abc",16#A9,"abc">>,{x,42,43}} = coverage_build(<<>>, <<16#7,16#A>>, {x,y,z}), - ?line [<<2>>,<<1>>] = coverage_bc(<<1,2>>, []), + [<<2>>,<<1>>] = coverage_bc(<<1,2>>, []), - ?line {x,<<"abc">>,z} = coverage_setelement(<<2,"abc">>, {x,y,z}), + {x,<<"abc">>,z} = coverage_setelement(<<2,"abc">>, {x,y,z}), - ?line [42] = coverage_apply(<<42>>, [coverage_id]), - ?line 42 = coverage_external(<<42>>), + [42] = coverage_apply(<<42>>, [coverage_id]), + 42 = coverage_external(<<42>>), - ?line do_coverage_bin_to_term_list([]), - ?line do_coverage_bin_to_term_list([lists:seq(0, 10),{a,b,c},<<23:42>>]), - ?line fc(coverage_bin_to_term_list, [<<0,0,0,7>>], + do_coverage_bin_to_term_list([]), + do_coverage_bin_to_term_list([lists:seq(0, 10),{a,b,c},<<23:42>>]), + fc(coverage_bin_to_term_list, [<<0,0,0,7>>], catch do_coverage_bin_to_term_list_1(<<7:32>>)), - ?line <<>> = coverage_per_key(<<4:32>>), - ?line <<$a,$b,$c>> = coverage_per_key(<<7:32,"abc">>), + <<>> = coverage_per_key(<<4:32>>), + <<$a,$b,$c>> = coverage_per_key(<<7:32,"abc">>), ok. @@ -707,9 +765,9 @@ coverage_per_key(<<BinSize:32,Bin/binary>> = B) -> Bin. multiple_uses(Config) when is_list(Config) -> - ?line {344,62879,345,<<245,159,1,89>>} = multiple_uses_1(<<1,88,245,159,1,89>>), - ?line true = multiple_uses_2(<<0,0,197,18>>), - ?line <<42,43>> = multiple_uses_3(<<0,0,42,43>>, fun id/1), + {344,62879,345,<<245,159,1,89>>} = multiple_uses_1(<<1,88,245,159,1,89>>), + true = multiple_uses_2(<<0,0,197,18>>), + <<42,43>> = multiple_uses_3(<<0,0,42,43>>, fun id/1), ok. multiple_uses_1(<<X:16,Tail/binary>>) -> @@ -732,8 +790,8 @@ multiple_uses_cmp(<<Y:16>>, <<Y:16>>) -> true; multiple_uses_cmp(<<_:16>>, <<_:16>>) -> false. zero_label(Config) when is_list(Config) -> - ?line <<"nosemouth">> = read_pols(<<"FACE","nose","mouth">>), - ?line <<"CE">> = read_pols(<<"noFACE">>), + <<"nosemouth">> = read_pols(<<"FACE","nose","mouth">>), + <<"CE">> = read_pols(<<"noFACE">>), ok. read_pols(Data) -> @@ -761,14 +819,14 @@ matching_meets_construction(Config) when is_list(Config) -> Bin = id(<<"abc">>), Len = id(2), Tail0 = id(<<1,2,3,4,5>>), - ?line <<_:Len/binary,Tail/binary>> = Tail0, - ?line Res = <<Tail/binary,Bin/binary>>, - ?line <<3,4,5,"abc">> = Res, - ?line {'EXIT',{badarg,_}} = (catch matching_meets_construction_1(<<"Abc">>)), - ?line {'EXIT',{badarg,_}} = (catch matching_meets_construction_2(<<"Abc">>)), - ?line <<"Bbc">> = matching_meets_construction_3(<<"Abc">>), - - ?line <<1,2>> = encode_octet_string(<<1,2,3>>, 2), + <<_:Len/binary,Tail/binary>> = Tail0, + Res = <<Tail/binary,Bin/binary>>, + <<3,4,5,"abc">> = Res, + {'EXIT',{badarg,_}} = (catch matching_meets_construction_1(<<"Abc">>)), + {'EXIT',{badarg,_}} = (catch matching_meets_construction_2(<<"Abc">>)), + <<"Bbc">> = matching_meets_construction_3(<<"Abc">>), + + <<1,2>> = encode_octet_string(<<1,2,3>>, 2), ok. matching_meets_construction_1(<<"A",H/binary>>) -> <<"B",H>>. @@ -781,14 +839,14 @@ encode_octet_string(<<OctetString/binary>>, Len) -> <<OctetString:Len/binary-unit:8>>. simon(Config) when is_list(Config) -> - ?line one = simon(blurf, <<>>), - ?line two = simon(0, <<42>>), - ?line fc(simon, [17,<<1>>], catch simon(17, <<1>>)), - ?line fc(simon, [0,<<1,2,3>>], catch simon(0, <<1,2,3>>)), - - ?line one = simon2(blurf, <<9>>), - ?line two = simon2(0, <<9,1>>), - ?line fc(simon2, [0,<<9,10,11>>], catch simon2(0, <<9,10,11>>)), + one = simon(blurf, <<>>), + two = simon(0, <<42>>), + fc(simon, [17,<<1>>], catch simon(17, <<1>>)), + fc(simon, [0,<<1,2,3>>], catch simon(0, <<1,2,3>>)), + + one = simon2(blurf, <<9>>), + two = simon2(0, <<9,1>>), + fc(simon2, [0,<<9,10,11>>], catch simon2(0, <<9,10,11>>)), ok. simon(_, <<>>) -> one; @@ -800,10 +858,10 @@ simon2(0, <<_:16>>) -> two. %% OTP-7113: Crash in v3_codegen. matching_and_andalso(Config) when is_list(Config) -> - ?line ok = matching_and_andalso_1(<<1,2,3>>, 3), - ?line {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, -8)), - ?line {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, blurf)), - ?line {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, 19)), + ok = matching_and_andalso_1(<<1,2,3>>, 3), + {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, -8)), + {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, blurf)), + {'EXIT',{function_clause,_}} = (catch matching_and_andalso_1(<<1,2,3>>, 19)), {"abc",<<"xyz">>} = matching_and_andalso_2("abc", <<"-xyz">>), {"abc",<<"">>} = matching_and_andalso_2("abc", <<($a-1)>>), @@ -836,7 +894,7 @@ otp_7188(Config) when is_list(Config) -> 0,0,0,0,0,0,50,48,48,48,50,48,48,48,32,45,32,66,101,115, 116,32,79,102,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,12>>, - ?line {ok,{"ID3v1", + {ok,{"ID3v1", [{title,<<68,117,154,105,232,107,121>>}, {artist,<<"Daniel Landa">>}, {album,<<"Best Of">>}]}} = parse_v1_or_v11_tag(MP3). @@ -880,11 +938,11 @@ skip_blanks_and_zero(L) -> -record(rec_otp_7233, {key, val}). otp_7233(Config) when is_list(Config) -> - ?line otp_7233_1(#rec_otp_7233{key = <<"XXabcde">>,val=[{"xxxxxxxx",42}]}), - ?line [<<"XXabcde">>,{"xxxxxxxx",42}] = get(io_format), + otp_7233_1(#rec_otp_7233{key = <<"XXabcde">>,val=[{"xxxxxxxx",42}]}), + [<<"XXabcde">>,{"xxxxxxxx",42}] = get(io_format), erase(io_format), - ?line otp_7233_1(#rec_otp_7233{key = <<"XXabcde">>,val=[]}), - ?line undefined = get(io_format), + otp_7233_1(#rec_otp_7233{key = <<"XXabcde">>,val=[]}), + undefined = get(io_format), ok. otp_7233_1(Rec) -> @@ -892,32 +950,34 @@ otp_7233_1(Rec) -> case K of <<"XX">> -> Value = Rec#rec_otp_7233.val, - case lists:keysearch("xxxxxxxx", 1, Value) of - {value,T} -> put(io_format, [Rec#rec_otp_7233.key,T]); - false -> ok + case lists:keyfind("xxxxxxxx", 1, Value) of + false -> + ok; + T -> + put(io_format, [Rec#rec_otp_7233.key,T]) end; _ -> ok end. otp_7240(Config) when is_list(Config) -> - ?line a = otp_7240_a(0, <<>>), - ?line b = otp_7240_a(1, 2), + a = otp_7240_a(0, <<>>), + b = otp_7240_a(1, 2), - ?line a = otp_7240_b(anything, <<>>), - ?line b = otp_7240_b(1, {x,y}), + a = otp_7240_b(anything, <<>>), + b = otp_7240_b(1, {x,y}), - ?line a = otp_7240_c(anything, <<>>), - ?line b = otp_7240_c(1, <<2>>), + a = otp_7240_c(anything, <<>>), + b = otp_7240_c(1, <<2>>), - ?line a = otp_7240_d(anything, <<>>), - ?line b = otp_7240_d(again, <<2>>), + a = otp_7240_d(anything, <<>>), + b = otp_7240_d(again, <<2>>), - ?line a = otp_7240_e(anything, <<>>), - ?line b = otp_7240_e(1, 41), + a = otp_7240_e(anything, <<>>), + b = otp_7240_e(1, 41), - ?line a = otp_7240_f(anything, <<>>), - ?line b = otp_7240_f(1, {}), + a = otp_7240_f(anything, <<>>), + b = otp_7240_f(1, {}), ok. @@ -940,15 +1000,15 @@ otp_7240_f(_, <<>>) -> a; otp_7240_f(1, B) when is_tuple(B) -> b. otp_7498(Config) when is_list(Config) -> - ?line <<1,2,3>> = otp_7498_foo(<<1,2,3>>, 0), - ?line <<2,3>> = otp_7498_foo(<<1,2,3>>, 1), - ?line <<1,2,3>> = otp_7498_foo(<<1,2,3>>, 2), + <<1,2,3>> = otp_7498_foo(<<1,2,3>>, 0), + <<2,3>> = otp_7498_foo(<<1,2,3>>, 1), + <<1,2,3>> = otp_7498_foo(<<1,2,3>>, 2), - ?line <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 0), - ?line <<2,3>> = otp_7498_bar(<<1,2,3>>, 1), - ?line <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 2), - ?line <<>> = otp_7498_bar(<<>>, 2), - ?line <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 3), + <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 0), + <<2,3>> = otp_7498_bar(<<1,2,3>>, 1), + <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 2), + <<>> = otp_7498_bar(<<>>, 2), + <<1,2,3>> = otp_7498_bar(<<1,2,3>>, 3), ok. @@ -977,19 +1037,19 @@ match_string(Config) when is_list(Config) -> %% check the coverage for the v3_kernel module. case erlang:system_info(endian) of little -> - ?line do_match_string_native(<<$a,0,$b,0>>); + do_match_string_native(<<$a,0,$b,0>>); big -> - ?line do_match_string_native(<<0,$a,0,$b>>) + do_match_string_native(<<0,$a,0,$b>>) end, - ?line do_match_string_big(<<0,$a,0,$b>>), - ?line do_match_string_little(<<$a,0,$b,0>>), + do_match_string_big(<<0,$a,0,$b>>), + do_match_string_little(<<$a,0,$b,0>>), - ?line do_match_string_big_signed(<<255,255>>), - ?line do_match_string_little_signed(<<255,255>>), + do_match_string_big_signed(<<255,255>>), + do_match_string_little_signed(<<255,255>>), - ?line plain = no_match_string_opt(<<"abc">>), - ?line strange = no_match_string_opt(<<$a:9,$b:9,$c:9>>), + plain = no_match_string_opt(<<"abc">>), + strange = no_match_string_opt(<<$a:9,$b:9,$c:9>>), ok. @@ -1010,13 +1070,13 @@ no_match_string_opt(<<$a:9,$b:9,$c:9>>) -> strange. %% OTP-7591: A zero-width segment in matching would crash the compiler. zero_width(Config) when is_list(Config) -> - ?line <<Len:16/little, Str:Len/binary, 0:0>> = <<2, 0, $h, $i, 0:0>>, - ?line 2 = Len, - ?line Str = <<"hi">>, + <<Len:16/little, Str:Len/binary, 0:0>> = <<2, 0, $h, $i, 0:0>>, + 2 = Len, + Str = <<"hi">>, %% Match sure that values that cannot fit in a segment will not match. case id(<<0:8>>) of - <<256:8>> -> ?line ?t:fail(); + <<256:8>> -> ct:fail(should_not_match); _ -> ok end, ok. @@ -1025,14 +1085,14 @@ zero_width(Config) when is_list(Config) -> %% OTP_7650: A invalid size for binary segments could crash the compiler. bad_size(Config) when is_list(Config) -> Tuple = {a,b,c}, - ?line {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Tuple>> = id(<<>>)), + {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Tuple>> = id(<<>>)), Binary = <<1,2,3>>, - ?line {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Binary>> = id(<<>>)), + {'EXIT',{{badmatch,<<>>},_}} = (catch <<32:Binary>> = id(<<>>)), ok. haystack(Config) when is_list(Config) -> - ?line <<0:10/unit:8>> = haystack_1(<<0:10/unit:8>>), - ?line [<<0:10/unit:8>>, + <<0:10/unit:8>> = haystack_1(<<0:10/unit:8>>), + [<<0:10/unit:8>>, <<0:20/unit:8>>] = haystack_2(<<1:8192>>), ok. @@ -1067,10 +1127,10 @@ fc(_, Args, {'EXIT',{{case_clause,ActualArgs},_}}) %% Cover the clause handling bs_context to binary in %% beam_block:initialized_regs/2. cover_beam_bool(Config) when is_list(Config) -> - ?line ok = do_cover_beam_bool(<<>>, 3), - ?line <<19>> = do_cover_beam_bool(<<19>>, 2), - ?line <<42>> = do_cover_beam_bool(<<42>>, 1), - ?line <<17>> = do_cover_beam_bool(<<13,17>>, 0), + ok = do_cover_beam_bool(<<>>, 3), + <<19>> = do_cover_beam_bool(<<19>>, 2), + <<42>> = do_cover_beam_bool(<<42>>, 1), + <<17>> = do_cover_beam_bool(<<13,17>>, 0), ok. do_cover_beam_bool(Bin, X) when X > 0 -> @@ -1208,6 +1268,189 @@ match_binary_in_map(Map) -> ok end. +match_string_opt(Config) when is_list(Config) -> + {x,<<1,2,3>>,{<<1>>,{v,<<1,2,3>>}}} = + do_match_string_opt({<<1>>,{v,<<1,2,3>>}}), + ok. + +do_match_string_opt({<<1>>,{v,V}}=T) -> + {x,V,T}. + +select_on_integer(Config) when is_list(Config) -> + 42 = do_select_on_integer(<<42>>), + <<"abc">> = do_select_on_integer(<<128,"abc">>), + + {'EXIT',_} = (catch do_select_on_integer(<<0:1>>)), + {'EXIT',_} = (catch do_select_on_integer(<<1:1>>)), + {'EXIT',_} = (catch do_select_on_integer(<<0:1,0:15>>)), + ok. + +%% The ASN.1 compiler frequently generates code like this. +do_select_on_integer(<<0:1,I:7>>) -> + I; +do_select_on_integer(<<1:1,_:7,Bin/binary>>) -> + Bin. + +%% If 'bin_opt_info' was given the warning would lack filename +%% and line number. + +map_and_binary(_Config) -> + {<<"10">>,<<"37">>,<<"am">>} = do_map_and_binary(<<"10:37am">>), + Map1 = #{time => "noon"}, + {ok,Map1} = do_map_and_binary(Map1), + Map2 = #{hour => 8, min => 42}, + {8,42,Map2} = do_map_and_binary(Map2), + ok. + +do_map_and_binary(<<Hour:2/bytes, $:, Min:2/bytes, Rest/binary>>) -> + {Hour, Min, Rest}; +do_map_and_binary(#{time := _} = T) -> + {ok, T}; +do_map_and_binary(#{hour := Hour, min := Min} = T) -> + {Hour, Min, T}. + +%% Unsafe caching of branch outcomes in beam_bsm would cause the +%% delayed creation of sub-binaries optimization to be applied even +%% when it was unsafe. + +unsafe_branch_caching(_Config) -> + <<>> = do_unsafe_branch_caching(<<42,1>>), + <<>> = do_unsafe_branch_caching(<<42,2>>), + <<>> = do_unsafe_branch_caching(<<42,3>>), + <<17,18>> = do_unsafe_branch_caching(<<42,3,17,18>>), + <<>> = do_unsafe_branch_caching(<<1,3,42,2>>), + + ok. + +do_unsafe_branch_caching(<<Code/integer, Bin/binary>>) -> + <<C1/integer, B1/binary>> = Bin, + case C1 of + X when X =:= 1 orelse X =:= 2 -> + Bin2 = <<>>; + _ -> + Bin2 = B1 + end, + case Code of + 1 -> do_unsafe_branch_caching(Bin2); + _ -> Bin2 + end. + +bad_literals(_Config) -> + Mod = list_to_atom(?MODULE_STRING ++ "_" ++ + atom_to_list(?FUNCTION_NAME)), + S = [signed_lit_match(V, Sz) || V <- lists:seq(-8, 8), + Sz <- [0,1,2,3]] ++ + [unsigned_lit_match(V, Sz) || V <- lists:seq(-2, 8), + Sz <- [0,1,2]] ++ + [unicode_match(V) || + V <- [-100,-1,0,1,2|lists:seq(16#10FFFC, 16#110004)]], + Code = ?Q(["-module('@Mod@').\n" + "-export([f/0]).\n" + "f() ->\n" + "_@S,\n" + "ok.\n"]), + merl:print(Code), + Opts = test_lib:opt_opts(?MODULE), + {ok,_} = merl:compile_and_load(Code, Opts), + Mod:f(), + + {'EXIT',<<42>>} = (catch bad_literals_1()), + + Sz = id(8), + {'EXIT',{{badmatch,_},_}} = (catch <<-1:Sz>> = <<-1>>), + ok. + +bad_literals_1() -> + BadSz = bad, + case case <<42>> of + <<42:BadSz>> -> ok; + Val -> exit(Val) + end of + ok -> ok; + error -> error + end. + +signed_lit_match(V, Sz) -> + case <<V:Sz>> of + <<V:Sz/signed>> -> + ?Q("<<_@V@:_@Sz@/signed>> = <<_@V@:_@Sz@>>"); + _ -> + ?Q(["case <<_@V@:_@Sz@>> of\n", + " <<_@V@:_@Sz@/signed>> ->\n", + " ct:fail(should_not_match);\n", + " _ ->\n", + " ok\n", + "end\n"]) + end. + +unsigned_lit_match(V, Sz) -> + case <<V:Sz>> of + <<V:Sz/unsigned>> -> + ?Q("<<_@V@:_@Sz@>> = <<_@V@:_@Sz@>>"); + _ -> + ?Q(["case <<_@V@:_@Sz@>> of\n", + " <<_@V@:_@Sz@/unsigned>> ->\n", + " ct:fail(should_not_match);\n", + " _ ->\n", + " ok\n", + "end\n"]) + end. + +unicode_match(V) -> + try <<V/utf8>> of + <<V/utf8>> -> + ?Q(["<<_@V@/utf8>> = <<_@V@/utf8>>,\n", + "<<_@V@/utf16>> = <<_@V@/utf16>>,\n", + "<<_@V@/utf32>> = <<_@V@/utf32>>\n"]) + catch + error:badarg -> + ?Q(["case <<_@V@:32>> of\n", + " <<_@V@/utf32>> ->\n", + " ct:fail(should_not_match);\n", + " _ ->\n", + " ok\n", + "end\n"]) + end. + +%% Test a few legal but rare cases. + +good_literals(_Config) -> + Sz = id(64), + + %% Variable size. + <<42:Sz>> = id(<<42:Sz>>), + <<42.0:Sz/float>> = id(<<42:Sz/float>>), + + %% unit > 1 + <<16#cafebeef:4/unit:8>> = id(<<16#cafebeef:32>>), + ok. + +constant_propagation(_Config) -> + <<5>> = constant_propagation_a(a, <<5>>), + {'EXIT',{{case_clause,b},_}} = (catch constant_propagation_a(b, <<5>>)), + 258 = constant_propagation_b(<<1,2>>), + F = constant_propagation_c(), + 259 = F(<<1,3>>), + ok. + +constant_propagation_a(X, Y) -> + case X of + a -> Y2 = 8 + end, + <<5:Y2>> = Y. + +constant_propagation_b(B) -> + Sz = 16, + <<X:Sz/integer>> = B, + X. + +constant_propagation_c() -> + Size = 16, + fun(Bin) -> + <<X:Size/integer>> = Bin, + X + end. + check(F, R) -> R = F(). diff --git a/lib/compiler/test/bs_shadowed_size_var.core b/lib/compiler/test/bs_shadowed_size_var.core deleted file mode 100644 index d1d5ebba6d..0000000000 --- a/lib/compiler/test/bs_shadowed_size_var.core +++ /dev/null @@ -1,25 +0,0 @@ -module 'bs_shadowed_size_var' ['filter_essentials'/1] - attributes [] - -%% Reduced code from beam_asm inlined using the old inliner. - -'filter_essentials'/1 = - fun (_cor0) -> - case _cor0 of - <[#{#<Sz>(32,1,'integer',['unsigned','big']) }#|T]> when 'true' -> - let <_cor4> = - case T of - %% Variable 'Sz' repeated here. Should work. - <#{#<Sz>(32,1,'integer',['unsigned','big']), - #<Data>(Sz,8,'binary',['unsigned','big'])}#> when 'true' -> - Data - <_cor5> when 'true' -> - primop 'match_fail' - ({'case_clause',{_cor5}}) - end - in [Sz|_cor4] - <_cor5> when 'true' -> - primop 'match_fail' - ({'function_clause',_cor5}) - end -end diff --git a/lib/compiler/test/bs_utf_SUITE.erl b/lib/compiler/test/bs_utf_SUITE.erl index 94549ad0d3..c894041f72 100644 --- a/lib/compiler/test/bs_utf_SUITE.erl +++ b/lib/compiler/test/bs_utf_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -25,7 +26,7 @@ utf32_roundtrip/1,guard/1,extreme_tripping/1, literals/1,coverage/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -52,14 +53,14 @@ end_per_group(_GroupName, Config) -> utf8_roundtrip(Config) when is_list(Config) -> - ?line [utf8_roundtrip_1(P) || P <- utf_data()], + [utf8_roundtrip_1(P) || P <- utf_data()], ok. utf8_roundtrip_1({Str,Bin,Bin}) -> - ?line Str = utf8_to_list(Bin), - ?line Bin = list_to_utf8(Str), - ?line [ok = utf8_guard(C, <<42,C/utf8>>) || C <- Str], - ?line [error = utf8_guard(C, <<C/utf8>>) || C <- Str], + Str = utf8_to_list(Bin), + Bin = list_to_utf8(Str), + [ok = utf8_guard(C, <<42,C/utf8>>) || C <- Str], + [error = utf8_guard(C, <<C/utf8>>) || C <- Str], ok. utf8_guard(C, Bin) when <<42,C/utf8>> =:= Bin -> ok; @@ -89,14 +90,14 @@ utf8_len(<<_/utf8,T/binary>>, N) -> utf8_len(<<>>, N) -> N. utf16_roundtrip(Config) when is_list(Config) -> - ?line {Str,Big,Big,Little,Little} = utf16_data(), - ?line 4 = utf16_big_len(Big), - ?line 4 = utf16_little_len(Little), - ?line Str = big_utf16_to_list(Big), - ?line Str = little_utf16_to_list(Little), + {Str,Big,Big,Little,Little} = utf16_data(), + 4 = utf16_big_len(Big), + 4 = utf16_little_len(Little), + Str = big_utf16_to_list(Big), + Str = little_utf16_to_list(Little), - ?line Big = list_to_big_utf16(Str), - ?line Little = list_to_little_utf16(Str), + Big = list_to_big_utf16(Str), + Little = list_to_little_utf16(Str), ok. @@ -137,14 +138,14 @@ little_utf16_to_list(<<H/little-utf16,T/binary>>) -> little_utf16_to_list(<<>>) -> []. utf32_roundtrip(Config) when is_list(Config) -> - ?line {Str,Big,Big,Little,Little} = utf32_data(), - ?line 4 = utf32_big_len(Big), - ?line 4 = utf32_little_len(Little), - ?line Str = big_utf32_to_list(Big), - ?line Str = little_utf32_to_list(Little), + {Str,Big,Big,Little,Little} = utf32_data(), + 4 = utf32_big_len(Big), + 4 = utf32_little_len(Little), + Str = big_utf32_to_list(Big), + Str = little_utf32_to_list(Little), - ?line Big = list_to_big_utf32(Str), - ?line Little = list_to_little_utf32(Str), + Big = list_to_big_utf32(Str), + Little = list_to_little_utf32(Str), ok. @@ -186,7 +187,7 @@ little_utf32_to_list(<<>>) -> []. guard(Config) when is_list(Config) -> - ?line error = do_guard(16#D800), + error = do_guard(16#D800), ok. do_guard(C) when byte_size(<<C/utf8>>) =/= 42 -> ok; @@ -198,13 +199,13 @@ do_guard(_) -> error. %% the delayed creation of sub-binaries works. extreme_tripping(Config) when is_list(Config) -> - ?line Unicode = lists:seq(0, 1024), - ?line Utf8 = unicode_to_utf8(Unicode, <<>>), - ?line Utf16 = utf8_to_utf16(Utf8, <<>>), - ?line Utf32 = utf8_to_utf32(Utf8, <<>>), - ?line Utf32 = utf16_to_utf32(Utf16, <<>>), - ?line Utf8 = utf32_to_utf8(Utf32, <<>>), - ?line Unicode = utf32_to_unicode(Utf32), + Unicode = lists:seq(0, 1024), + Utf8 = unicode_to_utf8(Unicode, <<>>), + Utf16 = utf8_to_utf16(Utf8, <<>>), + Utf32 = utf8_to_utf32(Utf8, <<>>), + Utf32 = utf16_to_utf32(Utf16, <<>>), + Utf8 = utf32_to_utf8(Utf32, <<>>), + Unicode = utf32_to_unicode(Utf32), ok. unicode_to_utf8([C|T], Bin) -> @@ -232,58 +233,58 @@ utf32_to_unicode(<<C/utf32,T/binary>>) -> utf32_to_unicode(<<>>) -> []. literals(Config) when is_list(Config) -> - ?line abc_utf8 = match_literal(<<"abc"/utf8>>), - ?line abc_utf8 = match_literal(<<$a,$b,$c>>), - - ?line abc_utf16be = match_literal(<<"abc"/utf16>>), - ?line abc_utf16be = match_literal(<<$a:16,$b:16,$c:16>>), - ?line abc_utf16le = match_literal(<<"abc"/little-utf16>>), - ?line abc_utf16le = match_literal(<<$a:16/little,$b:16/little,$c:16/little>>), - - ?line abc_utf32be = match_literal(<<"abc"/utf32>>), - ?line abc_utf32be = match_literal(<<$a:32,$b:32,$c:32>>), - ?line abc_utf32le = match_literal(<<"abc"/little-utf32>>), - ?line abc_utf32le = match_literal(<<$a:32/little,$b:32/little,$c:32/little>>), - - ?line bjorn_utf8 = match_literal(<<"bj\366rn"/utf8>>), - ?line bjorn_utf8 = match_literal(<<$b,$j,195,182,$r,$n>>), - - ?line bjorn_utf16be = match_literal(<<"bj\366rn"/utf16>>), - ?line bjorn_utf16be = match_literal(<<$b:16,$j:16,246:16,$r:16,$n:16>>), - ?line bjorn_utf16le = match_literal(<<"bj\366rn"/little-utf16>>), - ?line bjorn_utf16le = match_literal(<<$b:16/little,$j:16/little, + abc_utf8 = match_literal(<<"abc"/utf8>>), + abc_utf8 = match_literal(<<$a,$b,$c>>), + + abc_utf16be = match_literal(<<"abc"/utf16>>), + abc_utf16be = match_literal(<<$a:16,$b:16,$c:16>>), + abc_utf16le = match_literal(<<"abc"/little-utf16>>), + abc_utf16le = match_literal(<<$a:16/little,$b:16/little,$c:16/little>>), + + abc_utf32be = match_literal(<<"abc"/utf32>>), + abc_utf32be = match_literal(<<$a:32,$b:32,$c:32>>), + abc_utf32le = match_literal(<<"abc"/little-utf32>>), + abc_utf32le = match_literal(<<$a:32/little,$b:32/little,$c:32/little>>), + + bjorn_utf8 = match_literal(<<"bj\366rn"/utf8>>), + bjorn_utf8 = match_literal(<<$b,$j,195,182,$r,$n>>), + + bjorn_utf16be = match_literal(<<"bj\366rn"/utf16>>), + bjorn_utf16be = match_literal(<<$b:16,$j:16,246:16,$r:16,$n:16>>), + bjorn_utf16le = match_literal(<<"bj\366rn"/little-utf16>>), + bjorn_utf16le = match_literal(<<$b:16/little,$j:16/little, 246:16/little,$r:16/little, $n:16/little>>), - ?line <<244,143,191,191>> = <<16#10ffff/utf8>>, + <<244,143,191,191>> = <<16#10ffff/utf8>>, %% Invalid literals. I = 0, - ?line {'EXIT',{badarg,_}} = (catch <<(-1)/utf8,I/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<(-1)/utf16,I/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<(-1)/little-utf16,I/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<(-1)/utf32,I/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<(-1)/little-utf32,I/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<16#D800/utf8,I/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<16#D800/utf16,I/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<16#D800/little-utf16,I/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<16#D800/utf32,I/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<16#D800/little-utf32,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<(-1)/utf8,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<(-1)/utf16,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<(-1)/little-utf16,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<(-1)/utf32,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<(-1)/little-utf32,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<16#D800/utf8,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<16#D800/utf16,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<16#D800/little-utf16,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<16#D800/utf32,I/utf8>>), + {'EXIT',{badarg,_}} = (catch <<16#D800/little-utf32,I/utf8>>), B = 16#10FFFF+1, - ?line {'EXIT',{badarg,_}} = (catch <<B/utf8>>), - ?line {'EXIT',{badarg,_}} = (catch <<B/utf16>>), - ?line {'EXIT',{badarg,_}} = (catch <<B/little-utf16>>), - ?line {'EXIT',{badarg,_}} = (catch <<B/utf32>>), - ?line {'EXIT',{badarg,_}} = (catch <<B/little-utf32>>), + {'EXIT',{badarg,_}} = (catch <<B/utf8>>), + {'EXIT',{badarg,_}} = (catch <<B/utf16>>), + {'EXIT',{badarg,_}} = (catch <<B/little-utf16>>), + {'EXIT',{badarg,_}} = (catch <<B/utf32>>), + {'EXIT',{badarg,_}} = (catch <<B/little-utf32>>), %% Matching of bad literals. - ?line error = bad_literal_match(<<237,160,128>>), %16#D800 in UTF-8 - ?line error = bad_literal_match(<<244,144,128,128>>), %16#110000 in UTF-8 + error = bad_literal_match(<<237,160,128>>), %16#D800 in UTF-8 + error = bad_literal_match(<<244,144,128,128>>), %16#110000 in UTF-8 - ?line error = bad_literal_match(<<16#D800:32>>), - ?line error = bad_literal_match(<<16#110000:32>>), - ?line error = bad_literal_match(<<16#D800:32/little>>), - ?line error = bad_literal_match(<<16#110000:32/little>>), + error = bad_literal_match(<<16#D800:32>>), + error = bad_literal_match(<<16#110000:32>>), + error = bad_literal_match(<<16#D800:32/little>>), + error = bad_literal_match(<<16#110000:32/little>>), ok. @@ -306,13 +307,13 @@ bad_literal_match(_) -> error. coverage(Config) when is_list(Config) -> %% Cover bit syntax matching optimizations in v3_kernel. - ?line 0 = coverage_1(<<4096/utf8,65536/utf8,0>>), - ?line 1 = coverage_1(<<4096/utf8,65536/utf8,1>>), + 0 = coverage_1(<<4096/utf8,65536/utf8,0>>), + 1 = coverage_1(<<4096/utf8,65536/utf8,1>>), - ?line 0 = coverage_2(<<4096/utf8,65536/utf8,0>>), - ?line 1 = coverage_2(<<1024/utf8,1025/utf8,1>>), + 0 = coverage_2(<<4096/utf8,65536/utf8,0>>), + 1 = coverage_2(<<1024/utf8,1025/utf8,1>>), - ?line fc(catch coverage_3(1)), + fc(catch coverage_3(1)), %% Cover beam_flatten (combining the heap allocation in %% a subsequent test_heap instruction into the bs_init2 diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl index f7b1dbdddf..cd1bc099e9 100644 --- a/lib/compiler/test/compilation_SUITE.erl +++ b/lib/compiler/test/compilation_SUITE.erl @@ -1,30 +1,69 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% -%% %%% Purpose : Compiles various modules with tough code -module(compilation_SUITE). - --include_lib("test_server/include/test_server.hrl"). - --compile(export_all). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, + init_per_group/2,end_per_group/2, + beam_compiler_4/1, + beam_compiler_6/1, + beam_compiler_7/1, + beam_compiler_8/1, + beam_compiler_9/1, + beam_compiler_10/1, + beam_compiler_11/1, + compiler_1/1, + const_list_256/1, + convopts/1, + live_var/1, + on_load/1, + on_load_inline/1, + opt_crash/1, + otp_2330/1, + otp_2380/1, + otp_4790/1, + otp_5151/1, + otp_5235/1, + otp_5404/1, + otp_5436/1, + otp_5481/1, + otp_5553/1, + otp_5632/1, + otp_5714/1, + otp_5872/1, + otp_6121/1, + otp_7202/1, + otp_8949_a/1, + redundant_case/1, + self_compile/1, + self_compile_old_inliner/1, + split_cases/1, + string_table/1, + vsn_1/1, + vsn_2/1, + vsn_3/1]). + +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,10}}]. all() -> test_lib:recompile(?MODULE), @@ -35,23 +74,18 @@ groups() -> [{vsn,[parallel],[vsn_1,vsn_2,vsn_3]}, {p,test_lib:parallel(), [compiler_1, - compiler_3,compiler_5,beam_compiler_1, - beam_compiler_2,beam_compiler_3,beam_compiler_4, - beam_compiler_5,beam_compiler_6,beam_compiler_7, + beam_compiler_4,beam_compiler_6,beam_compiler_7, beam_compiler_8,beam_compiler_9,beam_compiler_10, - beam_compiler_11,beam_compiler_12, - nested_tuples_in_case_expr,otp_2330,guards, - {group,vsn},otp_2380,otp_2141,otp_2173,otp_4790, - const_list_256,bin_syntax_1,bin_syntax_2, - bin_syntax_3,bin_syntax_4,bin_syntax_5,bin_syntax_6, - live_var,convopts, - catch_in_catch,redundant_case,long_string,otp_5076, - complex_guard,otp_5092,otp_5151,otp_5235,otp_5244, - trycatch_4,opt_crash,otp_5404,otp_5436,otp_5481, + beam_compiler_11, + otp_2330, + {group,vsn},otp_2380,otp_4790, + const_list_256,live_var,convopts, + redundant_case, + otp_5151,otp_5235, + opt_crash,otp_5404,otp_5436,otp_5481, otp_5553,otp_5632,otp_5714,otp_5872,otp_6121, - otp_6121a,otp_6121b,otp_7202,otp_7345,on_load, - string_table,otp_8949_a,otp_8949_b,split_cases, - beam_utils_liveopt]}]. + otp_7202,on_load,on_load_inline, + string_table,otp_8949_a,split_cases]}]. init_per_suite(Config) -> Config. @@ -68,85 +102,25 @@ end_per_group(_GroupName, Config) -> -define(comp(N), N(Config) when is_list(Config) -> try_it(N, Config)). --define(comp_fail(N), - N(Config) when is_list(Config) -> failure(N, Config)). - ?comp(compiler_1). -?comp(compiler_3). -?comp(compiler_4). -?comp(compiler_5). -?comp(beam_compiler_1). -?comp(beam_compiler_2). -?comp(beam_compiler_3). ?comp(beam_compiler_4). -?comp(beam_compiler_5). ?comp(beam_compiler_6). ?comp(beam_compiler_8). ?comp(beam_compiler_9). ?comp(beam_compiler_10). ?comp(beam_compiler_11). -?comp(beam_compiler_12). -?comp(beam_compiler_13). - -?comp(nested_tuples_in_case_expr). ?comp(otp_2330). ?comp(otp_2380). -?comp(otp_2141). -?comp(otp_2173). ?comp(otp_4790). ?comp(otp_5235). -?comp(otp_5244). - -?comp(guards). - -?comp(pattern_expr). - ?comp(const_list_256). -?comp(bin_syntax_1). -?comp(bin_syntax_2). -?comp(bin_syntax_3). -?comp(bin_syntax_4). - -?comp(bin_syntax_6). - -?comp(otp_5076). - -?comp(complex_guard). - -?comp(otp_5092). ?comp(otp_5151). -%%% By Per Gustafsson <[email protected]> - -bin_syntax_5(Config) when is_list(Config) -> - {<<45>>,<<>>} = split({int, 1}, <<1:16,45>>). - -split({int, N}, <<N:16,B:N/binary,T/binary>>) -> - {B,T}. - -%% This program works with the old version of the compiler -%% but, the core erlang that it produces have the same variable appearing -%% looks like this: -%% -%% split({int, N}, <<_core1:16, B:N/binary, T/binary>>) when _core1==N -%% -%% with my change it will look like this: -%% -%% split({int, N}, <<_core1:16, B:_core1/binary, T/binary>>) when _core1==N -%% -%% This means that everything worked fine as long as the pattern -%% matching order was left-to-right but on core erlang any order should be possible - ?comp(live_var). - -?comp(trycatch_4). - -?comp(catch_in_catch). - ?comp(opt_crash). ?comp(otp_5404). @@ -157,18 +131,14 @@ split({int, N}, <<N:16,B:N/binary,T/binary>>) -> ?comp(otp_5714). ?comp(otp_5872). ?comp(otp_6121). -?comp(otp_6121a). -?comp(otp_6121b). ?comp(convopts). ?comp(otp_7202). ?comp(on_load). ?comp(on_load_inline). -beam_compiler_7(doc) -> - "Code snippet submitted from Ulf Wiger which fails in R3 Beam."; -beam_compiler_7(suite) -> []; +%% Code snippet submitted from Ulf Wiger which fails in R3 Beam. beam_compiler_7(Config) when is_list(Config) -> - ?line done = empty(2, false). + done = empty(2, false). empty(N, Toggle) when N > 0 -> %% R3 Beam copies the second argument to the first before call. @@ -193,124 +163,66 @@ redundant_case_1(3) -> d; redundant_case_1(4) -> d; redundant_case_1(_) -> d. -failure(Module, Conf) -> - ?line Src = filename:join(?config(data_dir, Conf), atom_to_list(Module)), - ?line Out = ?config(priv_dir,Conf), - ?line io:format("Compiling: ~ts\n", [Src]), - ?line CompRc = compile:file(Src, [{outdir,Out},return,time]), - ?line io:format("Result: ~p\n",[CompRc]), - ?line case CompRc of - error -> ok; - {error,Errors,_} -> check_errors(Errors); - _ -> test_server:fail({no_error, CompRc}) - end, - ok. - -check_errors([{_,Eds}|T]) -> - check_error(Eds), - check_errors(T); -check_errors([]) -> ok. - -check_error([{_,Mod,Error}|T]) -> - check_error_1(Mod:format_error(Error)), - check_error(T); -check_error([{Mod,Error}|T]) -> - check_error_1(Mod:format_error(Error)), - check_error(T); -check_error([]) -> ok. - -check_error_1(Str0) -> - Str = lists:flatten(Str0), - io:format("~s\n", [Str]), - case Str of - "internal"++_=Str -> - ?t:fail(internal_compiler_error); - _ -> - ok - end. - --define(TC(Body), tc(fun() -> Body end, ?LINE)). - try_it(Module, Conf) -> - %% Change 'false' to 'true' to start a new node for every module. - try_it(false, Module, Conf). - -try_it(StartNode, Module, Conf) -> - ?line OtherOpts = [], %Can be changed to [time] if needed - ?line Src = filename:join(?config(data_dir, Conf), atom_to_list(Module)), - ?line Out = ?config(priv_dir,Conf), - ?line io:format("Compiling: ~s\n", [Src]), - ?line CompRc0 = compile:file(Src, [clint,{outdir,Out},report, - bin_opt_info|OtherOpts]), - ?line io:format("Result: ~p\n",[CompRc0]), - ?line {ok,_Mod} = CompRc0, - - ?line Dog = test_server:timetrap(test_server:minutes(10)), - Node = case StartNode of - false -> - node(); - true -> - ?line Pa = "-pa " ++ filename:dirname(code:which(?MODULE)), - ?line {ok,Node0} = start_node(compiler, Pa), - Node0 - end, - - ?line ok = rpc:call(Node, ?MODULE, load_and_call, [Out, Module]), - ?line load_and_call(Out, Module), - ?line test_server:timetrap_cancel(Dog), - - ?line NewDog = test_server:timetrap(test_server:minutes(10)), - ?line io:format("Compiling (without optimization): ~s\n", [Src]), - ?line CompRc1 = compile:file(Src, - [no_copt,no_postopt,{outdir,Out},report|OtherOpts]), - - ?line io:format("Result: ~p\n",[CompRc1]), - ?line {ok,_Mod} = CompRc1, - ?line ok = rpc:call(Node, ?MODULE, load_and_call, [Out, Module]), - ?line test_server:timetrap_cancel(NewDog), - - ?line LastDog = test_server:timetrap(test_server:minutes(10)), - ?line io:format("Compiling (with old inliner): ~s\n", [Src]), - ?line CompRc2 = compile:file(Src, [{outdir,Out},report,bin_opt_info, - {inline,1000}|OtherOpts]), - ?line io:format("Result: ~p\n",[CompRc2]), - ?line {ok,_Mod} = CompRc2, - ?line ok = rpc:call(Node, ?MODULE, load_and_call, [Out, Module]), - ?line test_server:timetrap_cancel(LastDog), - - AsmDog = test_server:timetrap(test_server:minutes(10)), + Timetrap = {minutes,10}, + OtherOpts = [], %Can be changed to [time] if needed + Src = filename:join(proplists:get_value(data_dir, Conf), + atom_to_list(Module)), + Out = proplists:get_value(priv_dir,Conf), + io:format("Compiling: ~s\n", [Src]), + CompRc0 = compile:file(Src, [clint0,clint,{outdir,Out},report, + bin_opt_info|OtherOpts]), + io:format("Result: ~p\n",[CompRc0]), + {ok,_Mod} = CompRc0, + + load_and_call(Out, Module), + + ct:timetrap(Timetrap), + io:format("Compiling (without optimization): ~s\n", [Src]), + CompRc1 = compile:file(Src, + [no_copt,no_postopt, + {outdir,Out},report|OtherOpts]), + + io:format("Result: ~p\n",[CompRc1]), + {ok,_Mod} = CompRc1, + load_and_call(Out, Module), + + ct:timetrap(Timetrap), + io:format("Compiling (with old inliner): ~s\n", [Src]), + CompRc2 = compile:file(Src, [clint, + {outdir,Out},report,bin_opt_info, + {inline,1000}|OtherOpts]), + io:format("Result: ~p\n",[CompRc2]), + {ok,_Mod} = CompRc2, + load_and_call(Out, Module), + + ct:timetrap(Timetrap), io:format("Compiling (from assembly): ~s\n", [Src]), {ok,_} = compile:file(Src, [to_asm,{outdir,Out},report|OtherOpts]), Asm = filename:join(Out, lists:concat([Module, ".S"])), CompRc3 = compile:file(Asm, [from_asm,{outdir,Out},report|OtherOpts]), io:format("Result: ~p\n",[CompRc3]), {ok,_} = CompRc3, - ok = rpc:call(Node, ?MODULE, load_and_call, [Out, Module]), - test_server:timetrap_cancel(AsmDog), + load_and_call(Out, Module), - case StartNode of - false -> ok; - true -> ?line test_server:stop_node(Node) - end, - ?line test_server:timetrap_cancel(LastDog), ok. load_and_call(Out, Module) -> - ?line io:format("Loading...\n",[]), - ?line {module,Module} = code:load_abs(filename:join(Out, Module)), + io:format("Loading...\n",[]), + {module,Module} = code:load_abs(filename:join(Out, Module)), - ?line io:format("Calling...\n",[]), + io:format("Calling...\n",[]), %% Call M:M, and expect ok back, that's our interface - ?line CallRc = Module:Module(), - ?line io:format("Got value: ~p\n",[CallRc]), + CallRc = Module:Module(), + io:format("Got value: ~p\n",[CallRc]), - ?line ok = CallRc, + ok = CallRc, %% Smoke-test of beam disassembler. - ?line test_lib:smoke_disasm(Module), + test_lib:smoke_disasm(Module), - ?line true = erlang:delete_module(Module), - ?line true = erlang:purge_module(Module), + _ = code:delete(Module), + _ = code:purge(Module), %% Restore state of trap_exit just in case. (Since the compiler %% uses a temporary process, we will get {'EXIT',Pid,normal} messages @@ -320,102 +232,73 @@ load_and_call(Out, Module) -> ok. -tc(F, Line) -> - {Diff,Value} = timer:tc(erlang, apply, [F,[]]), - io:format("~p: ~p\n", [Line,Diff]), - Value. - -start_node(Name, Args) -> - case test_server:start_node(Name, slave, [{args, Args}]) of - {ok, Node} -> - {ok, Node}; - Error -> - ?line test_server:fail(Error) - end. +%% Test generation of 'vsn' attribute. +vsn_1(Conf) when is_list(Conf) -> + M = vsn_1, -from(H, [H | T]) -> T; -from(H, [_ | T]) -> from(H, T); -from(_, []) -> []. + compile_load(M, proplists:get_value(data_dir, Conf), Conf), + Vsn1 = get_vsn(M), + timer:sleep(1000), + compile_load(M, proplists:get_value(data_dir, Conf), Conf), + Vsn2 = get_vsn(M), -vsn_1(doc) -> - "Test generation of 'vsn' attribute"; -vsn_1(suite) -> []; -vsn_1(Conf) when is_list(Conf) -> - ?line M = vsn_1, - - ?line compile_load(M, ?config(data_dir, Conf), Conf), - ?line Vsn1 = get_vsn(M), - ?line timer:sleep(1000), - - ?line compile_load(M, ?config(data_dir, Conf), Conf), - ?line Vsn2 = get_vsn(M), - - ?line compile_load(M, filename:join(?config(data_dir, Conf), "other"), - Conf), - ?line Vsn3 = get_vsn(M), - ?line if - Vsn1 == Vsn2, Vsn2 == Vsn3 -> - ok; - true -> - test_server:fail({vsn, Vsn1, Vsn2, Vsn3}) - end, + compile_load(M, filename:join(proplists:get_value(data_dir, Conf), + "other"), + Conf), + Vsn3 = get_vsn(M), + if + Vsn1 == Vsn2, Vsn2 == Vsn3 -> + ok; + true -> + ct:fail({vsn, Vsn1, Vsn2, Vsn3}) + end, ok. -vsn_2(doc) -> - "Test overriding of generation of 'vsn' attribute"; -vsn_2(suite) -> []; +%% Test overriding of generation of 'vsn' attribute. vsn_2(Conf) when is_list(Conf) -> - ?line M = vsn_2, - - ?line compile_load(M, ?config(data_dir, Conf), Conf), - ?line Vsn = get_vsn(M), - ?line case Vsn of - [34] -> - ok; - _ -> - test_server:fail({vsn, Vsn}) - end, + M = vsn_2, + + compile_load(M, proplists:get_value(data_dir, Conf), Conf), + Vsn = get_vsn(M), + case Vsn of + [34] -> + ok; + _ -> + ct:fail({vsn, Vsn}) + end, ok. -vsn_3(doc) -> - "Test that different code yields different generated 'vsn'"; -vsn_3(suite) -> []; +%% Test that different code yields different generated 'vsn'. vsn_3(Conf) when is_list(Conf) -> - ?line M = vsn_3, - - ?line compile_load(M, ?config(data_dir, Conf), Conf), - ?line Vsn1 = get_vsn(M), - - ?line compile_load(M, filename:join(?config(data_dir, Conf), "other"), - Conf), - ?line Vsn2 = get_vsn(M), - ?line if - Vsn1 /= Vsn2 -> - ok; - true -> - test_server:fail({vsn, Vsn1, Vsn2}) - end, + M = vsn_3, + + compile_load(M, proplists:get_value(data_dir, Conf), Conf), + Vsn1 = get_vsn(M), + + compile_load(M, filename:join(proplists:get_value(data_dir, Conf), + "other"), + Conf), + Vsn2 = get_vsn(M), + if + Vsn1 /= Vsn2 -> + ok; + true -> + ct:fail({vsn, Vsn1, Vsn2}) + end, ok. get_vsn(M) -> - {value, {vsn, V}} = lists:keysearch(vsn, 1, M:module_info(attributes)), + {vsn,V} = lists:keyfind(vsn, 1, M:module_info(attributes)), V. -long_string(Config) when is_list(Config) -> - %% The test must complete in one minute - it should be plenty of time. - ?line Dog = test_server:timetrap(test_server:minutes(1)), - ?line try_it(long_string, Config), - ?line test_server:timetrap_cancel(Dog), - ok. - compile_load(Module, Dir, Conf) -> - ?line Src = filename:join(Dir, atom_to_list(Module)), - ?line Out = ?config(priv_dir,Conf), - ?line CompRc = compile:file(Src, [{outdir,Out}]), - ?line {ok, Module} = CompRc, - ?line code:purge(Module), - ?line {module, Module} = + Src = filename:join(Dir, atom_to_list(Module)), + Out = proplists:get_value(priv_dir,Conf), + CompRc = compile:file(Src, [{outdir,Out}]), + {ok, Module} = CompRc, + code:purge(Module), + {module, Module} = code:load_abs(filename:join(Out, atom_to_list(Module))), ok. @@ -428,57 +311,51 @@ self_compile_old_inliner(Config) when is_list(Config) -> self_compile_1(Config, "old", [verbose,{inline,500}]). self_compile_1(Config, Prefix, Opts) -> - ?line Dog = test_server:timetrap(test_server:minutes(40)), + ct:timetrap({minutes,40}), - ?line Priv = ?config(priv_dir,Config), - ?line Version = compiler_version(), + Priv = proplists:get_value(priv_dir,Config), + Version = compiler_version(), %% Compile the compiler. (In this node to get better coverage.) - ?line CompA = make_compiler_dir(Priv, Prefix++"compiler_a"), - ?line VsnA = Version ++ ".0", - ?line compile_compiler(compiler_src(), CompA, VsnA, [clint|Opts]), + CompA = make_compiler_dir(Priv, Prefix++"compiler_a"), + VsnA = Version ++ ".0", + compile_compiler(compiler_src(), CompA, VsnA, [clint0,clint|Opts]), %% Compile the compiler again using the newly compiled compiler. %% (In another node because reloading the compiler would disturb cover.) CompilerB = Prefix++"compiler_b", CompB = make_compiler_dir(Priv, CompilerB), - ?line VsnB = VsnA ++ ".0", + VsnB = VsnA ++ ".0", self_compile_node(CompA, CompB, VsnB, Opts), - %% Compare compiler directories. - ?line compare_compilers(CompA, CompB), - - %% Compile and compare compiler C. - ?line CompilerC = Prefix++"compiler_c", - ?line CompC = make_compiler_dir(Priv, CompilerC), - ?line VsnC = VsnB ++ ".0", - self_compile_node(CompB, CompC, VsnC, Opts), - ?line compare_compilers(CompB, CompC), + %% Compare compiler directories. The compiler directories should + %% be equal (except for beam_asm that contains the compiler version). + compare_compilers(CompA, CompB), - ?line test_server:timetrap_cancel(Dog), ok. self_compile_node(CompilerDir, OutDir, Version, Opts) -> - ?line Dog = test_server:timetrap(test_server:minutes(15)), - ?line Pa = "-pa " ++ filename:dirname(code:which(?MODULE)) ++ + ct:timetrap({minutes,15}), + Pa = "-pa " ++ filename:dirname(code:which(?MODULE)) ++ " -pa " ++ CompilerDir, - ?line Files = compiler_src(), + Files = compiler_src(), %% We don't want the cover server started on the other node, %% because it will load the same cover-compiled code as on this %% node. Use a shielded node to prevent the cover server from %% being started. - ?t:run_on_shielded_node( - fun() -> - compile_compiler(Files, OutDir, Version, Opts) - end, Pa), - ?line test_server:timetrap_cancel(Dog), + test_server:run_on_shielded_node( + fun() -> + compile_compiler(Files, OutDir, Version, Opts) + end, Pa), + ok. compile_compiler(Files, OutDir, Version, InlineOpts) -> io:format("~ts", [code:which(compile)]), io:format("Compiling ~s into ~ts", [Version,OutDir]), Opts = [report, + clint0,clint, bin_opt_info, {outdir,OutDir}, {d,'COMPILER_VSN',"\""++Version++"\""}, @@ -494,149 +371,32 @@ compile_compiler(Files, OutDir, Version, InlineOpts) -> compiler_src() -> filelib:wildcard(filename:join([code:lib_dir(compiler), "src", "*.erl"])). -compiler_modules(Dir) -> - Files = filelib:wildcard(filename:join(Dir, "*.beam")), - [list_to_atom(filename:rootname(filename:basename(F))) || F <- Files]. - make_compiler_dir(Priv, Dir0) -> - ?line Dir = filename:join(Priv, Dir0), - ?line ok = file:make_dir(Dir), + Dir = filename:join(Priv, Dir0), + ok = file:make_dir(Dir), Dir. -make_current(Dir) -> - true = code:add_patha(Dir), - lists:foreach(fun(File) -> - c:l(File) - end, compiler_modules(Dir)), - io:format("~p\n", [code:which(compile)]). - compiler_version() -> - {value,{version,Version}} = lists:keysearch(version, 1, - compile:module_info(compile)), + {version,Version} = lists:keyfind(version, 1, + compile:module_info(compile)), Version. compare_compilers(ADir, BDir) -> {[],[],D} = beam_lib:cmp_dirs(ADir, BDir), - [] = [T || {A,_}=T <- D, - filename:basename(A) =/= "beam_asm.beam"]. %Contains compiler version. - - -%%% -%%% The only test of the following code is that it compiles. -%%% - -%% Slightly simplifed from megaco_binary_term_id_gen. -%% beam_block failed to note that the {gc_bif,'-'...} instruction could -%% fail, and that therefore {y,0} need to be initialized. -%% {allocate,8,6}. -%% %% {init,{y,0}} needed here. -%% {get_list,{x,1},{x,6},{x,7}}. -%% {'catch',{y,7},{f,3}}. -%% {move,{x,4},{y,1}}. -%% {move,{x,3},{y,2}}. -%% {move,{x,2},{y,3}}. -%% {move,{x,5},{y,4}}. -%% {move,{x,7},{y,5}}. -%% {move,{x,6},{y,6}}. -%% {gc_bif,'-',{f,0},8,[{x,3},{x,6}],{x,0}}. -%% {move,{x,0},{y,0}}. - -encode_wildcards3([],[],_,_) -> []; -encode_wildcards3([Level|Levels],[BitsInLevel|BitsRest],LevelNo,TotSize) -> - case (catch ?MODULE:encode_wildcard(Level,BitsInLevel,TotSize-BitsInLevel, - length(Levels))) of - {'EXIT',{Reason,Info}} -> - exit({Reason,{LevelNo,Info}}); - - no_wildcard -> - encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel); - - {level,Wl} -> - [Wl| - encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel)]; - - {recursive,Wr} -> - [Wr] - end. -%% Slightly simplified code from hipe_rtl_ssapre. -%% beam_block used to do the following incorrect optimization: -%% -%% {gc_bif,length,{f,0},1,[{x,0}],{x,3}}. -%% ^^^^^ Was {x,0} - changing to {x,3} is not safe. -%% {gc_bif,'+',{f,0},0,[{y,2},{integer,1}],{x,0}}. -%% ^^^ Only one register live -%% . . . -%% {call_last,4,{f,2},4}. %% beam_validator noted that {x,3} wasn't live. - -find_operands(Cfg,XsiGraph,[],_Count) -> - {Cfg,XsiGraph}; -find_operands(Cfg,XsiGraph,ActiveList,Count) -> - {NewCfg,TempActiveList}=?MODULE:find_operands_for_active_list(Cfg,XsiGraph, - ActiveList,[]), - NewActiveList=lists:reverse(TempActiveList), - [Count+1, length(NewActiveList), length(digraph:vertices(XsiGraph))], - find_operands(NewCfg,XsiGraph,NewActiveList,Count+1). - - -%% The following code -%% -%% {get_list,{x,2},{x,0},{x,1}}. -%% {gc_bif,length,{f,0},1,[{x,0}],{x,0}}. -%% {move,{x,0},{x,1}}. -%% -%% was incorrectly optimized to -%% -%% {get_list,{x,2},{x,0},{y,0}}. -%% {gc_bif,length,{f,0},3,[{x,0}],{x,1}}. -%% -%% because beam_block:is_transparent({x,1}, -%% {gc_bif,length,{f,0},3,[{x,0}],{x,1}} -%% incorrectly returned true. - --record(contextId,{cid,device_type,contextRef}). --record(dpRef,{cid,tlli,ms_device_context_id}). --record(qosProfileBssgp,{peak_bit_rate_msb, - peak_bit_rate_lsb, - t_a_precedence}). --record(llUnitdataReq,{sapi, - l3_pdu_length, - pdu_life}). --record(ptmsi,{value}). - -otp_7345(Config) when is_list(Config) -> - #llUnitdataReq{l3_pdu_length=3,pdu_life=4} = - otp_7345(#contextId{}, 0, [[1,2,3],4,5]). - - -otp_7345(ObjRef, _RdEnv, Args) -> - Cid = ObjRef#contextId.cid, - _DpRef = - #dpRef{cid = Cid, - ms_device_context_id = cid_id, - tlli = #ptmsi{value = 0}}, - _QosProfile = - #qosProfileBssgp{peak_bit_rate_msb = 0, - peak_bit_rate_lsb = 80, - t_a_precedence = 49}, - [Cpdu|_] = Args, - LlUnitdataReq = - #llUnitdataReq{sapi = 7, - l3_pdu_length = length(Cpdu), - pdu_life = - id(42) - div - 10}, - id(LlUnitdataReq). + %% beam_asm.beam contains compiler version and therefore it *must* + %% compare unequal. + ["beam_asm.beam"] = [filename:basename(A) || {A,_} <- D], + ok. %% Check the generation of the string table. string_table(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line File = filename:join(DataDir, "string_table.erl"), - ?line {ok,string_table,Beam,[]} = compile:file(File, [return, binary]), - ?line {ok,{string_table,[StringTableChunk]}} = beam_lib:chunks(Beam, ["StrT"]), - ?line {"StrT", <<"stringtable">>} = StringTableChunk, + DataDir = proplists:get_value(data_dir, Config), + File = filename:join(DataDir, "string_table.erl"), + {ok,string_table,Beam,[]} = compile:file(File, [return, binary]), + {ok,{string_table,[StringTableChunk]}} = beam_lib:chunks(Beam, ["StrT"]), + {"StrT", <<"stringtable">>} = StringTableChunk, ok. otp_8949_a(Config) when is_list(Config) -> @@ -661,24 +421,6 @@ do_otp_8949_a() -> end end. -otp_8949_b(Config) when is_list(Config) -> - self() ! something, - ?line value = otp_8949_b([], false), - ?line {'EXIT',_} = (catch otp_8949_b([], true)), - ok. - -%% Would cause an endless loop in beam_utils. -otp_8949_b(A, B) -> - Var = id(value), - if - A == [], B == false -> - ok - end, - receive - something -> - id(Var) - end. - split_cases(_) -> dummy1 = do_split_cases(x), {'EXIT',{{badmatch,b},_}} = (catch do_split_cases(y)), @@ -694,21 +436,5 @@ do_split_cases(A) -> end, Z. --record(alarmInfo, {type,cause,origin}). - -beam_utils_liveopt(Config) -> - F = beam_utils_liveopt_fun(42, pebkac, user), - void = F(42, #alarmInfo{type=sctp,cause=pebkac,origin=user}), - ok. - -beam_utils_liveopt_fun(Peer, Cause, Origin) -> - fun(PeerNo, AlarmInfo) - when PeerNo == Peer andalso - AlarmInfo == #alarmInfo{type=sctp, - cause=Cause, - origin=Origin} -> - void - end. - id(I) -> I. diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_1.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_1.erl deleted file mode 100644 index d6c9c869c8..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_1.erl +++ /dev/null @@ -1,31 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(beam_compiler_1). --export([beam_compiler_1/0]). - -beam_compiler_1() -> - ok. - --record(foo,{a,b}). - -try_me() -> - X = #foo{}, - Y = #foo{}, - {X#foo.a == Y#foo.a,X#foo.b}. - diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_10.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_10.erl index a1264055bd..66f2f78b32 100644 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_10.erl +++ b/lib/compiler/test/compilation_SUITE_data/beam_compiler_10.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_11.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_11.erl index 4be32ab505..0339b2a488 100644 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_11.erl +++ b/lib/compiler/test/compilation_SUITE_data/beam_compiler_11.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_12.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_12.erl deleted file mode 100644 index baee6b9bce..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_12.erl +++ /dev/null @@ -1,29 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(beam_compiler_12). - --export([?MODULE/0,t/1]). - -?MODULE() -> - ok. - -t(Name) -> - {ok = {file_info,_,regular,_,AccTime1,ModTime1,_,_,_,_,_,_,_,_}} = - prim_file:read_file_info(Name). - diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_2.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_2.erl deleted file mode 100644 index 0c094d584a..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_2.erl +++ /dev/null @@ -1,35 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(beam_compiler_2). --export([beam_compiler_2/0]). - -beam_compiler_2() -> - ok. - --record(foo,{a,b}). - -try_me() -> - try_me({foo,x,z},{foo,y,z}). - -try_me(X,Y) -> - f(X#foo.a =/= Y#foo.a,X#foo.b =/= X#foo.b). - -f(A,B) -> - A. - diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_3.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_3.erl deleted file mode 100644 index aced49b69c..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_3.erl +++ /dev/null @@ -1,29 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(beam_compiler_3). --export([beam_compiler_3/0, f/0]). - -%% From Ulf Wiger. - -beam_compiler_3() -> - ok. - -f() -> - [_|T] = lists:reverse("xxx"), - T. diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_4.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_4.erl index 5e74d3cd3c..0a394619de 100644 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_4.erl +++ b/lib/compiler/test/compilation_SUITE_data/beam_compiler_4.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_5.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_5.erl deleted file mode 100644 index a23a0d518c..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_5.erl +++ /dev/null @@ -1,28 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(beam_compiler_5). --export([beam_compiler_5/0]). - --compile(export_all). - -beam_compiler_5() -> - ok. - -t() -> - [_|_] = x. diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_6.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_6.erl index f263fd75b2..1fe1b2d693 100644 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_6.erl +++ b/lib/compiler/test/compilation_SUITE_data/beam_compiler_6.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_8.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_8.erl index f1f4839798..de51c59aae 100644 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_8.erl +++ b/lib/compiler/test/compilation_SUITE_data/beam_compiler_8.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/beam_compiler_9.erl b/lib/compiler/test/compilation_SUITE_data/beam_compiler_9.erl index 581b908753..5d0097f0fd 100644 --- a/lib/compiler/test/compilation_SUITE_data/beam_compiler_9.erl +++ b/lib/compiler/test/compilation_SUITE_data/beam_compiler_9.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_1.erl b/lib/compiler/test/compilation_SUITE_data/bin_syntax_1.erl deleted file mode 100644 index 7df1543d83..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_1.erl +++ /dev/null @@ -1,31 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(bin_syntax_1). - --export([f/2,?MODULE/0]). - -?MODULE() -> - ok. - -f(X, Y) -> - case X of - a -> - Y2 = 8 - end, - <<5:Y2>> = Y. diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_2.erl b/lib/compiler/test/compilation_SUITE_data/bin_syntax_2.erl deleted file mode 100644 index dcf7b0f8bd..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_2.erl +++ /dev/null @@ -1,41 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(bin_syntax_2). - --export([?MODULE/0]). - -%% This module tests that constant propagation is done properly. - -?MODULE() -> - 258 = b(<<1,2>>), - F = c(), - 259 = F(<<1,3>>), - ok. - -b(B) -> - Sz = 16, - <<X:Sz/integer>> = B, - X. - -c() -> - Size = 16, - fun(Bin) -> - <<X:Size/integer>> = Bin, - X - end. diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_3.erl b/lib/compiler/test/compilation_SUITE_data/bin_syntax_3.erl deleted file mode 100644 index 93d35d5628..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_3.erl +++ /dev/null @@ -1,35 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(bin_syntax_3). --export([?MODULE/0,decode_integer/3]). - -?MODULE() -> - ok. - -decode_integer(Len, <<B1:1,B2:7,Bs/binary>>, RemovedBytes) when B1 == 0 -> - Bin = <<Skip:Len/unit:8, Buffer2/binary>> = <<B1:1,B2:7,Bs/binary>>, - Size = size(Bin), - <<Int:Size/unit:8>> = Bin, - {Int,Buffer2,RemovedBytes}; -decode_integer(Len,<<B1:1,B2:7,Bs/binary>>,RemovedBytes) -> - Bin = <<Skip:Len/unit:8,Buffer2/binary>> = <<B1:1,B2:7,Bs/binary>>, - Size = size(Bin), - <<N:Size/unit:8>> = <<B2,Bs/binary>>, - Int = N - (1 bsl (8 * size(Bin) -1)), - {Int,Buffer2,RemovedBytes}. diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_4.erl b/lib/compiler/test/compilation_SUITE_data/bin_syntax_4.erl deleted file mode 100644 index fe0ce80270..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_4.erl +++ /dev/null @@ -1,32 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(bin_syntax_4). --export([?MODULE/0,f4b/2,f4c/2]). - -?MODULE() -> - ok. - -f4b(X, Y) -> - fun (<< A:Y >>, Y, B) -> fum(A, X, Y, B) end. - -f4c(X, Y) -> - fun (Y, << A:Y >>, B) -> fum(A, X, Y, B) end. - -fum(A, B, C, D) -> - {A,B,C,D}. diff --git a/lib/compiler/test/compilation_SUITE_data/bin_syntax_6.erl b/lib/compiler/test/compilation_SUITE_data/bin_syntax_6.erl deleted file mode 100644 index 8de3a9094f..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/bin_syntax_6.erl +++ /dev/null @@ -1,39 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(bin_syntax_6). --export([?MODULE/0,x/1,y/1]). - -?MODULE() -> - ok. - -x(X) -> - blurf(), - B = {X,"OK",<<>>}, - catch b({a,B}). - -y(X) -> - blurf(), - B = {X,"OK",<<42>>}, - catch b({a,B}). - -blurf() -> - ok. - -b(_) -> - ok. diff --git a/lib/compiler/test/compilation_SUITE_data/catch_in_catch.erl b/lib/compiler/test/compilation_SUITE_data/catch_in_catch.erl deleted file mode 100644 index c732a912f0..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/catch_in_catch.erl +++ /dev/null @@ -1,51 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(catch_in_catch). - --export([?MODULE/0,do_start/1]). - -?MODULE() -> - process_flag(trap_exit, true), - Pid = spawn_link(?MODULE, do_start, [x]), - receive - {'EXIT',Pid,good_exit} -> ok; - Other -> - io:format("Unexpected: ~p\n", [Other]), - error - after 32000 -> - io:format("No message received\n"), - error - end. - -do_start(Param) -> - init(Param), - exit(good_exit). - -init(Param) -> - process_flag(trap_exit, true), - %% The catches were improperly nested, causing a "No catch found" crash. - (catch begin - foo(Param), - (catch exit(bar)) - end - ), - ignore. - -foo(_) -> - ok. diff --git a/lib/compiler/test/compilation_SUITE_data/compiler_1.erl b/lib/compiler/test/compilation_SUITE_data/compiler_1.erl index 6dbd80d962..87985bdf80 100644 --- a/lib/compiler/test/compilation_SUITE_data/compiler_1.erl +++ b/lib/compiler/test/compilation_SUITE_data/compiler_1.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/compiler_3.erl b/lib/compiler/test/compilation_SUITE_data/compiler_3.erl deleted file mode 100644 index 47891a22b5..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/compiler_3.erl +++ /dev/null @@ -1,33 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(compiler_3). --export([compiler_3/0]). --record(rec,{a}). - -compiler_3() -> - guard_record(). - -guard_record() -> - 1=func(#rec{}), - {'EXIT',_} = (catch func({rec})), - ok. - -func(X) when record(X, -rec) -> - 1. diff --git a/lib/compiler/test/compilation_SUITE_data/compiler_5.erl b/lib/compiler/test/compilation_SUITE_data/compiler_5.erl deleted file mode 100644 index c2a0c2064f..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/compiler_5.erl +++ /dev/null @@ -1,49 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(compiler_5). --export([compiler_5/0]). - -compiler_5() -> - f0(), - f1(), - f2(), - ok. - -%% compiler treats records with 1 and 2 fields differently... --record(nil, {}). --record(foo, {hello=1}). --record(bar, {hello=2,there=3}). - -f0() -> - R1 = #nil{}, - R2 = R1#nil{}, %% stupid code, but compiler shouldn't crash - R1 = R2, - ok. - -f1() -> - R1 = #foo{}, - R2 = R1#foo{}, %% stupid code, but compiler shouldn't crash - R1 = R2, - ok. - -f2() -> - R1 = #bar{}, - R2 = R1#bar{}, %% stupid code, but compiler shouldn't crash - R1 = R2, - ok. diff --git a/lib/compiler/test/compilation_SUITE_data/complex_guard.erl b/lib/compiler/test/compilation_SUITE_data/complex_guard.erl deleted file mode 100644 index 961aa6a460..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/complex_guard.erl +++ /dev/null @@ -1,31 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(complex_guard). - --compile(export_all). - -?MODULE() -> - ok. - -f(X1,Y1,Z1) -> - if - ((X1 =:= 4) or (X1 =:= 5)) and ((Y1 =:= 4) or (Y1 =:= 5)) and ((Z1 =:= 4) or (Z1 =:= 5)) or ((X1 =:= 1) or (X1 =:= 2) or (X1 =:= 3)) and ((Y1 =:= 1) or (Y1 =:= 2) or (Y1 =:= 3)) and ((Z1 =:= 1) or (Z1 =:= 2) or (Z1 =:= 3)) -> - true - end. - diff --git a/lib/compiler/test/compilation_SUITE_data/const_list_256.erl b/lib/compiler/test/compilation_SUITE_data/const_list_256.erl index 0baf427911..bdce438162 100644 --- a/lib/compiler/test/compilation_SUITE_data/const_list_256.erl +++ b/lib/compiler/test/compilation_SUITE_data/const_list_256.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/convopts.erl b/lib/compiler/test/compilation_SUITE_data/convopts.erl index 429c683ca9..8404eb82fd 100644 --- a/lib/compiler/test/compilation_SUITE_data/convopts.erl +++ b/lib/compiler/test/compilation_SUITE_data/convopts.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/guards.erl b/lib/compiler/test/compilation_SUITE_data/guards.erl deleted file mode 100644 index 84e41b8ede..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/guards.erl +++ /dev/null @@ -1,106 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(guards). - --export([guards/0]). - -guards() -> - ok = t(), - ok = f(), - ok = ct(1), - ok = multi(1), - ok = multi(2), - ok = multi(3). - -%% The following tests are always true. -t() when integer(42) -> - ok; -t() when float(2.0) -> - ok; -t() when number(7) -> - ok; -t() when number(3.14) -> - ok; -t() when atom(error) -> - ok; -t() when list([a]) -> - ok; -t() when tuple({}) -> - ok; -t() when tuple({1, 2}) -> - ok. - -%% The following tests are always false. -f() when integer(a) -> - ok; -f() when float(b) -> - ok; -f() when number(c) -> - ok; -f() when atom(42) -> - ok; -f() when list(33) -> - ok; -f() when list({}) -> - ok; -f() when list({1, 2}) -> - ok; -f() when tuple(33) -> - ok; -f() when tuple([a]) -> - ok; -f() when tuple([]) -> - ok; -f() when tuple(35) -> - ok; -f() -> - ok. - -%% The following tests are always true. -ct(X) -> - case X of - Y when integer(42) -> - ok; - Y when float(2.0) -> - ok; - Y when number(7) -> - ok; - Y when number(3.14) -> - ok; - Y when atom(error) -> - ok; - Y when list([a]) -> - ok; - Y when tuple({}) -> - ok; - Y when tuple({1, 2}) -> - ok - end. - -multi(X) -> - case X of - Y when float(Y) ; integer(Y) -> - ok; - Y when Y > 1, Y < 10 ; atom(Y) -> - ok; - Y when Y == 4, number(Y) ; list(Y) -> - pannkaka; - Y when Y==3 ; Y==5 ; Y==6 -> - ok - end. diff --git a/lib/compiler/test/compilation_SUITE_data/live_var.erl b/lib/compiler/test/compilation_SUITE_data/live_var.erl index 483eec0630..b984d98c2b 100644 --- a/lib/compiler/test/compilation_SUITE_data/live_var.erl +++ b/lib/compiler/test/compilation_SUITE_data/live_var.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/long_string.erl b/lib/compiler/test/compilation_SUITE_data/long_string.erl deleted file mode 100644 index b3cf77b13f..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/long_string.erl +++ /dev/null @@ -1,670 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(long_string). - --export([?MODULE/0]). - -?MODULE() -> - Options = "some stupid long string", - 49252 = length(generate(Options, "348927432097sfkjfkljf329")), - ok. - -generate(Options, Glurf) -> - "asdhfaslfdjhhwleirsk e4kjhr430usduy fdk;///s llsjkf;laskjfsdfkjasdfkj -sdkljflasdfkjasldkfjasd" ++ Options ++ -"CSAgICAgICBWZXJzaW9uIDIsIEp1bmUgMTk5MQoKIENvcHlyaWdodCAoQykgMTk4OSwgMTk5MSBG -cmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIEluYy4KICAgICAgICAgICAgICAgICAgICAgICA1OSBU -ZW1wbGUgUGxhY2UsIFN1aXRlIDMzMCwgQm9zdG9uLCBNQSAgMDIxMTEtMTMwNyAgVVNBCiBFdmVy -eW9uZSBpcyBwZXJtaXR0ZWQgdG8gY29weSBhbmQgZGlzdHJpYnV0ZSB2ZXJiYXRpbSBjb3BpZXMK -IG9mIHRoaXMgbGljZW5zZSBkb2N1bWVudCwgYnV0IGNoYW5naW5nIGl0IGlzIG5vdCBhbGxvd2Vk -LgoKCQkJICAgIFByZWFtYmxlCgogIFRoZSBsaWNlbnNlcyBmb3IgbW9zdCBzb2Z0d2FyZSBhcmUg -ZGVzaWduZWQgdG8gdGFrZSBhd2F5IHlvdXIKZnJlZWRvbSB0byBzaGFyZSBhbmQgY2hhbmdlIGl0 -LiAgQnkgY29udHJhc3QsIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMKTGljZW5zZSBpcyBpbnRlbmRl -ZCB0byBndWFyYW50ZWUgeW91ciBmcmVlZG9tIHRvIHNoYXJlIGFuZCBjaGFuZ2UgZnJlZQpzb2Z0 -d2FyZS0tdG8gbWFrZSBzdXJlIHRoZSBzb2Z0d2FyZSBpcyBmcmVlIGZvciBhbGwgaXRzIHVzZXJz -LiAgVGhpcwpHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFwcGxpZXMgdG8gbW9zdCBvZiB0aGUgRnJl -ZSBTb2Z0d2FyZQpGb3VuZGF0aW9uJ3Mgc29mdHdhcmUgYW5kIHRvIGFueSBvdGhlciBwcm9ncmFt -IHdob3NlIGF1dGhvcnMgY29tbWl0IHRvCnVzaW5nIGl0LiAgKFNvbWUgb3RoZXIgRnJlZSBTb2Z0 -d2FyZSBGb3VuZGF0aW9uIHNvZnR3YXJlIGlzIGNvdmVyZWQgYnkKdGhlIEdOVSBMaWJyYXJ5IEdl -bmVyYWwgUHVibGljIExpY2Vuc2UgaW5zdGVhZC4pICBZb3UgY2FuIGFwcGx5IGl0IHRvCnlvdXIg -cHJvZ3JhbXMsIHRvby4KCiAgV2hlbiB3ZSBzcGVhayBvZiBmcmVlIHNvZnR3YXJlLCB3ZSBhcmUg -cmVmZXJyaW5nIHRvIGZyZWVkb20sIG5vdApwcmljZS4gIE91ciBHZW5lcmFsIFB1YmxpYyBMaWNl -bnNlcyBhcmUgZGVzaWduZWQgdG8gbWFrZSBzdXJlIHRoYXQgeW91CmhhdmUgdGhlIGZyZWVkb20g -dG8gZGlzdHJpYnV0ZSBjb3BpZXMgb2YgZnJlZSBzb2Z0d2FyZSAoYW5kIGNoYXJnZSBmb3IKdGhp -cyBzZXJ2aWNlIGlmIHlvdSB3aXNoKSwgdGhhdCB5b3UgcmVjZWl2ZSBzb3VyY2UgY29kZSBvciBj -YW4gZ2V0IGl0CmlmIHlvdSB3YW50IGl0LCB0aGF0IHlvdSBjYW4gY2hhbmdlIHRoZSBzb2Z0d2Fy -ZSBvciB1c2UgcGllY2VzIG9mIGl0CmluIG5ldyBmcmVlIHByb2dyYW1zOyBhbmQgdGhhdCB5b3Ug -a25vdyB5b3UgY2FuIGRvIHRoZXNlIHRoaW5ncy4KCiAgVG8gcHJvdGVjdCB5b3VyIHJpZ2h0cywg -d2UgbmVlZCB0byBtYWtlIHJlc3RyaWN0aW9ucyB0aGF0IGZvcmJpZAphbnlvbmUgdG8gZGVueSB5 -b3UgdGhlc2UgcmlnaHRzIG9yIHRvIGFzayB5b3UgdG8gc3VycmVuZGVyIHRoZSByaWdodHMuClRo -ZXNlIHJlc3RyaWN0aW9ucyB0cmFuc2xhdGUgdG8gY2VydGFpbiByZXNwb25zaWJpbGl0aWVzIGZv -ciB5b3UgaWYgeW91CmRpc3RyaWJ1dGUgY29waWVzIG9mIHRoZSBzb2Z0d2FyZSwgb3IgaWYgeW91 -IG1vZGlmeSBpdC4KCiAgRm9yIGV4YW1wbGUsIGlmIHlvdSBkaXN0cmlidXRlIGNvcGllcyBvZiBz -dWNoIGEgcHJvZ3JhbSwgd2hldGhlcgpncmF0aXMgb3IgZm9yIGEgZmVlLCB5b3UgbXVzdCBnaXZl -IHRoZSByZWNpcGllbnRzIGFsbCB0aGUgcmlnaHRzIHRoYXQKeW91IGhhdmUuICBZb3UgbXVzdCBt -YWtlIHN1cmUgdGhhdCB0aGV5LCB0b28sIHJlY2VpdmUgb3IgY2FuIGdldCB0aGUKc291cmNlIGNv -ZGUuICBBbmQgeW91IG11c3Qgc2hvdyB0aGVtIHRoZXNlIHRlcm1zIHNvIHRoZXkga25vdyB0aGVp -cgpyaWdodHMuCgogIFdlIHByb3RlY3QgeW91ciByaWdodHMgd2l0aCB0d28gc3RlcHM6ICgxKSBj -b3B5cmlnaHQgdGhlIHNvZnR3YXJlLCBhbmQKKDIpIG9mZmVyIHlvdSB0aGlzIGxpY2Vuc2Ugd2hp -Y2ggZ2l2ZXMgeW91IGxlZ2FsIHBlcm1pc3Npb24gdG8gY29weSwKZGlzdHJpYnV0ZSBhbmQvb3Ig -bW9kaWZ5IHRoZSBzb2Z0d2FyZS4KCiAgQWxzbywgZm9yIGVhY2ggYXV0aG9yJ3MgcHJvdGVjdGlv -biBhbmQgb3Vycywgd2Ugd2FudCB0byBtYWtlIGNlcnRhaW4KdGhhdCBldmVyeW9uZSB1bmRlcnN0 -YW5kcyB0aGF0IHRoZXJlIGlzIG5vIHdhcnJhbnR5IGZvciB0aGlzIGZyZWUKc29mdHdhcmUuICBJ -ZiB0aGUgc29mdHdhcmUgaXMgbW9kaWZpZWQgYnkgc29tZW9uZSBlbHNlIGFuZCBwYXNzZWQgb24s -IHdlCndhbnQgaXRzIHJlY2lwaWVudHMgdG8ga25vdyB0aGF0IHdoYXQgdGhleSBoYXZlIGlzIG5v -dCB0aGUgb3JpZ2luYWwsIHNvCnRoYXQgYW55IHByb2JsZW1zIGludHJvZHVjZWQgYnkgb3RoZXJz -IHdpbGwgbm90IHJlZmxlY3Qgb24gdGhlIG9yaWdpbmFsCmF1dGhvcnMnIHJlcHV0YXRpb25zLgoK -ICBGaW5hbGx5LCBhbnkgZnJlZSBwcm9ncmFtIGlzIHRocmVhdGVuZWQgY29uc3RhbnRseSBieSBz -b2Z0d2FyZQpwYXRlbnRzLiAgV2Ugd2lzaCB0byBhdm9pZCB0aGUgZGFuZ2VyIHRoYXQgcmVkaXN0 -cmlidXRvcnMgb2YgYSBmcmVlCnByb2dyYW0gd2lsbCBpbmRpdmlkdWFsbHkgb2J0YWluIHBhdGVu -dCBsaWNlbnNlcywgaW4gZWZmZWN0IG1ha2luZyB0aGUKcHJvZ3JhbSBwcm9wcmlldGFyeS4gIFRv -IHByZXZlbnQgdGhpcywgd2UgaGF2ZSBtYWRlIGl0IGNsZWFyIHRoYXQgYW55CnBhdGVudCBtdXN0 -IGJlIGxpY2Vuc2VkIGZvciBldmVyeW9uZSdzIGZyZWUgdXNlIG9yIG5vdCBsaWNlbnNlZCBhdCBh -bGwuCgogIFRoZSBwcmVjaXNlIHRlcm1zIGFuZCBjb25kaXRpb25zIGZvciBjb3B5aW5nLCBkaXN0 -cmlidXRpb24gYW5kCm1vZGlmaWNhdGlvbiBmb2xsb3cuCgwKCQkgICAgR05VIEdFTkVSQUwgUFVC -TElDIExJQ0VOU0UKICAgVEVSTVMgQU5EIENPTkRJVElPTlMgRk9SIENPUFlJTkcsIERJU1RSSUJV -VElPTiBBTkQgTU9ESUZJQ0FUSU9OCgogIDAuIFRoaXMgTGljZW5zZSBhcHBsaWVzIHRvIGFueSBw -cm9ncmFtIG9yIG90aGVyIHdvcmsgd2hpY2ggY29udGFpbnMKYSBub3RpY2UgcGxhY2VkIGJ5IHRo -ZSBjb3B5cmlnaHQgaG9sZGVyIHNheWluZyBpdCBtYXkgYmUgZGlzdHJpYnV0ZWQKdW5kZXIgdGhl -IHRlcm1zIG9mIHRoaXMgR2VuZXJhbCBQdWJsaWMgTGljZW5zZS4gIFRoZSAiUHJvZ3JhbSIsIGJl -bG93LApyZWZlcnMgdG8gYW55IHN1Y2ggcHJvZ3JhbSBvciB3b3JrLCBhbmQgYSAid29yayBiYXNl -ZCBvbiB0aGUgUHJvZ3JhbSIKbWVhbnMgZWl0aGVyIHRoZSBQcm9ncmFtIG9yIGFueSBkZXJpdmF0 -aXZlIHdvcmsgdW5kZXIgY29weXJpZ2h0IGxhdzoKdGhhdCBpcyB0byBzYXksIGEgd29yayBjb250 -YWluaW5nIHRoZSBQcm9ncmFtIG9yIGEgcG9ydGlvbiBvZiBpdCwKZWl0aGVyIHZlcmJhdGltIG9y -IHdpdGggbW9kaWZpY2F0aW9ucyBhbmQvb3IgdHJhbnNsYXRlZCBpbnRvIGFub3RoZXIKbGFuZ3Vh -Z2UuICAoSGVyZWluYWZ0ZXIsIHRyYW5zbGF0aW9uIGlzIGluY2x1ZGVkIHdpdGhvdXQgbGltaXRh -dGlvbiBpbgp0aGUgdGVybSAibW9kaWZpY2F0aW9uIi4pICBFYWNoIGxpY2Vuc2VlIGlzIGFkZHJl -c3NlZCBhcyAieW91Ii4KCkFjdGl2aXRpZXMgb3RoZXIgdGhhbiBjb3B5aW5nLCBkaXN0cmlidXRp -b24gYW5kIG1vZGlmaWNhdGlvbiBhcmUgbm90CmNvdmVyZWQgYnkgdGhpcyBMaWNlbnNlOyB0aGV5 -IGFyZSBvdXRzaWRlIGl0cyBzY29wZS4gIFRoZSBhY3Qgb2YKcnVubmluZyB0aGUgUHJvZ3JhbSBp -cyBub3QgcmVzdHJpY3RlZCwgYW5kIHRoZSBvdXRwdXQgZnJvbSB0aGUgUHJvZ3JhbQppcyBjb3Zl -cmVkIG9ubHkgaWYgaXRzIGNvbnRlbnRzIGNvbnN0aXR1dGUgYSB3b3JrIGJhc2VkIG9uIHRoZQpQ -cm9ncmFtIChpbmRlcGVuZGVudCBvZiBoYXZpbmcgYmVlbiBtYWRlIGJ5IHJ1bm5pbmcgdGhlIFBy -b2dyYW0pLgpXaGV0aGVyIHRoYXQgaXMgdHJ1ZSBkZXBlbmRzIG9uIHdoYXQgdGhlIFByb2dyYW0g -ZG9lcy4KCiAgMS4gWW91IG1heSBjb3B5IGFuZCBkaXN0cmlidXRlIHZlcmJhdGltIGNvcGllcyBv -ZiB0aGUgUHJvZ3JhbSdzCnNvdXJjZSBjb2RlIGFzIHlvdSByZWNlaXZlIGl0LCBpbiBhbnkgbWVk -aXVtLCBwcm92aWRlZCB0aGF0IHlvdQpjb25zcGljdW91c2x5IGFuZCBhcHByb3ByaWF0ZWx5IHB1 -Ymxpc2ggb24gZWFjaCBjb3B5IGFuIGFwcHJvcHJpYXRlCmNvcHlyaWdodCBub3RpY2UgYW5kIGRp -c2NsYWltZXIgb2Ygd2FycmFudHk7IGtlZXAgaW50YWN0IGFsbCB0aGUKbm90aWNlcyB0aGF0IHJl -ZmVyIHRvIHRoaXMgTGljZW5zZSBhbmQgdG8gdGhlIGFic2VuY2Ugb2YgYW55IHdhcnJhbnR5Owph -bmQgZ2l2ZSBhbnkgb3RoZXIgcmVjaXBpZW50cyBvZiB0aGUgUHJvZ3JhbSBhIGNvcHkgb2YgdGhp -cyBMaWNlbnNlCmFsb25nIHdpdGggdGhlIFByb2dyYW0uCgpZb3UgbWF5IGNoYXJnZSBhIGZlZSBm -b3IgdGhlIHBoeXNpY2FsIGFjdCBvZiB0cmFuc2ZlcnJpbmcgYSBjb3B5LCBhbmQKeW91IG1heSBh -dCB5b3VyIG9wdGlvbiBvZmZlciB3YXJyYW50eSBwcm90ZWN0aW9uIGluIGV4Y2hhbmdlIGZvciBh -IGZlZS4KCiAgMi4gWW91IG1heSBtb2RpZnkgeW91ciBjb3B5IG9yIGNvcGllcyBvZiB0aGUgUHJv -Z3JhbSBvciBhbnkgcG9ydGlvbgpvZiBpdCwgdGh1cyBmb3JtaW5nIGEgd29yayBiYXNlZCBvbiB0 -aGUgUHJvZ3JhbSwgYW5kIGNvcHkgYW5kCmRpc3RyaWJ1dGUgc3VjaCBtb2RpZmljYXRpb25zIG9y -IHdvcmsgdW5kZXIgdGhlIHRlcm1zIG9mIFNlY3Rpb24gMQphYm92ZSwgcHJvdmlkZWQgdGhhdCB5 -b3UgYWxzbyBtZWV0IGFsbCBvZiB0aGVzZSBjb25kaXRpb25zOgoKICAgIGEpIFlvdSBtdXN0IGNh -dXNlIHRoZSBtb2RpZmllZCBmaWxlcyB0byBjYXJyeSBwcm9taW5lbnQgbm90aWNlcwogICAgc3Rh -dGluZyB0aGF0IHlvdSBjaGFuZ2VkIHRoZSBmaWxlcyBhbmQgdGhlIGRhdGUgb2YgYW55IGNoYW5n -ZS4KCiAgICBiKSBZb3UgbXVzdCBjYXVzZSBhbnkgd29yayB0aGF0IHlvdSBkaXN0cmlidXRlIG9y -IHB1Ymxpc2gsIHRoYXQgaW4KICAgIHdob2xlIG9yIGluIHBhcnQgY29udGFpbnMgb3IgaXMgZGVy -aXZlZCBmcm9tIHRoZSBQcm9ncmFtIG9yIGFueQogICAgcGFydCB0aGVyZW9mLCB0byBiZSBsaWNl -bnNlZCBhcyBhIHdob2xlIGF0IG5vIGNoYXJnZSB0byBhbGwgdGhpcmQKICAgIHBhcnRpZXMgdW5k -ZXIgdGhlIHRlcm1zIG9mIHRoaXMgTGljZW5zZS4KCiAgICBjKSBJZiB0aGUgbW9kaWZpZWQgcHJv -Z3JhbSBub3JtYWxseSByZWFkcyBjb21tYW5kcyBpbnRlcmFjdGl2ZWx5CiAgICB3aGVuIHJ1biwg -eW91IG11c3QgY2F1c2UgaXQsIHdoZW4gc3RhcnRlZCBydW5uaW5nIGZvciBzdWNoCiAgICBpbnRl -cmFjdGl2ZSB1c2UgaW4gdGhlIG1vc3Qgb3JkaW5hcnkgd2F5LCB0byBwcmludCBvciBkaXNwbGF5 -IGFuCiAgICBhbm5vdW5jZW1lbnQgaW5jbHVkaW5nIGFuIGFwcHJvcHJpYXRlIGNvcHlyaWdodCBu -b3RpY2UgYW5kIGEKICAgIG5vdGljZSB0aGF0IHRoZXJlIGlzIG5vIHdhcnJhbnR5IChvciBlbHNl -LCBzYXlpbmcgdGhhdCB5b3UgcHJvdmlkZQogICAgYSB3YXJyYW50eSkgYW5kIHRoYXQgdXNlcnMg -bWF5IHJlZGlzdHJpYnV0ZSB0aGUgcHJvZ3JhbSB1bmRlcgogICAgdGhlc2UgY29uZGl0aW9ucywg -YW5kIHRlbGxpbmcgdGhlIHVzZXIgaG93IHRvIHZpZXcgYSBjb3B5IG9mIHRoaXMKICAgIExpY2Vu -c2UuICAoRXhjZXB0aW9uOiBpZiB0aGUgUHJvZ3JhbSBpdHNlbGYgaXMgaW50ZXJhY3RpdmUgYnV0 -CiAgICBkb2VzIG5vdCBub3JtYWxseSBwcmludCBzdWNoIGFuIGFubm91bmNlbWVudCwgeW91ciB3 -b3JrIGJhc2VkIG9uCiAgICB0aGUgUHJvZ3JhbSBpcyBub3QgcmVxdWlyZWQgdG8gcHJpbnQgYW4g -YW5ub3VuY2VtZW50LikKDApUaGVzZSByZXF1aXJlbWVudHMgYXBwbHkgdG8gdGhlIG1vZGlmaWVk -IHdvcmsgYXMgYSB3aG9sZS4gIElmCmlkZW50aWZpYWJsZSBzZWN0aW9ucyBvZiB0aGF0IHdvcmsg -YXJlIG5vdCBkZXJpdmVkIGZyb20gdGhlIFByb2dyYW0sCmFuZCBjYW4gYmUgcmVhc29uYWJseSBj -b25zaWRlcmVkIGluZGVwZW5kZW50IGFuZCBzZXBhcmF0ZSB3b3JrcyBpbgp0aGVtc2VsdmVzLCB0 -aGVuIHRoaXMgTGljZW5zZSwgYW5kIGl0cyB0ZXJtcywgZG8gbm90IGFwcGx5IHRvIHRob3NlCnNl -Y3Rpb25zIHdoZW4geW91IGRpc3RyaWJ1dGUgdGhlbSBhcyBzZXBhcmF0ZSB3b3Jrcy4gIEJ1dCB3 -aGVuIHlvdQpkaXN0cmlidXRlIHRoZSBzYW1lIHNlY3Rpb25zIGFzIHBhcnQgb2YgYSB3aG9sZSB3 -aGljaCBpcyBhIHdvcmsgYmFzZWQKb24gdGhlIFByb2dyYW0sIHRoZSBkaXN0cmlidXRpb24gb2Yg -dGhlIHdob2xlIG11c3QgYmUgb24gdGhlIHRlcm1zIG9mCnRoaXMgTGljZW5zZSwgd2hvc2UgcGVy -bWlzc2lvbnMgZm9yIG90aGVyIGxpY2Vuc2VlcyBleHRlbmQgdG8gdGhlCmVudGlyZSB3aG9sZSwg -YW5kIHRodXMgdG8gZWFjaCBhbmQgZXZlcnkgcGFydCByZWdhcmRsZXNzIG9mIHdobyB3cm90ZSBp -dC4KClRodXMsIGl0IGlzIG5vdCB0aGUgaW50ZW50IG9mIHRoaXMgc2VjdGlvbiB0byBjbGFpbSBy -aWdodHMgb3IgY29udGVzdAp5b3VyIHJpZ2h0cyB0byB3b3JrIHdyaXR0ZW4gZW50aXJlbHkgYnkg -eW91OyByYXRoZXIsIHRoZSBpbnRlbnQgaXMgdG8KZXhlcmNpc2UgdGhlIHJpZ2h0IHRvIGNvbnRy -b2wgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkZXJpdmF0aXZlIG9yCmNvbGxlY3RpdmUgd29ya3MgYmFz -ZWQgb24gdGhlIFByb2dyYW0uCgpJbiBhZGRpdGlvbiwgbWVyZSBhZ2dyZWdhdGlvbiBvZiBhbm90 -aGVyIHdvcmsgbm90IGJhc2VkIG9uIHRoZSBQcm9ncmFtCndpdGggdGhlIFByb2dyYW0gKG9yIHdp -dGggYSB3b3JrIGJhc2VkIG9uIHRoZSBQcm9ncmFtKSBvbiBhIHZvbHVtZSBvZgphIHN0b3JhZ2Ug -b3IgZGlzdHJpYnV0aW9uIG1lZGl1bSBkb2VzIG5vdCBicmluZyB0aGUgb3RoZXIgd29yayB1bmRl -cgp0aGUgc2NvcGUgb2YgdGhpcyBMaWNlbnNlLgoKICAzLiBZb3UgbWF5IGNvcHkgYW5kIGRpc3Ry -aWJ1dGUgdGhlIFByb2dyYW0gKG9yIGEgd29yayBiYXNlZCBvbiBpdCwKdW5kZXIgU2VjdGlvbiAy -KSBpbiBvYmplY3QgY29kZSBvciBleGVjdXRhYmxlIGZvcm0gdW5kZXIgdGhlIHRlcm1zIG9mClNl -Y3Rpb25zIDEgYW5kIDIgYWJvdmUgcHJvdmlkZWQgdGhhdCB5b3UgYWxzbyBkbyBvbmUgb2YgdGhl -IGZvbGxvd2luZzoKCiAgICBhKSBBY2NvbXBhbnkgaXQgd2l0aCB0aGUgY29tcGxldGUgY29ycmVz -cG9uZGluZyBtYWNoaW5lLXJlYWRhYmxlCiAgICBzb3VyY2UgY29kZSwgd2hpY2ggbXVzdCBiZSBk -aXN0cmlidXRlZCB1bmRlciB0aGUgdGVybXMgb2YgU2VjdGlvbnMKICAgIDEgYW5kIDIgYWJvdmUg -b24gYSBtZWRpdW0gY3VzdG9tYXJpbHkgdXNlZCBmb3Igc29mdHdhcmUgaW50ZXJjaGFuZ2U7IG9y -LAoKICAgIGIpIEFjY29tcGFueSBpdCB3aXRoIGEgd3JpdHRlbiBvZmZlciwgdmFsaWQgZm9yIGF0 -IGxlYXN0IHRocmVlCiAgICB5ZWFycywgdG8gZ2l2ZSBhbnkgdGhpcmQgcGFydHksIGZvciBhIGNo -YXJnZSBubyBtb3JlIHRoYW4geW91cgogICAgY29zdCBvZiBwaHlzaWNhbGx5IHBlcmZvcm1pbmcg -c291cmNlIGRpc3RyaWJ1dGlvbiwgYSBjb21wbGV0ZQogICAgbWFjaGluZS1yZWFkYWJsZSBjb3B5 -IG9mIHRoZSBjb3JyZXNwb25kaW5nIHNvdXJjZSBjb2RlLCB0byBiZQogICAgZGlzdHJpYnV0ZWQg -dW5kZXIgdGhlIHRlcm1zIG9mIFNlY3Rpb25zIDEgYW5kIDIgYWJvdmUgb24gYSBtZWRpdW0KICAg -IGN1c3RvbWFyaWx5IHVzZWQgZm9yIHNvZnR3YXJlIGludGVyY2hhbmdlOyBvciwKCiAgICBjKSBB -Y2NvbXBhbnkgaXQgd2l0aCB0aGUgaW5mb3JtYXRpb24geW91IHJlY2VpdmVkIGFzIHRvIHRoZSBv -ZmZlcgogICAgdG8gZGlzdHJpYnV0ZSBjb3JyZXNwb25kaW5nIHNvdXJjZSBjb2RlLiAgKFRoaXMg -YWx0ZXJuYXRpdmUgaXMKICAgIGFsbG93ZWQgb25seSBmb3Igbm9uY29tbWVyY2lhbCBkaXN0cmli -dXRpb24gYW5kIG9ubHkgaWYgeW91CiAgICByZWNlaXZlZCB0aGUgcHJvZ3JhbSBpbiBvYmplY3Qg -Y29kZSBvciBleGVjdXRhYmxlIGZvcm0gd2l0aCBzdWNoCiAgICBhbiBvZmZlciwgaW4gYWNjb3Jk -IHdpdGggU3Vic2VjdGlvbiBiIGFib3ZlLikKClRoZSBzb3VyY2UgY29kZSBmb3IgYSB3b3JrIG1l -YW5zIHRoZSBwcmVmZXJyZWQgZm9ybSBvZiB0aGUgd29yayBmb3IKbWFraW5nIG1vZGlmaWNhdGlv -bnMgdG8gaXQuICBGb3IgYW4gZXhlY3V0YWJsZSB3b3JrLCBjb21wbGV0ZSBzb3VyY2UKY29kZSBt -ZWFucyBhbGwgdGhlIHNvdXJjZSBjb2RlIGZvciBhbGwgbW9kdWxlcyBpdCBjb250YWlucywgcGx1 -cyBhbnkKYXNzb2NpYXRlZCBpbnRlcmZhY2UgZGVmaW5pdGlvbiBmaWxlcywgcGx1cyB0aGUgc2Ny -aXB0cyB1c2VkIHRvCmNvbnRyb2wgY29tcGlsYXRpb24gYW5kIGluc3RhbGxhdGlvbiBvZiB0aGUg -ZXhlY3V0YWJsZS4gIEhvd2V2ZXIsIGFzIGEKc3BlY2lhbCBleGNlcHRpb24sIHRoZSBzb3VyY2Ug -Y29kZSBkaXN0cmlidXRlZCBuZWVkIG5vdCBpbmNsdWRlCmFueXRoaW5nIHRoYXQgaXMgbm9ybWFs -bHkgZGlzdHJpYnV0ZWQgKGluIGVpdGhlciBzb3VyY2Ugb3IgYmluYXJ5CmZvcm0pIHdpdGggdGhl -IG1ham9yIGNvbXBvbmVudHMgKGNvbXBpbGVyLCBrZXJuZWwsIGFuZCBzbyBvbikgb2YgdGhlCm9w -ZXJhdGluZyBzeXN0ZW0gb24gd2hpY2ggdGhlIGV4ZWN1dGFibGUgcnVucywgdW5sZXNzIHRoYXQg -Y29tcG9uZW50Cml0c2VsZiBhY2NvbXBhbmllcyB0aGUgZXhlY3V0YWJsZS4KCklmIGRpc3RyaWJ1 -dGlvbiBvZiBleGVjdXRhYmxlIG9yIG9iamVjdCBjb2RlIGlzIG1hZGUgYnkgb2ZmZXJpbmcKYWNj -ZXNzIHRvIGNvcHkgZnJvbSBhIGRlc2lnbmF0ZWQgcGxhY2UsIHRoZW4gb2ZmZXJpbmcgZXF1aXZh -bGVudAphY2Nlc3MgdG8gY29weSB0aGUgc291cmNlIGNvZGUgZnJvbSB0aGUgc2FtZSBwbGFjZSBj -b3VudHMgYXMKZGlzdHJpYnV0aW9uIG9mIHRoZSBzb3VyY2UgY29kZSwgZXZlbiB0aG91Z2ggdGhp -cmQgcGFydGllcyBhcmUgbm90CmNvbXBlbGxlZCB0byBjb3B5IHRoZSBzb3VyY2UgYWxvbmcgd2l0 -aCB0aGUgb2JqZWN0IGNvZGUuCgwKICA0LiBZb3UgbWF5IG5vdCBjb3B5LCBtb2RpZnksIHN1Ymxp -Y2Vuc2UsIG9yIGRpc3RyaWJ1dGUgdGhlIFByb2dyYW0KZXhjZXB0IGFzIGV4cHJlc3NseSBwcm92 -aWRlZCB1bmRlciB0aGlzIExpY2Vuc2UuICBBbnkgYXR0ZW1wdApvdGhlcndpc2UgdG8gY29weSwg -bW9kaWZ5LCBzdWJsaWNlbnNlIG9yIGRpc3RyaWJ1dGUgdGhlIFByb2dyYW0gaXMKdm9pZCwgYW5k -IHdpbGwgYXV0b21hdGljYWxseSB0ZXJtaW5hdGUgeW91ciByaWdodHMgdW5kZXIgdGhpcyBMaWNl -bnNlLgpIb3dldmVyLCBwYXJ0aWVzIHdobyBoYXZlIHJlY2VpdmVkIGNvcGllcywgb3IgcmlnaHRz -LCBmcm9tIHlvdSB1bmRlcgp0aGlzIExpY2Vuc2Ugd2lsbCBub3QgaGF2ZSB0aGVpciBsaWNlbnNl -cyB0ZXJtaW5hdGVkIHNvIGxvbmcgYXMgc3VjaApwYXJ0aWVzIHJlbWFpbiBpbiBmdWxsIGNvbXBs -aWFuY2UuCgogIDUuIFlvdSBhcmUgbm90IHJlcXVpcmVkIHRvIGFjY2VwdCB0aGlzIExpY2Vuc2Us -IHNpbmNlIHlvdSBoYXZlIG5vdApzaWduZWQgaXQuICBIb3dldmVyLCBub3RoaW5nIGVsc2UgZ3Jh -bnRzIHlvdSBwZXJtaXNzaW9uIHRvIG1vZGlmeSBvcgpkaXN0cmlidXRlIHRoZSBQcm9ncmFtIG9y -IGl0cyBkZXJpdmF0aXZlIHdvcmtzLiAgVGhlc2UgYWN0aW9ucyBhcmUKcHJvaGliaXRlZCBieSBs -YXcgaWYgeW91IGRvIG5vdCBhY2NlcHQgdGhpcyBMaWNlbnNlLiAgVGhlcmVmb3JlLCBieQptb2Rp -Znlpbmcgb3IgZGlzdHJpYnV0aW5nIHRoZSBQcm9ncmFtIChvciBhbnkgd29yayBiYXNlZCBvbiB0 -aGUKUHJvZ3JhbSksIHlvdSBpbmRpY2F0ZSB5b3VyIGFjY2VwdGFuY2Ugb2YgdGhpcyBMaWNlbnNl -IHRvIGRvIHNvLCBhbmQKYWxsIGl0cyB0ZXJtcyBhbmQgY29uZGl0aW9ucyBmb3IgY29weWluZywg -ZGlzdHJpYnV0aW5nIG9yIG1vZGlmeWluZwp0aGUgUHJvZ3JhbSBvciB3b3JrcyBiYXNlZCBvbiBp -dC4KCiAgNi4gRWFjaCB0aW1lIHlvdSByZWRpc3RyaWJ1dGUgdGhlIFByb2dyYW0gKG9yIGFueSB3 -b3JrIGJhc2VkIG9uIHRoZQpQcm9ncmFtKSwgdGhlIHJlY2lwaWVudCBhdXRvbWF0aWNhbGx5IHJl -Y2VpdmVzIGEgbGljZW5zZSBmcm9tIHRoZQpvcmlnaW5hbCBsaWNlbnNvciB0byBjb3B5LCBkaXN0 -cmlidXRlIG9yIG1vZGlmeSB0aGUgUHJvZ3JhbSBzdWJqZWN0IHRvCnRoZXNlIHRlcm1zIGFuZCBj -b25kaXRpb25zLiAgWW91IG1heSBub3QgaW1wb3NlIGFueSBmdXJ0aGVyCnJlc3RyaWN0aW9ucyBv -biB0aGUgcmVjaXBpZW50cycgZXhlcmNpc2Ugb2YgdGhlIHJpZ2h0cyBncmFudGVkIGhlcmVpbi4K -WW91IGFyZSBub3QgcmVzcG9uc2libGUgZm9yIGVuZm9yY2luZyBjb21wbGlhbmNlIGJ5IHRoaXJk -IHBhcnRpZXMgdG8KdGhpcyBMaWNlbnNlLgoKICA3LiBJZiwgYXMgYSBjb25zZXF1ZW5jZSBvZiBh -IGNvdXJ0IGp1ZGdtZW50IG9yIGFsbGVnYXRpb24gb2YgcGF0ZW50CmluZnJpbmdlbWVudCBvciBm -b3IgYW55IG90aGVyIHJlYXNvbiAobm90IGxpbWl0ZWQgdG8gcGF0ZW50IGlzc3VlcyksCmNvbmRp -dGlvbnMgYXJlIGltcG9zZWQgb24geW91ICh3aGV0aGVyIGJ5IGNvdXJ0IG9yZGVyLCBhZ3JlZW1l -bnQgb3IKb3RoZXJ3aXNlKSB0aGF0IGNvbnRyYWRpY3QgdGhlIGNvbmRpdGlvbnMgb2YgdGhpcyBM -aWNlbnNlLCB0aGV5IGRvIG5vdApleGN1c2UgeW91IGZyb20gdGhlIGNvbmRpdGlvbnMgb2YgdGhp -cyBMaWNlbnNlLiAgSWYgeW91IGNhbm5vdApkaXN0cmlidXRlIHNvIGFzIHRvIHNhdGlzZnkgc2lt -dWx0YW5lb3VzbHkgeW91ciBvYmxpZ2F0aW9ucyB1bmRlciB0aGlzCkxpY2Vuc2UgYW5kIGFueSBv -dGhlciBwZXJ0aW5lbnQgb2JsaWdhdGlvbnMsIHRoZW4gYXMgYSBjb25zZXF1ZW5jZSB5b3UKbWF5 -IG5vdCBkaXN0cmlidXRlIHRoZSBQcm9ncmFtIGF0IGFsbC4gIEZvciBleGFtcGxlLCBpZiBhIHBh -dGVudApsaWNlbnNlIHdvdWxkIG5vdCBwZXJtaXQgcm95YWx0eS1mcmVlIHJlZGlzdHJpYnV0aW9u -IG9mIHRoZSBQcm9ncmFtIGJ5CmFsbCB0aG9zZSB3aG8gcmVjZWl2ZSBjb3BpZXMgZGlyZWN0bHkg -b3IgaW5kaXJlY3RseSB0aHJvdWdoIHlvdSwgdGhlbgp0aGUgb25seSB3YXkgeW91IGNvdWxkIHNh -dGlzZnkgYm90aCBpdCBhbmQgdGhpcyBMaWNlbnNlIHdvdWxkIGJlIHRvCnJlZnJhaW4gZW50aXJl -bHkgZnJvbSBkaXN0cmlidXRpb24gb2YgdGhlIFByb2dyYW0uCgpJZiBhbnkgcG9ydGlvbiBvZiB0 -aGlzIHNlY3Rpb24gaXMgaGVsZCBpbnZhbGlkIG9yIHVuZW5mb3JjZWFibGUgdW5kZXIKYW55IHBh -cnRpY3VsYXIgY2lyY3Vtc3RhbmNlLCB0aGUgYmFsYW5jZSBvZiB0aGUgc2VjdGlvbiBpcyBpbnRl -bmRlZCB0bwphcHBseSBhbmQgdGhlIHNlY3Rpb24gYXMgYSB3aG9sZSBpcyBpbnRlbmRlZCB0byBh -cHBseSBpbiBvdGhlcgpjaXJjdW1zdGFuY2VzLgoKSXQgaXMgbm90IHRoZSBwdXJwb3NlIG9mIHRo -aXMgc2VjdGlvbiB0byBpbmR1Y2UgeW91IHRvIGluZnJpbmdlIGFueQpwYXRlbnRzIG9yIG90aGVy -IHByb3BlcnR5IHJpZ2h0IGNsYWltcyBvciB0byBjb250ZXN0IHZhbGlkaXR5IG9mIGFueQpzdWNo -IGNsYWltczsgdGhpcyBzZWN0aW9uIGhhcyB0aGUgc29sZSBwdXJwb3NlIG9mIHByb3RlY3Rpbmcg -dGhlCmludGVncml0eSBvZiB0aGUgZnJlZSBzb2Z0d2FyZSBkaXN0cmlidXRpb24gc3lzdGVtLCB3 -aGljaCBpcwppbXBsZW1lbnRlZCBieSBwdWJsaWMgbGljZW5zZSBwcmFjdGljZXMuICBNYW55IHBl -b3BsZSBoYXZlIG1hZGUKZ2VuZXJvdXMgY29udHJpYnV0aW9ucyB0byB0aGUgd2lkZSByYW5nZSBv -ZiBzb2Z0d2FyZSBkaXN0cmlidXRlZAp0aHJvdWdoIHRoYXQgc3lzdGVtIGluIHJlbGlhbmNlIG9u -IGNvbnNpc3RlbnQgYXBwbGljYXRpb24gb2YgdGhhdApzeXN0ZW07IGl0IGlzIHVwIHRvIHRoZSBh -dXRob3IvZG9ub3IgdG8gZGVjaWRlIGlmIGhlIG9yIHNoZSBpcyB3aWxsaW5nCnRvIGRpc3RyaWJ1 -dGUgc29mdHdhcmUgdGhyb3VnaCBhbnkgb3RoZXIgc3lzdGVtIGFuZCBhIGxpY2Vuc2VlIGNhbm5v -dAppbXBvc2UgdGhhdCBjaG9pY2UuCgpUaGlzIHNlY3Rpb24gaXMgaW50ZW5kZWQgdG8gbWFrZSB0 -aG9yb3VnaGx5IGNsZWFyIHdoYXQgaXMgYmVsaWV2ZWQgdG8KYmUgYSBjb25zZXF1ZW5jZSBvZiB0 -aGUgcmVzdCBvZiB0aGlzIExpY2Vuc2UuCgwKICA4LiBJZiB0aGUgZGlzdHJpYnV0aW9uIGFuZC9v -ciB1c2Ugb2YgdGhlIFByb2dyYW0gaXMgcmVzdHJpY3RlZCBpbgpjZXJ0YWluIGNvdW50cmllcyBl -aXRoZXIgYnkgcGF0ZW50cyBvciBieSBjb3B5cmlnaHRlZCBpbnRlcmZhY2VzLCB0aGUKb3JpZ2lu -YWwgY29weXJpZ2h0IGhvbGRlciB3aG8gcGxhY2VzIHRoZSBQcm9ncmFtIHVuZGVyIHRoaXMgTGlj -ZW5zZQptYXkgYWRkIGFuIGV4cGxpY2l0IGdlb2dyYXBoaWNhbCBkaXN0cmlidXRpb24gbGltaXRh -dGlvbiBleGNsdWRpbmcKdGhvc2UgY291bnRyaWVzLCBzbyB0aGF0IGRpc3RyaWJ1dGlvbiBpcyBw -ZXJtaXR0ZWQgb25seSBpbiBvciBhbW9uZwpjb3VudHJpZXMgbm90IHRodXMgZXhjbHVkZWQuICBJ -biBzdWNoIGNhc2UsIHRoaXMgTGljZW5zZSBpbmNvcnBvcmF0ZXMKdGhlIGxpbWl0YXRpb24gYXMg -aWYgd3JpdHRlbiBpbiB0aGUgYm9keSBvZiB0aGlzIExpY2Vuc2UuCgogIDkuIFRoZSBGcmVlIFNv -ZnR3YXJlIEZvdW5kYXRpb24gbWF5IHB1Ymxpc2ggcmV2aXNlZCBhbmQvb3IgbmV3IHZlcnNpb25z -Cm9mIHRoZSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZyb20gdGltZSB0byB0aW1lLiAgU3VjaCBu -ZXcgdmVyc2lvbnMgd2lsbApiZSBzaW1pbGFyIGluIHNwaXJpdCB0byB0aGUgcHJlc2VudCB2ZXJz -aW9uLCBidXQgbWF5IGRpZmZlciBpbiBkZXRhaWwgdG8KYWRkcmVzcyBuZXcgcHJvYmxlbXMgb3Ig -Y29uY2VybnMuCgpFYWNoIHZlcnNpb24gaXMgZ2l2ZW4gYSBkaXN0aW5ndWlzaGluZyB2ZXJzaW9u -IG51bWJlci4gIElmIHRoZSBQcm9ncmFtCnNwZWNpZmllcyBhIHZlcnNpb24gbnVtYmVyIG9mIHRo -aXMgTGljZW5zZSB3aGljaCBhcHBsaWVzIHRvIGl0IGFuZCAiYW55CmxhdGVyIHZlcnNpb24iLCB5 -b3UgaGF2ZSB0aGUgb3B0aW9uIG9mIGZvbGxvd2luZyB0aGUgdGVybXMgYW5kIGNvbmRpdGlvbnMK -ZWl0aGVyIG9mIHRoYXQgdmVyc2lvbiBvciBvZiBhbnkgbGF0ZXIgdmVyc2lvbiBwdWJsaXNoZWQg -YnkgdGhlIEZyZWUKU29mdHdhcmUgRm91bmRhdGlvbi4gIElmIHRoZSBQcm9ncmFtIGRvZXMgbm90 -IHNwZWNpZnkgYSB2ZXJzaW9uIG51bWJlciBvZgp0aGlzIExpY2Vuc2UsIHlvdSBtYXkgY2hvb3Nl -IGFueSB2ZXJzaW9uIGV2ZXIgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlCkZvdW5kYXRp -b24uCgogIDEwLiBJZiB5b3Ugd2lzaCB0byBpbmNvcnBvcmF0ZSBwYXJ0cyBvZiB0aGUgUHJvZ3Jh -bSBpbnRvIG90aGVyIGZyZWUKcHJvZ3JhbXMgd2hvc2UgZGlzdHJpYnV0aW9uIGNvbmRpdGlvbnMg -YXJlIGRpZmZlcmVudCwgd3JpdGUgdG8gdGhlIGF1dGhvcgp0byBhc2sgZm9yIHBlcm1pc3Npb24u -ICBGb3Igc29mdHdhcmUgd2hpY2ggaXMgY29weXJpZ2h0ZWQgYnkgdGhlIEZyZWUKU29mdHdhcmUg -Rm91bmRhdGlvbiwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgd2Ugc29t -ZXRpbWVzCm1ha2UgZXhjZXB0aW9ucyBmb3IgdGhpcy4gIE91ciBkZWNpc2lvbiB3aWxsIGJlIGd1 -aWRlZCBieSB0aGUgdHdvIGdvYWxzCm9mIHByZXNlcnZpbmcgdGhlIGZyZWUgc3RhdHVzIG9mIGFs -bCBkZXJpdmF0aXZlcyBvZiBvdXIgZnJlZSBzb2Z0d2FyZSBhbmQKb2YgcHJvbW90aW5nIHRoZSBz -aGFyaW5nIGFuZCByZXVzZSBvZiBzb2Z0d2FyZSBnZW5lcmFsbHkuCgoJCQkgICAgTk8gV0FSUkFO -VFkKCiAgMTEuIEJFQ0FVU0UgVEhFIFBST0dSQU0gSVMgTElDRU5TRUQgRlJFRSBPRiBDSEFSR0Us -IFRIRVJFIElTIE5PIFdBUlJBTlRZCkZPUiBUSEUgUFJPR1JBTSwgVE8gVEhFIEVYVEVOVCBQRVJN -SVRURUQgQlkgQVBQTElDQUJMRSBMQVcuICBFWENFUFQgV0hFTgpPVEhFUldJU0UgU1RBVEVEIElO -IFdSSVRJTkcgVEhFIENPUFlSSUdIVCBIT0xERVJTIEFORC9PUiBPVEhFUiBQQVJUSUVTClBST1ZJ -REUgVEhFIFBST0dSQU0gIkFTIElTIiBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFSVRI -RVIgRVhQUkVTU0VECk9SIElNUExJRUQsIElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLCBU -SEUgSU1QTElFRCBXQVJSQU5USUVTIE9GCk1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUyBGT1Ig -QSBQQVJUSUNVTEFSIFBVUlBPU0UuICBUSEUgRU5USVJFIFJJU0sgQVMKVE8gVEhFIFFVQUxJVFkg -QU5EIFBFUkZPUk1BTkNFIE9GIFRIRSBQUk9HUkFNIElTIFdJVEggWU9VLiAgU0hPVUxEIFRIRQpQ -Uk9HUkFNIFBST1ZFIERFRkVDVElWRSwgWU9VIEFTU1VNRSBUSEUgQ09TVCBPRiBBTEwgTkVDRVNT -QVJZIFNFUlZJQ0lORywKUkVQQUlSIE9SIENPUlJFQ1RJT04uCgogIDEyLiBJTiBOTyBFVkVOVCBV -TkxFU1MgUkVRVUlSRUQgQlkgQVBQTElDQUJMRSBMQVcgT1IgQUdSRUVEIFRPIElOIFdSSVRJTkcK -V0lMTCBBTlkgQ09QWVJJR0hUIEhPTERFUiwgT1IgQU5ZIE9USEVSIFBBUlRZIFdITyBNQVkgTU9E -SUZZIEFORC9PUgpSRURJU1RSSUJVVEUgVEhFIFBST0dSQU0gQVMgUEVSTUlUVEVEIEFCT1ZFLCBC -RSBMSUFCTEUgVE8gWU9VIEZPUiBEQU1BR0VTLApJTkNMVURJTkcgQU5ZIEdFTkVSQUwsIFNQRUNJ -QUwsIElOQ0lERU5UQUwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIEFSSVNJTkcKT1VUIE9GIFRI -RSBVU0UgT1IgSU5BQklMSVRZIFRPIFVTRSBUSEUgUFJPR1JBTSAoSU5DTFVESU5HIEJVVCBOT1Qg -TElNSVRFRApUTyBMT1NTIE9GIERBVEEgT1IgREFUQSBCRUlORyBSRU5ERVJFRCBJTkFDQ1VSQVRF -IE9SIExPU1NFUyBTVVNUQUlORUQgQlkKWU9VIE9SIFRISVJEIFBBUlRJRVMgT1IgQSBGQUlMVVJF -IE9GIFRIRSBQUk9HUkFNIFRPIE9QRVJBVEUgV0lUSCBBTlkgT1RIRVIKUFJPR1JBTVMpLCBFVkVO -IElGIFNVQ0ggSE9MREVSIE9SIE9USEVSIFBBUlRZIEhBUyBCRUVOIEFEVklTRUQgT0YgVEhFClBP -U1NJQklMSVRZIE9GIFNVQ0ggREFNQUdFUy4KCgkJICAgICBFTkQgT0YgVEVSTVMgQU5EIENPTkRJ -VElPTlMKDAoJICAgIEhvdyB0byBBcHBseSBUaGVzZSBUZXJtcyB0byBZb3VyIE5ldyBQcm9ncmFt -cwoKICBJZiB5b3UgZGV2ZWxvcCBhIG5ldyBwcm9ncmFtLCBhbmQgeW91IHdhbnQgaXQgdG8gYmUg -b2YgdGhlIGdyZWF0ZXN0CnBvc3NpYmxlIHVzZSB0byB0aGUgcHVibGljLCB0aGUgYmVzdCB3YXkg -dG8gYWNoaWV2ZSB0aGlzIGlzIHRvIG1ha2UgaXQKZnJlZSBzb2Z0d2FyZSB3aGljaCBldmVyeW9u -ZSBjYW4gcmVkaXN0cmlidXRlIGFuZCBjaGFuZ2UgdW5kZXIgdGhlc2UgdGVybXMuCgogIFRvIGRv -IHNvLCBhdHRhY2ggdGhlIGZvbGxvd2luZyBub3RpY2VzIHRvIHRoZSBwcm9ncmFtLiAgSXQgaXMg -c2FmZXN0CnRvIGF0dGFjaCB0aGVtIHRvIHRoZSBzdGFydCBvZiBlYWNoIHNvdXJjZSBmaWxlIHRv -IG1vc3QgZWZmZWN0aXZlbHkKY29udmV5IHRoZSBleGNsdXNpb24gb2Ygd2FycmFudHk7IGFuZCBl -YWNoIGZpbGUgc2hvdWxkIGhhdmUgYXQgbGVhc3QKdGhlICJjb3B5cmlnaHQiIGxpbmUgYW5kIGEg -cG9pbnRlciB0byB3aGVyZSB0aGUgZnVsbCBub3RpY2UgaXMgZm91bmQuCgogICAgPG9uZSBsaW5l -IHRvIGdpdmUgdGhlIHByb2dyYW0ncyBuYW1lIGFuZCBhIGJyaWVmIGlkZWEgb2Ygd2hhdCBpdCBk -b2VzLj4KICAgIENvcHlyaWdodCAoQykgPHllYXI+ICA8bmFtZSBvZiBhdXRob3I+CgogICAgVGhp -cyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9v -ciBtb2RpZnkKICAgIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGlj -IExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5CiAgICB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9u -OyBlaXRoZXIgdmVyc2lvbiAyIG9mIHRoZSBMaWNlbnNlLCBvcgogICAgKGF0IHlvdXIgb3B0aW9u -KSBhbnkgbGF0ZXIgdmVyc2lvbi4KCiAgICBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4g -dGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKICAgIGJ1dCBXSVRIT1VUIEFOWSBXQVJS -QU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mCiAgICBNRVJDSEFOVEFC -SUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlCiAgICBH -TlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgoKICAgIFlvdSBzaG91 -bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNl -CiAgICBhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBT -b2Z0d2FyZQogICAgRm91bmRhdGlvbiwgSW5jLiwgNTkgVGVtcGxlIFBsYWNlLCBTdWl0ZSAzMzAs -IEJvc3RvbiwgTUEgIDAyMTExLTEzMDcgIFVTQQoKCkFsc28gYWRkIGluZm9ybWF0aW9uIG9uIGhv -dyB0byBjb250YWN0IHlvdSBieSBlbGVjdHJvbmljIGFuZCBwYXBlciBtYWlsLgoKSWYgdGhlIHBy -b2dyYW0gaXMgaW50ZXJhY3RpdmUsIG1ha2UgaXQgb3V0cHV0IGEgc2hvcnQgbm90aWNlIGxpa2Ug -dGhpcwp3aGVuIGl0IHN0YXJ0cyBpbiBhbiBpbnRlcmFjdGl2ZSBtb2RlOgoKICAgIEdub21vdmlz -aW9uIHZlcnNpb24gNjksIENvcHlyaWdodCAoQykgeWVhciBuYW1lIG9mIGF1dGhvcgogICAgR25v -bW92aXNpb24gY29tZXMgd2l0aCBBQlNPTFVURUxZIE5PIFdBUlJBTlRZOyBmb3IgZGV0YWlscyB0 -eXBlIGBzaG93IHcnLgogICAgVGhpcyBpcyBmcmVlIHNvZnR3YXJlLCBhbmQgeW91IGFyZSB3ZWxj -b21lIHRvIHJlZGlzdHJpYnV0ZSBpdAogICAgdW5kZXIgY2VydGFpbiBjb25kaXRpb25zOyB0eXBl -IGBzaG93IGMnIGZvciBkZXRhaWxzLgoKVGhlIGh5cG90aGV0aWNhbCBjb21tYW5kcyBgc2hvdyB3 -JyBhbmQgYHNob3cgYycgc2hvdWxkIHNob3cgdGhlIGFwcHJvcHJpYXRlCnBhcnRzIG9mIHRoZSBH -ZW5lcmFsIFB1YmxpYyBMaWNlbnNlLiAgT2YgY291cnNlLCB0aGUgY29tbWFuZHMgeW91IHVzZSBt -YXkKYmUgY2FsbGVkIHNvbWV0aGluZyBvdGhlciB0aGFuIGBzaG93IHcnIGFuZCBgc2hvdyBjJzsg -dGhleSBjb3VsZCBldmVuIGJlCm1vdXNlLWNsaWNrcyBvciBtZW51IGl0ZW1zLS13aGF0ZXZlciBz -dWl0cyB5b3VyIHByb2dyYW0uCgpZb3Ugc2hvdWxkIGFsc28gZ2V0IHlvdXIgZW1wbG95ZXIgKGlm -IHlvdSB3b3JrIGFzIGEgcHJvZ3JhbW1lcikgb3IgeW91cgpzY2hvb2wsIGlmIGFueSwgdG8gc2ln -biBhICJjb3B5cmlnaHQgZGlzY2xhaW1lciIgZm9yIHRoZSBwcm9ncmFtLCBpZgpuZWNlc3Nhcnku -ICBIZXJlIGlzIGEgc2FtcGxlOyBhbHRlciB0aGUgbmFtZXM6CgogIFlveW9keW5lLCBJbmMuLCBo -ZXJlYnkgZGlzY2xhaW1zIGFsbCBjb3B5cmlnaHQgaW50ZXJlc3QgaW4gdGhlIHByb2dyYW0KICBg -R25vbW92aXNpb24nICh3aGljaCBtYWtlcyBwYXNzZXMgYXQgY29tcGlsZXJzKSB3cml0dGVuIGJ5 -IEphbWVzIEhhY2tlci4KCiAgPHNpZ25hdHVyZSBvZiBUeSBDb29uPiwgMSBBcHJpbCAxOTg5CiAg -VHkgQ29vbiwgUHJlc2lkZW50IG9mIFZpY2UKClRoaXMgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBk -b2VzIG5vdCBwZXJtaXQgaW5jb3Jwb3JhdGluZyB5b3VyIHByb2dyYW0gaW50bwpwcm9wcmlldGFy -eSBwcm9ncmFtcy4gIElmIHlvdXIgcHJvZ3JhbSBpcyBhIHN1YnJvdXRpbmUgbGlicmFyeSwgeW91 -IG1heQpjb25zaWRlciBpdCBtb3JlIHVzZWZ1bCB0byBwZXJtaXQgbGlua2luZyBwcm9wcmlldGFy -eSBhcHBsaWNhdGlvbnMgd2l0aCB0aGUKbGlicmFyeS4gIElmIHRoaXMgaXMgd2hhdCB5b3Ugd2Fu -dCB0byBkbywgdXNlIHRoZSBHTlUgTGlicmFyeSBHZW5lcmFsClB1YmxpYyBMaWNlbnNlIGluc3Rl -YWQgb2YgdGhpcyBMaWNlbnNlLgo= -ClxjaGFwdGVye1RoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZX0KClxiZWdpbntjZW50ZXJ9 -CntccGFyaW5kZW50IDBpbgoKVmVyc2lvbiAyLCBKdW5lIDE5OTEKCkNvcHlyaWdodCBcY29weXJp -Z2h0XCAxOTg5LCAxOTkxIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgSW5jLgoKXGJpZ3NraXAK -CjU5IFRlbXBsZSBQbGFjZSAtIFN1aXRlIDMzMCwgQm9zdG9uLCBNQSAgMDIxMTEtMTMwNywgVVNB -CgpcYmlnc2tpcAoKRXZlcnlvbmUgaXMgcGVybWl0dGVkIHRvIGNvcHkgYW5kIGRpc3RyaWJ1dGUg -dmVyYmF0aW0gY29waWVzCm9mIHRoaXMgbGljZW5zZSBkb2N1bWVudCwgYnV0IGNoYW5naW5nIGl0 -IGlzIG5vdCBhbGxvd2VkLgp9ClxlbmR7Y2VudGVyfQoKXGJlZ2lue2NlbnRlcn0Ke1xiZlxsYXJn -ZSBQcmVhbWJsZX0KXGVuZHtjZW50ZXJ9CgoKVGhlIGxpY2Vuc2VzIGZvciBtb3N0IHNvZnR3YXJl -IGFyZSBkZXNpZ25lZCB0byB0YWtlIGF3YXkgeW91ciBmcmVlZG9tIHRvCnNoYXJlIGFuZCBjaGFu -Z2UgaXQuICBCeSBjb250cmFzdCwgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGlzCmlu -dGVuZGVkIHRvIGd1YXJhbnRlZSB5b3VyIGZyZWVkb20gdG8gc2hhcmUgYW5kIGNoYW5nZSBmcmVl -IHNvZnR3YXJlLS0tdG8KbWFrZSBzdXJlIHRoZSBzb2Z0d2FyZSBpcyBmcmVlIGZvciBhbGwgaXRz -IHVzZXJzLiAgVGhpcyBHZW5lcmFsIFB1YmxpYwpMaWNlbnNlIGFwcGxpZXMgdG8gbW9zdCBvZiB0 -aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uJ3Mgc29mdHdhcmUgYW5kIHRvCmFueSBvdGhlciBw -cm9ncmFtIHdob3NlIGF1dGhvcnMgY29tbWl0IHRvIHVzaW5nIGl0LiAgKFNvbWUgb3RoZXIgRnJl -ZQpTb2Z0d2FyZSBGb3VuZGF0aW9uIHNvZnR3YXJlIGlzIGNvdmVyZWQgYnkgdGhlIEdOVSBMaWJy -YXJ5IEdlbmVyYWwgUHVibGljCkxpY2Vuc2UgaW5zdGVhZC4pICBZb3UgY2FuIGFwcGx5IGl0IHRv -IHlvdXIgcHJvZ3JhbXMsIHRvby4KCldoZW4gd2Ugc3BlYWsgb2YgZnJlZSBzb2Z0d2FyZSwgd2Ug -YXJlIHJlZmVycmluZyB0byBmcmVlZG9tLCBub3QgcHJpY2UuCk91ciBHZW5lcmFsIFB1YmxpYyBM -aWNlbnNlcyBhcmUgZGVzaWduZWQgdG8gbWFrZSBzdXJlIHRoYXQgeW91IGhhdmUgdGhlCmZyZWVk -b20gdG8gZGlzdHJpYnV0ZSBjb3BpZXMgb2YgZnJlZSBzb2Z0d2FyZSAoYW5kIGNoYXJnZSBmb3Ig -dGhpcyBzZXJ2aWNlCmlmIHlvdSB3aXNoKSwgdGhhdCB5b3UgcmVjZWl2ZSBzb3VyY2UgY29kZSBv -ciBjYW4gZ2V0IGl0IGlmIHlvdSB3YW50IGl0LAp0aGF0IHlvdSBjYW4gY2hhbmdlIHRoZSBzb2Z0 -d2FyZSBvciB1c2UgcGllY2VzIG9mIGl0IGluIG5ldyBmcmVlIHByb2dyYW1zOwphbmQgdGhhdCB5 -b3Uga25vdyB5b3UgY2FuIGRvIHRoZXNlIHRoaW5ncy4KClRvIHByb3RlY3QgeW91ciByaWdodHMs -IHdlIG5lZWQgdG8gbWFrZSByZXN0cmljdGlvbnMgdGhhdCBmb3JiaWQgYW55b25lIHRvCmRlbnkg -eW91IHRoZXNlIHJpZ2h0cyBvciB0byBhc2sgeW91IHRvIHN1cnJlbmRlciB0aGUgcmlnaHRzLiAg -VGhlc2UKcmVzdHJpY3Rpb25zIHRyYW5zbGF0ZSB0byBjZXJ0YWluIHJlc3BvbnNpYmlsaXRpZXMg -Zm9yIHlvdSBpZiB5b3UKZGlzdHJpYnV0ZSBjb3BpZXMgb2YgdGhlIHNvZnR3YXJlLCBvciBpZiB5 -b3UgbW9kaWZ5IGl0LgoKRm9yIGV4YW1wbGUsIGlmIHlvdSBkaXN0cmlidXRlIGNvcGllcyBvZiBz -dWNoIGEgcHJvZ3JhbSwgd2hldGhlciBncmF0aXMgb3IKZm9yIGEgZmVlLCB5b3UgbXVzdCBnaXZl -IHRoZSByZWNpcGllbnRzIGFsbCB0aGUgcmlnaHRzIHRoYXQgeW91IGhhdmUuICBZb3UKbXVzdCBt -YWtlIHN1cmUgdGhhdCB0aGV5LCB0b28sIHJlY2VpdmUgb3IgY2FuIGdldCB0aGUgc291cmNlIGNv -ZGUuICBBbmQKeW91IG11c3Qgc2hvdyB0aGVtIHRoZXNlIHRlcm1zIHNvIHRoZXkga25vdyB0aGVp -ciByaWdodHMuCgpXZSBwcm90ZWN0IHlvdXIgcmlnaHRzIHdpdGggdHdvIHN0ZXBzOiAoMSkgY29w -eXJpZ2h0IHRoZSBzb2Z0d2FyZSwgYW5kICgyKQpvZmZlciB5b3UgdGhpcyBsaWNlbnNlIHdoaWNo -IGdpdmVzIHlvdSBsZWdhbCBwZXJtaXNzaW9uIHRvIGNvcHksCmRpc3RyaWJ1dGUgYW5kL29yIG1v -ZGlmeSB0aGUgc29mdHdhcmUuCgpBbHNvLCBmb3IgZWFjaCBhdXRob3IncyBwcm90ZWN0aW9uIGFu -ZCBvdXJzLCB3ZSB3YW50IHRvIG1ha2UgY2VydGFpbiB0aGF0CmV2ZXJ5b25lIHVuZGVyc3RhbmRz -IHRoYXQgdGhlcmUgaXMgbm8gd2FycmFudHkgZm9yIHRoaXMgZnJlZSBzb2Z0d2FyZS4gIElmCnRo -ZSBzb2Z0d2FyZSBpcyBtb2RpZmllZCBieSBzb21lb25lIGVsc2UgYW5kIHBhc3NlZCBvbiwgd2Ug -d2FudCBpdHMKcmVjaXBpZW50cyB0byBrbm93IHRoYXQgd2hhdCB0aGV5IGhhdmUgaXMgbm90IHRo -ZSBvcmlnaW5hbCwgc28gdGhhdCBhbnkKcHJvYmxlbXMgaW50cm9kdWNlZCBieSBvdGhlcnMgd2ls -bCBub3QgcmVmbGVjdCBvbiB0aGUgb3JpZ2luYWwgYXV0aG9ycycKcmVwdXRhdGlvbnMuCgpGaW5h -bGx5LCBhbnkgZnJlZSBwcm9ncmFtIGlzIHRocmVhdGVuZWQgY29uc3RhbnRseSBieSBzb2Z0d2Fy -ZSBwYXRlbnRzLgpXZSB3aXNoIHRvIGF2b2lkIHRoZSBkYW5nZXIgdGhhdCByZWRpc3RyaWJ1dG9y -cyBvZiBhIGZyZWUgcHJvZ3JhbSB3aWxsCmluZGl2aWR1YWxseSBvYnRhaW4gcGF0ZW50IGxpY2Vu -c2VzLCBpbiBlZmZlY3QgbWFraW5nIHRoZSBwcm9ncmFtCnByb3ByaWV0YXJ5LiAgVG8gcHJldmVu -dCB0aGlzLCB3ZSBoYXZlIG1hZGUgaXQgY2xlYXIgdGhhdCBhbnkgcGF0ZW50IG11c3QKYmUgbGlj -ZW5zZWQgZm9yIGV2ZXJ5b25lJ3MgZnJlZSB1c2Ugb3Igbm90IGxpY2Vuc2VkIGF0IGFsbC4KClRo -ZSBwcmVjaXNlIHRlcm1zIGFuZCBjb25kaXRpb25zIGZvciBjb3B5aW5nLCBkaXN0cmlidXRpb24g -YW5kCm1vZGlmaWNhdGlvbiBmb2xsb3cuCgpcYmVnaW57Y2VudGVyfQp7XExhcmdlIFxzYyBUZXJt -cyBhbmQgQ29uZGl0aW9ucyBGb3IgQ29weWluZywgRGlzdHJpYnV0aW9uIGFuZAogIE1vZGlmaWNh -dGlvbn0KXGVuZHtjZW50ZXJ9CgoKJVxyZW5ld2NvbW1hbmR7XHRoZWVudW1pfXtcYWxwaGF7ZW51 -bWl9fQpcYmVnaW57ZW51bWVyYXRlfQoKXGFkZHRvY291bnRlcntlbnVtaX17LTF9CgpcaXRlbSAK -ClRoaXMgTGljZW5zZSBhcHBsaWVzIHRvIGFueSBwcm9ncmFtIG9yIG90aGVyIHdvcmsgd2hpY2gg -Y29udGFpbnMgYSBub3RpY2UKcGxhY2VkIGJ5IHRoZSBjb3B5cmlnaHQgaG9sZGVyIHNheWluZyBp -dCBtYXkgYmUgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlCnRlcm1zIG9mIHRoaXMgR2VuZXJhbCBQdWJs -aWMgTGljZW5zZS4gIFRoZSBgYFByb2dyYW0nJywgYmVsb3csIHJlZmVycyB0bwphbnkgc3VjaCBw -cm9ncmFtIG9yIHdvcmssIGFuZCBhIGBgd29yayBiYXNlZCBvbiB0aGUgUHJvZ3JhbScnIG1lYW5z -IGVpdGhlcgp0aGUgUHJvZ3JhbSBvciBhbnkgZGVyaXZhdGl2ZSB3b3JrIHVuZGVyIGNvcHlyaWdo -dCBsYXc6IHRoYXQgaXMgdG8gc2F5LCBhCndvcmsgY29udGFpbmluZyB0aGUgUHJvZ3JhbSBvciBh -IHBvcnRpb24gb2YgaXQsIGVpdGhlciB2ZXJiYXRpbSBvciB3aXRoCm1vZGlmaWNhdGlvbnMgYW5k -L29yIHRyYW5zbGF0ZWQgaW50byBhbm90aGVyIGxhbmd1YWdlLiAgKEhlcmVpbmFmdGVyLAp0cmFu -c2xhdGlvbiBpcyBpbmNsdWRlZCB3aXRob3V0IGxpbWl0YXRpb24gaW4gdGhlIHRlcm0gYGBtb2Rp -ZmljYXRpb24nJy4pCkVhY2ggbGljZW5zZWUgaXMgYWRkcmVzc2VkIGFzIGBgeW91JycuCgpBY3Rp -dml0aWVzIG90aGVyIHRoYW4gY29weWluZywgZGlzdHJpYnV0aW9uIGFuZCBtb2RpZmljYXRpb24g -YXJlIG5vdApjb3ZlcmVkIGJ5IHRoaXMgTGljZW5zZTsgdGhleSBhcmUgb3V0c2lkZSBpdHMgc2Nv -cGUuICBUaGUgYWN0IG9mCnJ1bm5pbmcgdGhlIFByb2dyYW0gaXMgbm90IHJlc3RyaWN0ZWQsIGFu -ZCB0aGUgb3V0cHV0IGZyb20gdGhlIFByb2dyYW0KaXMgY292ZXJlZCBvbmx5IGlmIGl0cyBjb250 -ZW50cyBjb25zdGl0dXRlIGEgd29yayBiYXNlZCBvbiB0aGUKUHJvZ3JhbSAoaW5kZXBlbmRlbnQg -b2YgaGF2aW5nIGJlZW4gbWFkZSBieSBydW5uaW5nIHRoZSBQcm9ncmFtKS4KV2hldGhlciB0aGF0 -IGlzIHRydWUgZGVwZW5kcyBvbiB3aGF0IHRoZSBQcm9ncmFtIGRvZXMuCgpcaXRlbSBZb3UgbWF5 -IGNvcHkgYW5kIGRpc3RyaWJ1dGUgdmVyYmF0aW0gY29waWVzIG9mIHRoZSBQcm9ncmFtJ3Mgc291 -cmNlCiAgY29kZSBhcyB5b3UgcmVjZWl2ZSBpdCwgaW4gYW55IG1lZGl1bSwgcHJvdmlkZWQgdGhh -dCB5b3UgY29uc3BpY3VvdXNseQogIGFuZCBhcHByb3ByaWF0ZWx5IHB1Ymxpc2ggb24gZWFjaCBj -b3B5IGFuIGFwcHJvcHJpYXRlIGNvcHlyaWdodCBub3RpY2UKICBhbmQgZGlzY2xhaW1lciBvZiB3 -YXJyYW50eTsga2VlcCBpbnRhY3QgYWxsIHRoZSBub3RpY2VzIHRoYXQgcmVmZXIgdG8KICB0aGlz -IExpY2Vuc2UgYW5kIHRvIHRoZSBhYnNlbmNlIG9mIGFueSB3YXJyYW50eTsgYW5kIGdpdmUgYW55 -IG90aGVyCiAgcmVjaXBpZW50cyBvZiB0aGUgUHJvZ3JhbSBhIGNvcHkgb2YgdGhpcyBMaWNlbnNl -IGFsb25nIHdpdGggdGhlIFByb2dyYW0uCgpZb3UgbWF5IGNoYXJnZSBhIGZlZSBmb3IgdGhlIHBo -eXNpY2FsIGFjdCBvZiB0cmFuc2ZlcnJpbmcgYSBjb3B5LCBhbmQgeW91Cm1heSBhdCB5b3VyIG9w -dGlvbiBvZmZlciB3YXJyYW50eSBwcm90ZWN0aW9uIGluIGV4Y2hhbmdlIGZvciBhIGZlZS4KClxp -dGVtCgpZb3UgbWF5IG1vZGlmeSB5b3VyIGNvcHkgb3IgY29waWVzIG9mIHRoZSBQcm9ncmFtIG9y -IGFueSBwb3J0aW9uCm9mIGl0LCB0aHVzIGZvcm1pbmcgYSB3b3JrIGJhc2VkIG9uIHRoZSBQcm9n -cmFtLCBhbmQgY29weSBhbmQKZGlzdHJpYnV0ZSBzdWNoIG1vZGlmaWNhdGlvbnMgb3Igd29yayB1 -bmRlciB0aGUgdGVybXMgb2YgU2VjdGlvbiAxCmFib3ZlLCBwcm92aWRlZCB0aGF0IHlvdSBhbHNv -IG1lZXQgYWxsIG9mIHRoZXNlIGNvbmRpdGlvbnM6CgpcYmVnaW57ZW51bWVyYXRlfQoKXGl0ZW0g -CgpZb3UgbXVzdCBjYXVzZSB0aGUgbW9kaWZpZWQgZmlsZXMgdG8gY2FycnkgcHJvbWluZW50IG5v -dGljZXMgc3RhdGluZyB0aGF0CnlvdSBjaGFuZ2VkIHRoZSBmaWxlcyBhbmQgdGhlIGRhdGUgb2Yg -YW55IGNoYW5nZS4KClxpdGVtCgpZb3UgbXVzdCBjYXVzZSBhbnkgd29yayB0aGF0IHlvdSBkaXN0 -cmlidXRlIG9yIHB1Ymxpc2gsIHRoYXQgaW4Kd2hvbGUgb3IgaW4gcGFydCBjb250YWlucyBvciBp -cyBkZXJpdmVkIGZyb20gdGhlIFByb2dyYW0gb3IgYW55CnBhcnQgdGhlcmVvZiwgdG8gYmUgbGlj -ZW5zZWQgYXMgYSB3aG9sZSBhdCBubyBjaGFyZ2UgdG8gYWxsIHRoaXJkCnBhcnRpZXMgdW5kZXIg -dGhlIHRlcm1zIG9mIHRoaXMgTGljZW5zZS4KClxpdGVtCklmIHRoZSBtb2RpZmllZCBwcm9ncmFt -IG5vcm1hbGx5IHJlYWRzIGNvbW1hbmRzIGludGVyYWN0aXZlbHkKd2hlbiBydW4sIHlvdSBtdXN0 -IGNhdXNlIGl0LCB3aGVuIHN0YXJ0ZWQgcnVubmluZyBmb3Igc3VjaAppbnRlcmFjdGl2ZSB1c2Ug -aW4gdGhlIG1vc3Qgb3JkaW5hcnkgd2F5LCB0byBwcmludCBvciBkaXNwbGF5IGFuCmFubm91bmNl -bWVudCBpbmNsdWRpbmcgYW4gYXBwcm9wcmlhdGUgY29weXJpZ2h0IG5vdGljZSBhbmQgYQpub3Rp -Y2UgdGhhdCB0aGVyZSBpcyBubyB3YXJyYW50eSAob3IgZWxzZSwgc2F5aW5nIHRoYXQgeW91IHBy -b3ZpZGUKYSB3YXJyYW50eSkgYW5kIHRoYXQgdXNlcnMgbWF5IHJlZGlzdHJpYnV0ZSB0aGUgcHJv -Z3JhbSB1bmRlcgp0aGVzZSBjb25kaXRpb25zLCBhbmQgdGVsbGluZyB0aGUgdXNlciBob3cgdG8g -dmlldyBhIGNvcHkgb2YgdGhpcwpMaWNlbnNlLiAgKEV4Y2VwdGlvbjogaWYgdGhlIFByb2dyYW0g -aXRzZWxmIGlzIGludGVyYWN0aXZlIGJ1dApkb2VzIG5vdCBub3JtYWxseSBwcmludCBzdWNoIGFu -IGFubm91bmNlbWVudCwgeW91ciB3b3JrIGJhc2VkIG9uCnRoZSBQcm9ncmFtIGlzIG5vdCByZXF1 -aXJlZCB0byBwcmludCBhbiBhbm5vdW5jZW1lbnQuKQoKXGVuZHtlbnVtZXJhdGV9CgoKVGhlc2Ug -cmVxdWlyZW1lbnRzIGFwcGx5IHRvIHRoZSBtb2RpZmllZCB3b3JrIGFzIGEgd2hvbGUuICBJZgpp -ZGVudGlmaWFibGUgc2VjdGlvbnMgb2YgdGhhdCB3b3JrIGFyZSBub3QgZGVyaXZlZCBmcm9tIHRo -ZSBQcm9ncmFtLAphbmQgY2FuIGJlIHJlYXNvbmFibHkgY29uc2lkZXJlZCBpbmRlcGVuZGVudCBh -bmQgc2VwYXJhdGUgd29ya3MgaW4KdGhlbXNlbHZlcywgdGhlbiB0aGlzIExpY2Vuc2UsIGFuZCBp -dHMgdGVybXMsIGRvIG5vdCBhcHBseSB0byB0aG9zZQpzZWN0aW9ucyB3aGVuIHlvdSBkaXN0cmli -dXRlIHRoZW0gYXMgc2VwYXJhdGUgd29ya3MuICBCdXQgd2hlbiB5b3UKZGlzdHJpYnV0ZSB0aGUg -c2FtZSBzZWN0aW9ucyBhcyBwYXJ0IG9mIGEgd2hvbGUgd2hpY2ggaXMgYSB3b3JrIGJhc2VkCm9u -IHRoZSBQcm9ncmFtLCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB3aG9sZSBtdXN0IGJlIG9uIHRo -ZSB0ZXJtcyBvZgp0aGlzIExpY2Vuc2UsIHdob3NlIHBlcm1pc3Npb25zIGZvciBvdGhlciBsaWNl -bnNlZXMgZXh0ZW5kIHRvIHRoZQplbnRpcmUgd2hvbGUsIGFuZCB0aHVzIHRvIGVhY2ggYW5kIGV2 -ZXJ5IHBhcnQgcmVnYXJkbGVzcyBvZiB3aG8gd3JvdGUgaXQuCgpUaHVzLCBpdCBpcyBub3QgdGhl -IGludGVudCBvZiB0aGlzIHNlY3Rpb24gdG8gY2xhaW0gcmlnaHRzIG9yIGNvbnRlc3QKeW91ciBy -aWdodHMgdG8gd29yayB3cml0dGVuIGVudGlyZWx5IGJ5IHlvdTsgcmF0aGVyLCB0aGUgaW50ZW50 -IGlzIHRvCmV4ZXJjaXNlIHRoZSByaWdodCB0byBjb250cm9sIHRoZSBkaXN0cmlidXRpb24gb2Yg -ZGVyaXZhdGl2ZSBvcgpjb2xsZWN0aXZlIHdvcmtzIGJhc2VkIG9uIHRoZSBQcm9ncmFtLgoKSW4g -YWRkaXRpb24sIG1lcmUgYWdncmVnYXRpb24gb2YgYW5vdGhlciB3b3JrIG5vdCBiYXNlZCBvbiB0 -aGUgUHJvZ3JhbQp3aXRoIHRoZSBQcm9ncmFtIChvciB3aXRoIGEgd29yayBiYXNlZCBvbiB0aGUg -UHJvZ3JhbSkgb24gYSB2b2x1bWUgb2YKYSBzdG9yYWdlIG9yIGRpc3RyaWJ1dGlvbiBtZWRpdW0g -ZG9lcyBub3QgYnJpbmcgdGhlIG90aGVyIHdvcmsgdW5kZXIKdGhlIHNjb3BlIG9mIHRoaXMgTGlj -ZW5zZS4KClxpdGVtCllvdSBtYXkgY29weSBhbmQgZGlzdHJpYnV0ZSB0aGUgUHJvZ3JhbSAob3Ig -YSB3b3JrIGJhc2VkIG9uIGl0LAp1bmRlciBTZWN0aW9uIDIpIGluIG9iamVjdCBjb2RlIG9yIGV4 -ZWN1dGFibGUgZm9ybSB1bmRlciB0aGUgdGVybXMgb2YKU2VjdGlvbnMgMSBhbmQgMiBhYm92ZSBw -cm92aWRlZCB0aGF0IHlvdSBhbHNvIGRvIG9uZSBvZiB0aGUgZm9sbG93aW5nOgoKXGJlZ2lue2Vu -dW1lcmF0ZX0KClxpdGVtCgpBY2NvbXBhbnkgaXQgd2l0aCB0aGUgY29tcGxldGUgY29ycmVzcG9u -ZGluZyBtYWNoaW5lLXJlYWRhYmxlCnNvdXJjZSBjb2RlLCB3aGljaCBtdXN0IGJlIGRpc3RyaWJ1 -dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiBTZWN0aW9ucwoxIGFuZCAyIGFib3ZlIG9uIGEgbWVkaXVt -IGN1c3RvbWFyaWx5IHVzZWQgZm9yIHNvZnR3YXJlIGludGVyY2hhbmdlOyBvciwKClxpdGVtCgpB -Y2NvbXBhbnkgaXQgd2l0aCBhIHdyaXR0ZW4gb2ZmZXIsIHZhbGlkIGZvciBhdCBsZWFzdCB0aHJl -ZQp5ZWFycywgdG8gZ2l2ZSBhbnkgdGhpcmQgcGFydHksIGZvciBhIGNoYXJnZSBubyBtb3JlIHRo -YW4geW91cgpjb3N0IG9mIHBoeXNpY2FsbHkgcGVyZm9ybWluZyBzb3VyY2UgZGlzdHJpYnV0aW9u -LCBhIGNvbXBsZXRlCm1hY2hpbmUtcmVhZGFibGUgY29weSBvZiB0aGUgY29ycmVzcG9uZGluZyBz -b3VyY2UgY29kZSwgdG8gYmUKZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIHRlcm1zIG9mIFNlY3Rpb25z -IDEgYW5kIDIgYWJvdmUgb24gYSBtZWRpdW0KY3VzdG9tYXJpbHkgdXNlZCBmb3Igc29mdHdhcmUg -aW50ZXJjaGFuZ2U7IG9yLAoKXGl0ZW0KCkFjY29tcGFueSBpdCB3aXRoIHRoZSBpbmZvcm1hdGlv -biB5b3UgcmVjZWl2ZWQgYXMgdG8gdGhlIG9mZmVyCnRvIGRpc3RyaWJ1dGUgY29ycmVzcG9uZGlu -ZyBzb3VyY2UgY29kZS4gIChUaGlzIGFsdGVybmF0aXZlIGlzCmFsbG93ZWQgb25seSBmb3Igbm9u -Y29tbWVyY2lhbCBkaXN0cmlidXRpb24gYW5kIG9ubHkgaWYgeW91CnJlY2VpdmVkIHRoZSBwcm9n -cmFtIGluIG9iamVjdCBjb2RlIG9yIGV4ZWN1dGFibGUgZm9ybSB3aXRoIHN1Y2gKYW4gb2ZmZXIs -IGluIGFjY29yZCB3aXRoIFN1YnNlY3Rpb24gYiBhYm92ZS4pCgpcZW5ke2VudW1lcmF0ZX0KCgpU -aGUgc291cmNlIGNvZGUgZm9yIGEgd29yayBtZWFucyB0aGUgcHJlZmVycmVkIGZvcm0gb2YgdGhl -IHdvcmsgZm9yCm1ha2luZyBtb2RpZmljYXRpb25zIHRvIGl0LiAgRm9yIGFuIGV4ZWN1dGFibGUg -d29yaywgY29tcGxldGUgc291cmNlCmNvZGUgbWVhbnMgYWxsIHRoZSBzb3VyY2UgY29kZSBmb3Ig -YWxsIG1vZHVsZXMgaXQgY29udGFpbnMsIHBsdXMgYW55CmFzc29jaWF0ZWQgaW50ZXJmYWNlIGRl -ZmluaXRpb24gZmlsZXMsIHBsdXMgdGhlIHNjcmlwdHMgdXNlZCB0bwpjb250cm9sIGNvbXBpbGF0 -aW9uIGFuZCBpbnN0YWxsYXRpb24gb2YgdGhlIGV4ZWN1dGFibGUuICBIb3dldmVyLCBhcyBhCnNw -ZWNpYWwgZXhjZXB0aW9uLCB0aGUgc291cmNlIGNvZGUgZGlzdHJpYnV0ZWQgbmVlZCBub3QgaW5j -bHVkZQphbnl0aGluZyB0aGF0IGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIChpbiBlaXRoZXIgc291 -cmNlIG9yIGJpbmFyeQpmb3JtKSB3aXRoIHRoZSBtYWpvciBjb21wb25lbnRzIChjb21waWxlciwg -a2VybmVsLCBhbmQgc28gb24pIG9mIHRoZQpvcGVyYXRpbmcgc3lzdGVtIG9uIHdoaWNoIHRoZSBl -eGVjdXRhYmxlIHJ1bnMsIHVubGVzcyB0aGF0IGNvbXBvbmVudAppdHNlbGYgYWNjb21wYW5pZXMg -dGhlIGV4ZWN1dGFibGUuCgpJZiBkaXN0cmlidXRpb24gb2YgZXhlY3V0YWJsZSBvciBvYmplY3Qg -Y29kZSBpcyBtYWRlIGJ5IG9mZmVyaW5nCmFjY2VzcyB0byBjb3B5IGZyb20gYSBkZXNpZ25hdGVk -IHBsYWNlLCB0aGVuIG9mZmVyaW5nIGVxdWl2YWxlbnQKYWNjZXNzIHRvIGNvcHkgdGhlIHNvdXJj -ZSBjb2RlIGZyb20gdGhlIHNhbWUgcGxhY2UgY291bnRzIGFzCmRpc3RyaWJ1dGlvbiBvZiB0aGUg -c291cmNlIGNvZGUsIGV2ZW4gdGhvdWdoIHRoaXJkIHBhcnRpZXMgYXJlIG5vdApjb21wZWxsZWQg -dG8gY29weSB0aGUgc291cmNlIGFsb25nIHdpdGggdGhlIG9iamVjdCBjb2RlLgoKXGl0ZW0KWW91 -IG1heSBub3QgY29weSwgbW9kaWZ5LCBzdWJsaWNlbnNlLCBvciBkaXN0cmlidXRlIHRoZSBQcm9n -cmFtCmV4Y2VwdCBhcyBleHByZXNzbHkgcHJvdmlkZWQgdW5kZXIgdGhpcyBMaWNlbnNlLiAgQW55 -IGF0dGVtcHQKb3RoZXJ3aXNlIHRvIGNvcHksIG1vZGlmeSwgc3VibGljZW5zZSBvciBkaXN0cmli -dXRlIHRoZSBQcm9ncmFtIGlzCnZvaWQsIGFuZCB3aWxsIGF1dG9tYXRpY2FsbHkgdGVybWluYXRl -IHlvdXIgcmlnaHRzIHVuZGVyIHRoaXMgTGljZW5zZS4KSG93ZXZlciwgcGFydGllcyB3aG8gaGF2 -ZSByZWNlaXZlZCBjb3BpZXMsIG9yIHJpZ2h0cywgZnJvbSB5b3UgdW5kZXIKdGhpcyBMaWNlbnNl -IHdpbGwgbm90IGhhdmUgdGhlaXIgbGljZW5zZXMgdGVybWluYXRlZCBzbyBsb25nIGFzIHN1Y2gK -cGFydGllcyByZW1haW4gaW4gZnVsbCBjb21wbGlhbmNlLgoKXGl0ZW0KWW91IGFyZSBub3QgcmVx -dWlyZWQgdG8gYWNjZXB0IHRoaXMgTGljZW5zZSwgc2luY2UgeW91IGhhdmUgbm90CnNpZ25lZCBp -dC4gIEhvd2V2ZXIsIG5vdGhpbmcgZWxzZSBncmFudHMgeW91IHBlcm1pc3Npb24gdG8gbW9kaWZ5 -IG9yCmRpc3RyaWJ1dGUgdGhlIFByb2dyYW0gb3IgaXRzIGRlcml2YXRpdmUgd29ya3MuICBUaGVz -ZSBhY3Rpb25zIGFyZQpwcm9oaWJpdGVkIGJ5IGxhdyBpZiB5b3UgZG8gbm90IGFjY2VwdCB0aGlz -IExpY2Vuc2UuICBUaGVyZWZvcmUsIGJ5Cm1vZGlmeWluZyBvciBkaXN0cmlidXRpbmcgdGhlIFBy -b2dyYW0gKG9yIGFueSB3b3JrIGJhc2VkIG9uIHRoZQpQcm9ncmFtKSwgeW91IGluZGljYXRlIHlv -dXIgYWNjZXB0YW5jZSBvZiB0aGlzIExpY2Vuc2UgdG8gZG8gc28sIGFuZAphbGwgaXRzIHRlcm1z -IGFuZCBjb25kaXRpb25zIGZvciBjb3B5aW5nLCBkaXN0cmlidXRpbmcgb3IgbW9kaWZ5aW5nCnRo -ZSBQcm9ncmFtIG9yIHdvcmtzIGJhc2VkIG9uIGl0LgoKXGl0ZW0KRWFjaCB0aW1lIHlvdSByZWRp -c3RyaWJ1dGUgdGhlIFByb2dyYW0gKG9yIGFueSB3b3JrIGJhc2VkIG9uIHRoZQpQcm9ncmFtKSwg -dGhlIHJlY2lwaWVudCBhdXRvbWF0aWNhbGx5IHJlY2VpdmVzIGEgbGljZW5zZSBmcm9tIHRoZQpv -cmlnaW5hbCBsaWNlbnNvciB0byBjb3B5LCBkaXN0cmlidXRlIG9yIG1vZGlmeSB0aGUgUHJvZ3Jh -bSBzdWJqZWN0IHRvCnRoZXNlIHRlcm1zIGFuZCBjb25kaXRpb25zLiAgWW91IG1heSBub3QgaW1w -b3NlIGFueSBmdXJ0aGVyCnJlc3RyaWN0aW9ucyBvbiB0aGUgcmVjaXBpZW50cycgZXhlcmNpc2Ug -b2YgdGhlIHJpZ2h0cyBncmFudGVkIGhlcmVpbi4KWW91IGFyZSBub3QgcmVzcG9uc2libGUgZm9y -IGVuZm9yY2luZyBjb21wbGlhbmNlIGJ5IHRoaXJkIHBhcnRpZXMgdG8KdGhpcyBMaWNlbnNlLgoK -XGl0ZW0KSWYsIGFzIGEgY29uc2VxdWVuY2Ugb2YgYSBjb3VydCBqdWRnbWVudCBvciBhbGxlZ2F0 -aW9uIG9mIHBhdGVudAppbmZyaW5nZW1lbnQgb3IgZm9yIGFueSBvdGhlciByZWFzb24gKG5vdCBs -aW1pdGVkIHRvIHBhdGVudCBpc3N1ZXMpLApjb25kaXRpb25zIGFyZSBpbXBvc2VkIG9uIHlvdSAo -d2hldGhlciBieSBjb3VydCBvcmRlciwgYWdyZWVtZW50IG9yCm90aGVyd2lzZSkgdGhhdCBjb250 -cmFkaWN0IHRoZSBjb25kaXRpb25zIG9mIHRoaXMgTGljZW5zZSwgdGhleSBkbyBub3QKZXhjdXNl -IHlvdSBmcm9tIHRoZSBjb25kaXRpb25zIG9mIHRoaXMgTGljZW5zZS4gIElmIHlvdSBjYW5ub3QK -ZGlzdHJpYnV0ZSBzbyBhcyB0byBzYXRpc2Z5IHNpbXVsdGFuZW91c2x5IHlvdXIgb2JsaWdhdGlv -bnMgdW5kZXIgdGhpcwpMaWNlbnNlIGFuZCBhbnkgb3RoZXIgcGVydGluZW50IG9ibGlnYXRpb25z -LCB0aGVuIGFzIGEgY29uc2VxdWVuY2UgeW91Cm1heSBub3QgZGlzdHJpYnV0ZSB0aGUgUHJvZ3Jh -bSBhdCBhbGwuICBGb3IgZXhhbXBsZSwgaWYgYSBwYXRlbnQKbGljZW5zZSB3b3VsZCBub3QgcGVy -bWl0IHJveWFsdHktZnJlZSByZWRpc3RyaWJ1dGlvbiBvZiB0aGUgUHJvZ3JhbSBieQphbGwgdGhv -c2Ugd2hvIHJlY2VpdmUgY29waWVzIGRpcmVjdGx5IG9yIGluZGlyZWN0bHkgdGhyb3VnaCB5b3Us -IHRoZW4KdGhlIG9ubHkgd2F5IHlvdSBjb3VsZCBzYXRpc2Z5IGJvdGggaXQgYW5kIHRoaXMgTGlj -ZW5zZSB3b3VsZCBiZSB0bwpyZWZyYWluIGVudGlyZWx5IGZyb20gZGlzdHJpYnV0aW9uIG9mIHRo -ZSBQcm9ncmFtLgoKSWYgYW55IHBvcnRpb24gb2YgdGhpcyBzZWN0aW9uIGlzIGhlbGQgaW52YWxp -ZCBvciB1bmVuZm9yY2VhYmxlIHVuZGVyCmFueSBwYXJ0aWN1bGFyIGNpcmN1bXN0YW5jZSwgdGhl -IGJhbGFuY2Ugb2YgdGhlIHNlY3Rpb24gaXMgaW50ZW5kZWQgdG8KYXBwbHkgYW5kIHRoZSBzZWN0 -aW9uIGFzIGEgd2hvbGUgaXMgaW50ZW5kZWQgdG8gYXBwbHkgaW4gb3RoZXIKY2lyY3Vtc3RhbmNl -cy4KCkl0IGlzIG5vdCB0aGUgcHVycG9zZSBvZiB0aGlzIHNlY3Rpb24gdG8gaW5kdWNlIHlvdSB0 -byBpbmZyaW5nZSBhbnkKcGF0ZW50cyBvciBvdGhlciBwcm9wZXJ0eSByaWdodCBjbGFpbXMgb3Ig -dG8gY29udGVzdCB2YWxpZGl0eSBvZiBhbnkKc3VjaCBjbGFpbXM7IHRoaXMgc2VjdGlvbiBoYXMg -dGhlIHNvbGUgcHVycG9zZSBvZiBwcm90ZWN0aW5nIHRoZQppbnRlZ3JpdHkgb2YgdGhlIGZyZWUg -c29mdHdhcmUgZGlzdHJpYnV0aW9uIHN5c3RlbSwgd2hpY2ggaXMKaW1wbGVtZW50ZWQgYnkgcHVi -bGljIGxpY2Vuc2UgcHJhY3RpY2VzLiAgTWFueSBwZW9wbGUgaGF2ZSBtYWRlCmdlbmVyb3VzIGNv -bnRyaWJ1dGlvbnMgdG8gdGhlIHdpZGUgcmFuZ2Ugb2Ygc29mdHdhcmUgZGlzdHJpYnV0ZWQKdGhy -b3VnaCB0aGF0IHN5c3RlbSBpbiByZWxpYW5jZSBvbiBjb25zaXN0ZW50IGFwcGxpY2F0aW9uIG9m -IHRoYXQKc3lzdGVtOyBpdCBpcyB1cCB0byB0aGUgYXV0aG9yL2Rvbm9yIHRvIGRlY2lkZSBpZiBo -ZSBvciBzaGUgaXMgd2lsbGluZwp0byBkaXN0cmlidXRlIHNvZnR3YXJlIHRocm91Z2ggYW55IG90 -aGVyIHN5c3RlbSBhbmQgYSBsaWNlbnNlZSBjYW5ub3QKaW1wb3NlIHRoYXQgY2hvaWNlLgoKVGhp -cyBzZWN0aW9uIGlzIGludGVuZGVkIHRvIG1ha2UgdGhvcm91Z2hseSBjbGVhciB3aGF0IGlzIGJl -bGlldmVkIHRvCmJlIGEgY29uc2VxdWVuY2Ugb2YgdGhlIHJlc3Qgb2YgdGhpcyBMaWNlbnNlLgoK -XGl0ZW0KSWYgdGhlIGRpc3RyaWJ1dGlvbiBhbmQvb3IgdXNlIG9mIHRoZSBQcm9ncmFtIGlzIHJl -c3RyaWN0ZWQgaW4KY2VydGFpbiBjb3VudHJpZXMgZWl0aGVyIGJ5IHBhdGVudHMgb3IgYnkgY29w -eXJpZ2h0ZWQgaW50ZXJmYWNlcywgdGhlCm9yaWdpbmFsIGNvcHlyaWdodCBob2xkZXIgd2hvIHBs -YWNlcyB0aGUgUHJvZ3JhbSB1bmRlciB0aGlzIExpY2Vuc2UKbWF5IGFkZCBhbiBleHBsaWNpdCBn -ZW9ncmFwaGljYWwgZGlzdHJpYnV0aW9uIGxpbWl0YXRpb24gZXhjbHVkaW5nCnRob3NlIGNvdW50 -cmllcywgc28gdGhhdCBkaXN0cmlidXRpb24gaXMgcGVybWl0dGVkIG9ubHkgaW4gb3IgYW1vbmcK -Y291bnRyaWVzIG5vdCB0aHVzIGV4Y2x1ZGVkLiAgSW4gc3VjaCBjYXNlLCB0aGlzIExpY2Vuc2Ug -aW5jb3Jwb3JhdGVzCnRoZSBsaW1pdGF0aW9uIGFzIGlmIHdyaXR0ZW4gaW4gdGhlIGJvZHkgb2Yg -dGhpcyBMaWNlbnNlLgoKXGl0ZW0KVGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiBtYXkgcHVi -bGlzaCByZXZpc2VkIGFuZC9vciBuZXcgdmVyc2lvbnMKb2YgdGhlIEdlbmVyYWwgUHVibGljIExp -Y2Vuc2UgZnJvbSB0aW1lIHRvIHRpbWUuICBTdWNoIG5ldyB2ZXJzaW9ucyB3aWxsCmJlIHNpbWls -YXIgaW4gc3Bpcml0IHRvIHRoZSBwcmVzZW50IHZlcnNpb24sIGJ1dCBtYXkgZGlmZmVyIGluIGRl -dGFpbCB0bwphZGRyZXNzIG5ldyBwcm9ibGVtcyBvciBjb25jZXJucy4KCkVhY2ggdmVyc2lvbiBp -cyBnaXZlbiBhIGRpc3Rpbmd1aXNoaW5nIHZlcnNpb24gbnVtYmVyLiAgSWYgdGhlIFByb2dyYW0K -c3BlY2lmaWVzIGEgdmVyc2lvbiBudW1iZXIgb2YgdGhpcyBMaWNlbnNlIHdoaWNoIGFwcGxpZXMg -dG8gaXQgYW5kIGBgYW55CmxhdGVyIHZlcnNpb24nJywgeW91IGhhdmUgdGhlIG9wdGlvbiBvZiBm -b2xsb3dpbmcgdGhlIHRlcm1zIGFuZCBjb25kaXRpb25zCmVpdGhlciBvZiB0aGF0IHZlcnNpb24g -b3Igb2YgYW55IGxhdGVyIHZlcnNpb24gcHVibGlzaGVkIGJ5IHRoZSBGcmVlClNvZnR3YXJlIEZv -dW5kYXRpb24uICBJZiB0aGUgUHJvZ3JhbSBkb2VzIG5vdCBzcGVjaWZ5IGEgdmVyc2lvbiBudW1i -ZXIgb2YKdGhpcyBMaWNlbnNlLCB5b3UgbWF5IGNob29zZSBhbnkgdmVyc2lvbiBldmVyIHB1Ymxp -c2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZQpGb3VuZGF0aW9uLgoKXGl0ZW0KSWYgeW91IHdpc2gg -dG8gaW5jb3Jwb3JhdGUgcGFydHMgb2YgdGhlIFByb2dyYW0gaW50byBvdGhlciBmcmVlCnByb2dy -YW1zIHdob3NlIGRpc3RyaWJ1dGlvbiBjb25kaXRpb25zIGFyZSBkaWZmZXJlbnQsIHdyaXRlIHRv -IHRoZSBhdXRob3IKdG8gYXNrIGZvciBwZXJtaXNzaW9uLiAgRm9yIHNvZnR3YXJlIHdoaWNoIGlz -IGNvcHlyaWdodGVkIGJ5IHRoZSBGcmVlClNvZnR3YXJlIEZvdW5kYXRpb24sIHdyaXRlIHRvIHRo -ZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IHdlIHNvbWV0aW1lcwptYWtlIGV4Y2VwdGlvbnMg -Zm9yIHRoaXMuICBPdXIgZGVjaXNpb24gd2lsbCBiZSBndWlkZWQgYnkgdGhlIHR3byBnb2Fscwpv -ZiBwcmVzZXJ2aW5nIHRoZSBmcmVlIHN0YXR1cyBvZiBhbGwgZGVyaXZhdGl2ZXMgb2Ygb3VyIGZy -ZWUgc29mdHdhcmUgYW5kCm9mIHByb21vdGluZyB0aGUgc2hhcmluZyBhbmQgcmV1c2Ugb2Ygc29m -dHdhcmUgZ2VuZXJhbGx5LgoKXGJlZ2lue2NlbnRlcn0Ke1xMYXJnZVxzYwpObyBXYXJyYW50eQp9 -ClxlbmR7Y2VudGVyfQoKXGl0ZW0Ke1xzYyBCZWNhdXNlIHRoZSBwcm9ncmFtIGlzIGxpY2Vuc2Vk -IGZyZWUgb2YgY2hhcmdlLCB0aGVyZSBpcyBubyB3YXJyYW50eQpmb3IgdGhlIHByb2dyYW0sIHRv -IHRoZSBleHRlbnQgcGVybWl0dGVkIGJ5IGFwcGxpY2FibGUgbGF3LiAgRXhjZXB0IHdoZW4Kb3Ro -ZXJ3aXNlIHN0YXRlZCBpbiB3cml0aW5nIHRoZSBjb3B5cmlnaHQgaG9sZGVycyBhbmQvb3Igb3Ro -ZXIgcGFydGllcwpwcm92aWRlIHRoZSBwcm9ncmFtIGBgYXMgaXMnJyB3aXRob3V0IHdhcnJhbnR5 -IG9mIGFueSBraW5kLCBlaXRoZXIgZXhwcmVzc2VkCm9yIGltcGxpZWQsIGluY2x1ZGluZywgYnV0 -IG5vdCBsaW1pdGVkIHRvLCB0aGUgaW1wbGllZCB3YXJyYW50aWVzIG9mCm1lcmNoYW50YWJpbGl0 -eSBhbmQgZml0bmVzcyBmb3IgYSBwYXJ0aWN1bGFyIHB1cnBvc2UuICBUaGUgZW50aXJlIHJpc2sg -YXMKdG8gdGhlIHF1YWxpdHkgYW5kIHBlcmZvcm1hbmNlIG9mIHRoZSBwcm9ncmFtIGlzIHdpdGgg -eW91LiAgU2hvdWxkIHRoZQpwcm9ncmFtIHByb3ZlIGRlZmVjdGl2ZSwgeW91IGFzc3VtZSB0aGUg -Y29zdCBvZiBhbGwgbmVjZXNzYXJ5IHNlcnZpY2luZywKcmVwYWlyIG9yIGNvcnJlY3Rpb24ufQoK -XGl0ZW0Ke1xzYyBJbiBubyBldmVudCB1bmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcg -b3IgYWdyZWVkIHRvIGluIHdyaXRpbmcKd2lsbCBhbnkgY29weXJpZ2h0IGhvbGRlciwgb3IgYW55 -IG90aGVyIHBhcnR5IHdobyBtYXkgbW9kaWZ5IGFuZC9vcgpyZWRpc3RyaWJ1dGUgdGhlIHByb2dy -YW0gYXMgcGVybWl0dGVkIGFib3ZlLCBiZSBsaWFibGUgdG8geW91IGZvciBkYW1hZ2VzLAppbmNs -dWRpbmcgYW55IGdlbmVyYWwsIHNwZWNpYWwsIGluY2lkZW50YWwgb3IgY29uc2VxdWVudGlhbCBk -YW1hZ2VzIGFyaXNpbmcKb3V0IG9mIHRoZSB1c2Ugb3IgaW5hYmlsaXR5IHRvIHVzZSB0aGUgcHJv -Z3JhbSAoaW5jbHVkaW5nIGJ1dCBub3QgbGltaXRlZAp0byBsb3NzIG9mIGRhdGEgb3IgZGF0YSBi -ZWluZyByZW5kZXJlZCBpbmFjY3VyYXRlIG9yIGxvc3NlcyBzdXN0YWluZWQgYnkKeW91IG9yIHRo -aXJkIHBhcnRpZXMgb3IgYSBmYWlsdXJlIG9mIHRoZSBwcm9ncmFtIHRvIG9wZXJhdGUgd2l0aCBh -bnkgb3RoZXIKcHJvZ3JhbXMpLCBldmVuIGlmIHN1Y2ggaG9sZGVyIG9yIG90aGVyIHBhcnR5IGhh -cyBiZWVuIGFkdmlzZWQgb2YgdGhlCnBvc3NpYmlsaXR5IG9mIHN1Y2ggZGFtYWdlcy59CgpcZW5k -e2VudW1lcmF0ZX0KCgpcYmVnaW57Y2VudGVyfQp7XExhcmdlXHNjIEVuZCBvZiBUZXJtcyBhbmQg -Q29uZGl0aW9uc30KXGVuZHtjZW50ZXJ9CgoKXHBhZ2VicmVha1syXQoKXHNlY3Rpb24qe0FwcGVu -ZGl4OiBIb3cgdG8gQXBwbHkgVGhlc2UgVGVybXMgdG8gWW91ciBOZXcgUHJvZ3JhbXN9CgpJZiB5 -b3UgZGV2ZWxvcCBhIG5ldyBwcm9ncmFtLCBhbmQgeW91IHdhbnQgaXQgdG8gYmUgb2YgdGhlIGdy -ZWF0ZXN0CnBvc3NpYmxlIHVzZSB0byB0aGUgcHVibGljLCB0aGUgYmVzdCB3YXkgdG8gYWNoaWV2 -ZSB0aGlzIGlzIHRvIG1ha2UgaXQKZnJlZSBzb2Z0d2FyZSB3aGljaCBldmVyeW9uZSBjYW4gcmVk -aXN0cmlidXRlIGFuZCBjaGFuZ2UgdW5kZXIgdGhlc2UKdGVybXMuCgogIFRvIGRvIHNvLCBhdHRh -Y2ggdGhlIGZvbGxvd2luZyBub3RpY2VzIHRvIHRoZSBwcm9ncmFtLiAgSXQgaXMgc2FmZXN0IHRv -CiAgYXR0YWNoIHRoZW0gdG8gdGhlIHN0YXJ0IG9mIGVhY2ggc291cmNlIGZpbGUgdG8gbW9zdCBl -ZmZlY3RpdmVseSBjb252ZXkKICB0aGUgZXhjbHVzaW9uIG9mIHdhcnJhbnR5OyBhbmQgZWFjaCBm -aWxlIHNob3VsZCBoYXZlIGF0IGxlYXN0IHRoZQogIGBgY29weXJpZ2h0JycgbGluZSBhbmQgYSBw -b2ludGVyIHRvIHdoZXJlIHRoZSBmdWxsIG5vdGljZSBpcyBmb3VuZC4KClxiZWdpbntxdW90ZX0K -b25lIGxpbmUgdG8gZ2l2ZSB0aGUgcHJvZ3JhbSdzIG5hbWUgYW5kIGEgYnJpZWYgaWRlYSBvZiB3 -aGF0IGl0IGRvZXMuIFxcCkNvcHlyaWdodCAoQykgeXl5eSAgbmFtZSBvZiBhdXRob3IgXFwKClRo -aXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQv -b3IgbW9kaWZ5Cml0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExp -Y2Vuc2UgYXMgcHVibGlzaGVkIGJ5CnRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhl -ciB2ZXJzaW9uIDIgb2YgdGhlIExpY2Vuc2UsIG9yCihhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVy -IHZlcnNpb24uCgpUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBp -dCB3aWxsIGJlIHVzZWZ1bCwKYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4g -dGhlIGltcGxpZWQgd2FycmFudHkgb2YKTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEg -UEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZQpHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBm -b3IgbW9yZSBkZXRhaWxzLgoKWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUg -R05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKYWxvbmcgd2l0aCB0aGlzIHByb2dyYW07IGlmIG5v -dCwgd3JpdGUgdG8gdGhlIEZyZWUgU29mdHdhcmUKRm91bmRhdGlvbiwgSW5jLiwgNTkgVGVtcGxl -IFBsYWNlIC0gU3VpdGUgMzMwLCBCb3N0b24sIE1BICAwMjExMS0xMzA3LCBVU0EuClxlbmR7cXVv -dGV9CgpBbHNvIGFkZCBpbmZvcm1hdGlvbiBvbiBob3cgdG8gY29udGFjdCB5b3UgYnkgZWxlY3Ry -b25pYyBhbmQgcGFwZXIgbWFpbC4KCklmIHRoZSBwcm9ncmFtIGlzIGludGVyYWN0aXZlLCBtYWtl -IGl0IG91dHB1dCBhIHNob3J0IG5vdGljZSBsaWtlIHRoaXMKd2hlbiBpdCBzdGFydHMgaW4gYW4g -aW50ZXJhY3RpdmUgbW9kZToKClxiZWdpbntxdW90ZX0KR25vbW92aXNpb24gdmVyc2lvbiA2OSwg -Q29weXJpZ2h0IChDKSB5eXl5ICBuYW1lIG9mIGF1dGhvciBcXApHbm9tb3Zpc2lvbiBjb21lcyB3 -aXRoIEFCU09MVVRFTFkgTk8gV0FSUkFOVFk7IGZvciBkZXRhaWxzIHR5cGUgYHNob3cgdycuIFxc -ClRoaXMgaXMgZnJlZSBzb2Z0d2FyZSwgYW5kIHlvdSBhcmUgd2VsY29tZSB0byByZWRpc3RyaWJ1 -dGUgaXQKdW5kZXIgY2VydGFpbiBjb25kaXRpb25zOyB0eXBlIGBzaG93IGMnIGZvciBkZXRhaWxz -LgpcZW5ke3F1b3RlfQoKClRoZSBoeXBvdGhldGljYWwgY29tbWFuZHMge1x0dCBzaG93IHd9IGFu -ZCB7XHR0IHNob3cgY30gc2hvdWxkIHNob3cgdGhlCmFwcHJvcHJpYXRlIHBhcnRzIG9mIHRoZSBH -ZW5lcmFsIFB1YmxpYyBMaWNlbnNlLiAgT2YgY291cnNlLCB0aGUgY29tbWFuZHMKeW91IHVzZSBt -YXkgYmUgY2FsbGVkIHNvbWV0aGluZyBvdGhlciB0aGFuIHtcdHQgc2hvdyB3fSBhbmQge1x0dCBz -aG93IGN9Owp0aGV5IGNvdWxkIGV2ZW4gYmUgbW91c2UtY2xpY2tzIG9yIG1lbnUgaXRlbXMtLS13 -aGF0ZXZlciBzdWl0cyB5b3VyCnByb2dyYW0uCgpZb3Ugc2hvdWxkIGFsc28gZ2V0IHlvdXIgZW1w -bG95ZXIgKGlmIHlvdSB3b3JrIGFzIGEgcHJvZ3JhbW1lcikgb3IgeW91cgpzY2hvb2wsIGlmIGFu -eSwgdG8gc2lnbiBhIGBgY29weXJpZ2h0IGRpc2NsYWltZXInJyBmb3IgdGhlIHByb2dyYW0sIGlm -Cm5lY2Vzc2FyeS4gIEhlcmUgaXMgYSBzYW1wbGU7IGFsdGVyIHRoZSBuYW1lczoKClxiZWdpbntx -dW90ZX0KWW95b2R5bmUsIEluYy4sIGhlcmVieSBkaXNjbGFpbXMgYWxsIGNvcHlyaWdodCBpbnRl -cmVzdCBpbiB0aGUgcHJvZ3JhbSBcXApgR25vbW92aXNpb24nICh3aGljaCBtYWtlcyBwYXNzZXMg -YXQgY29tcGlsZXJzKSB3cml0dGVuIGJ5IEphbWVzIEhhY2tlci4gXFwKCnNpZ25hdHVyZSBvZiBU -eSBDb29uLCAxIEFwcmlsIDE5ODkgXFwKVHkgQ29vbiwgUHJlc2lkZW50IG9mIFZpY2UKXGVuZHtx -dW90ZX0KCgpUaGlzIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZG9lcyBub3QgcGVybWl0IGluY29y -cG9yYXRpbmcgeW91ciBwcm9ncmFtCmludG8gcHJvcHJpZXRhcnkgcHJvZ3JhbXMuICBJZiB5b3Vy -IHByb2dyYW0gaXMgYSBzdWJyb3V0aW5lIGxpYnJhcnksIHlvdQptYXkgY29uc2lkZXIgaXQgbW9y -ZSB1c2VmdWwgdG8gcGVybWl0IGxpbmtpbmcgcHJvcHJpZXRhcnkgYXBwbGljYXRpb25zCndpdGgg -dGhlIGxpYnJhcnkuICBJZiB0aGlzIGlzIHdoYXQgeW91IHdhbnQgdG8gZG8sIHVzZSB0aGUgR05V -IExpYnJhcnkKR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBpbnN0ZWFkIG9mIHRoaXMgTGljZW5zZS4K -Cgo= -" ++ Glurf ++ lists:reverse(Glurf++"kalle"). diff --git a/lib/compiler/test/compilation_SUITE_data/nested_tuples_in_case_expr.erl b/lib/compiler/test/compilation_SUITE_data/nested_tuples_in_case_expr.erl deleted file mode 100644 index 62402c10b7..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/nested_tuples_in_case_expr.erl +++ /dev/null @@ -1,36 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(nested_tuples_in_case_expr). --export([nested_tuples_in_case_expr/0,t/2]). - -nested_tuples_in_case_expr() -> - ok. - -t(A, B) -> - case {{element(1, A),element(2, B)},{element(2, A),element(2, B)}} of - {Same,Same} -> ok; - {{0,1},{up,X}} -> bar(X); - {_,{X,_}} -> bar(X) - end. - -bar(X) -> X. - - - - diff --git a/lib/compiler/test/compilation_SUITE_data/opt_crash.erl b/lib/compiler/test/compilation_SUITE_data/opt_crash.erl index 3a1d625c28..f1607cca68 100644 --- a/lib/compiler/test/compilation_SUITE_data/opt_crash.erl +++ b/lib/compiler/test/compilation_SUITE_data/opt_crash.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/other/vsn_1.erl b/lib/compiler/test/compilation_SUITE_data/other/vsn_1.erl index 7cb0778b95..ea9b32bcbb 100644 --- a/lib/compiler/test/compilation_SUITE_data/other/vsn_1.erl +++ b/lib/compiler/test/compilation_SUITE_data/other/vsn_1.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/other/vsn_3.erl b/lib/compiler/test/compilation_SUITE_data/other/vsn_3.erl index 3870e0704d..9815138886 100644 --- a/lib/compiler/test/compilation_SUITE_data/other/vsn_3.erl +++ b/lib/compiler/test/compilation_SUITE_data/other/vsn_3.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_2141.erl b/lib/compiler/test/compilation_SUITE_data/otp_2141.erl deleted file mode 100644 index 3e766546be..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/otp_2141.erl +++ /dev/null @@ -1,24 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(otp_2141). --export([otp_2141/0]). - - -otp_2141() -> - ok. diff --git a/lib/compiler/test/compilation_SUITE_data/otp_2173.erl b/lib/compiler/test/compilation_SUITE_data/otp_2173.erl deleted file mode 100644 index 7e9d4c417b..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/otp_2173.erl +++ /dev/null @@ -1,31 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(otp_2173). --compile(export_all). - --record(t, {a = fun(X) -> X*X end}). - -otp_2173() -> - ok. - -t() -> - #t{}. - - - diff --git a/lib/compiler/test/compilation_SUITE_data/otp_2330.erl b/lib/compiler/test/compilation_SUITE_data/otp_2330.erl index f5f6717968..92e93be1c1 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_2330.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_2330.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_2380.erl b/lib/compiler/test/compilation_SUITE_data/otp_2380.erl index db2028b347..b7b2b04793 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_2380.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_2380.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_4790.erl b/lib/compiler/test/compilation_SUITE_data/otp_4790.erl index 130ee44e80..08769c5303 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_4790.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_4790.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5076.erl b/lib/compiler/test/compilation_SUITE_data/otp_5076.erl deleted file mode 100644 index f05a4e1148..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/otp_5076.erl +++ /dev/null @@ -1,27 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(otp_5076). --export([?MODULE/0]). - -?MODULE() -> - [] = t(), - ok. - -t() -> - [3 || {3=4} <- []]. diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5092.erl b/lib/compiler/test/compilation_SUITE_data/otp_5092.erl deleted file mode 100644 index e445e5e1e9..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/otp_5092.erl +++ /dev/null @@ -1,39 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(otp_5092). --export([?MODULE/0]). - -?MODULE() -> - [] = t(), - [] = t2(), - [t] = t4(), - [] = t5(), - ok. - -t() -> - [t || {C=D}={_,_} <- []]. - -t2() -> - [X || {X,{Y}={X,X}} <- []]. - -t4() -> - [t || "a"++"b" = "ab" <- ["ab"]]. - -t5() -> - [{X,Y} || {X} <- [], begin Y = X, Y =:= X end]. diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5151.erl b/lib/compiler/test/compilation_SUITE_data/otp_5151.erl index 2dccb979df..02a19ebd3d 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_5151.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_5151.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5235.erl b/lib/compiler/test/compilation_SUITE_data/otp_5235.erl index 1c918cdf9d..e0f77c73f6 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_5235.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_5235.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5244.erl b/lib/compiler/test/compilation_SUITE_data/otp_5244.erl deleted file mode 100644 index 30849c6b5e..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/otp_5244.erl +++ /dev/null @@ -1,47 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(otp_5244). --export([?MODULE/0]). - -?MODULE() -> - L = [{stretch,0,0}, - {bad,[]}, - {bad,atom}, - {bad,0}, - {bad,16#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}, - {bad,16#555555555555555555555555555555555555555555555555555}], - remove_failure(L, unit, 0). - -remove_failure([], _Unit, _MaxFailure) -> - ok; -remove_failure([{bad,Bad}|_], _Unit, _MaxFailure) -> - Bad; -remove_failure([{stretch,_,Mi}=Stretch | Specs], Unit, _MaxFailure) -> - {MinMax,NewMaxFailure} = max_failure(), - case {MinMax,remove_failure(Specs, Unit, NewMaxFailure)} of - {min,{NewMaxFailure,Rest}} -> - {done,[{fixed,Mi} | Rest]}; - {min,_} when Specs =/= [] -> - remove_failure([Stretch|tl(Specs)], Unit, NewMaxFailure); - {min,_} -> - ok - end. - -max_failure() -> - {min,1}. diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5404.erl b/lib/compiler/test/compilation_SUITE_data/otp_5404.erl index 2de147c082..393c32aa00 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_5404.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_5404.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5436.erl b/lib/compiler/test/compilation_SUITE_data/otp_5436.erl index 4df8d50647..842d46e93e 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_5436.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_5436.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5481.erl b/lib/compiler/test/compilation_SUITE_data/otp_5481.erl index 5cf114ac4e..881d2cd239 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_5481.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_5481.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5553.erl b/lib/compiler/test/compilation_SUITE_data/otp_5553.erl index 9a7004a980..fd7757ac1e 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_5553.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_5553.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5632.erl b/lib/compiler/test/compilation_SUITE_data/otp_5632.erl index ebf8e47ca2..aaf6814f4e 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_5632.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_5632.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5714.erl b/lib/compiler/test/compilation_SUITE_data/otp_5714.erl index 5aefa0a803..b65663f063 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_5714.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_5714.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_5872.erl b/lib/compiler/test/compilation_SUITE_data/otp_5872.erl index cada7ad994..b9b9677a14 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_5872.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_5872.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_6121.erl b/lib/compiler/test/compilation_SUITE_data/otp_6121.erl index c71f7806a0..f2bc9b3ee8 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_6121.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_6121.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/otp_6121a.erl b/lib/compiler/test/compilation_SUITE_data/otp_6121a.erl deleted file mode 100644 index caed631aa2..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/otp_6121a.erl +++ /dev/null @@ -1,32 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(otp_6121a). --export([?MODULE/0]). - -%% Thanks to Martin Bjorklund. - -?MODULE() -> - G = fun() -> ok end, - try - fun() -> ok end - after - fun({A, B}) -> A + B end - end, - ok. - diff --git a/lib/compiler/test/compilation_SUITE_data/otp_6121b.erl b/lib/compiler/test/compilation_SUITE_data/otp_6121b.erl deleted file mode 100644 index 967f078942..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/otp_6121b.erl +++ /dev/null @@ -1,33 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(otp_6121b). --export([?MODULE/0]). - -%% Thanks to Tim Rath. - -?MODULE() -> - A = {6}, - try - io:fwrite("") - after - fun () -> - fun () -> {B} = A end - end - end. - diff --git a/lib/compiler/test/compilation_SUITE_data/otp_7202.erl b/lib/compiler/test/compilation_SUITE_data/otp_7202.erl index 9dca244aa5..4d86763b52 100644 --- a/lib/compiler/test/compilation_SUITE_data/otp_7202.erl +++ b/lib/compiler/test/compilation_SUITE_data/otp_7202.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/pattern_expr.erl b/lib/compiler/test/compilation_SUITE_data/pattern_expr.erl deleted file mode 100644 index 02eacced81..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/pattern_expr.erl +++ /dev/null @@ -1,30 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(pattern_expr). - --export(pattern_expr/0). - -pattern_expr() -> - f(). - -f() -> - case 4 of - 2+2 -> - ok - end. diff --git a/lib/compiler/test/compilation_SUITE_data/trycatch_4.erl b/lib/compiler/test/compilation_SUITE_data/trycatch_4.erl deleted file mode 100644 index 7ea710856f..0000000000 --- a/lib/compiler/test/compilation_SUITE_data/trycatch_4.erl +++ /dev/null @@ -1,50 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(trycatch_4). --export([trycatch_4/0]). --record(state, {foo}). - -trycatch_4() -> - handle_info({foo}, #state{}), - ok. - -handle_info({_}, State) -> - foo(), - State#state{foo = bar}, - case ok of - _ -> - case catch foo() of - ok -> - {stop, State} - end - end; -handle_info(_, State) -> - (catch begin - foo(), - State#state{foo = bar} - end), - case ok of - _ -> - case catch foo() of - ok -> - {stop, State} - end - end. - -foo() -> ok. diff --git a/lib/compiler/test/compilation_SUITE_data/vsn_1.erl b/lib/compiler/test/compilation_SUITE_data/vsn_1.erl index a9e23f7c25..fed91a25c6 100644 --- a/lib/compiler/test/compilation_SUITE_data/vsn_1.erl +++ b/lib/compiler/test/compilation_SUITE_data/vsn_1.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/vsn_2.erl b/lib/compiler/test/compilation_SUITE_data/vsn_2.erl index 4833458cf4..670b3b34ed 100644 --- a/lib/compiler/test/compilation_SUITE_data/vsn_2.erl +++ b/lib/compiler/test/compilation_SUITE_data/vsn_2.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compilation_SUITE_data/vsn_3.erl b/lib/compiler/test/compilation_SUITE_data/vsn_3.erl index 7b970b2b75..8fcd596636 100644 --- a/lib/compiler/test/compilation_SUITE_data/vsn_3.erl +++ b/lib/compiler/test/compilation_SUITE_data/vsn_3.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 8cb7d1b55b..b0148f7103 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -20,7 +21,8 @@ %% Tests compile:file/1 and compile:file/2 with various options. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("stdlib/include/erl_compile.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, @@ -28,11 +30,12 @@ file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1, binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1, other_output/1, encrypted_abstr/1, - bad_record_use1/1, bad_record_use2/1, strict_record/1, - missing_testheap/1, cover/1, env/1, core/1, asm/1, - sys_pre_attributes/1]). - --export([init/3]). + strict_record/1, + cover/1, env/1, core/1, + core_roundtrip/1, asm/1, + sys_pre_attributes/1, dialyzer/1, + warnings/1, pre_load_check/1, env_compiler_options/1 + ]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -45,13 +48,13 @@ all() -> [app_test, appup_test, file_1, forms_2, module_mismatch, big_file, outdir, binary, makedep, cond_and_ifdef, listings, listings_big, other_output, encrypted_abstr, - {group, bad_record_use}, strict_record, - missing_testheap, cover, env, core, asm, - sys_pre_attributes]. + strict_record, + cover, env, core, core_roundtrip, asm, + sys_pre_attributes, dialyzer, warnings, pre_load_check, + env_compiler_options]. groups() -> - [{bad_record_use, [], - [bad_record_use1, bad_record_use2]}]. + []. init_per_suite(Config) -> Config. @@ -69,68 +72,94 @@ end_per_group(_GroupName, Config) -> %% Test that the Application file has no `basic' errors."; app_test(Config) when is_list(Config) -> - ?line ?t:app_test(compiler). + test_server:app_test(compiler). %% Test that the Application upgrade file has no `basic' errors."; appup_test(Config) when is_list(Config) -> - ok = ?t:appup_test(compiler). + ok = test_server:appup_test(compiler). %% Tests that we can compile and run a simple Erlang program, %% using compile:file/1. file_1(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(5)), - process_flag(trap_exit, true), - ?line {Simple, Target} = files(Config, "file_1"), - ?line {ok, Cwd} = file:get_cwd(), - ?line ok = file:set_cwd(filename:dirname(Target)), + {Simple, Target} = get_files(Config, simple, "file_1"), + {ok, Cwd} = file:get_cwd(), + ok = file:set_cwd(filename:dirname(Target)), %% Native from BEAM without compilation info. - ?line {ok,simple} = compile:file(Simple, [slim]), %Smoke test only. - ?line {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test. + {ok,simple} = compile:file(Simple, [slim]), %Smoke test only. + {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test. %% Native from BEAM with compilation info. - ?line {ok,simple} = compile:file(Simple), %Smoke test only. - ?line {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test. + {ok,simple} = compile:file(Simple), %Smoke test only. + {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test. + + {ok,simple} = compile:file(Simple, [native,report]), %Smoke test. - ?line {ok,simple} = compile:file(Simple, [native,report]), %Smoke test. + compile_and_verify(Simple, Target, []), + compile_and_verify(Simple, Target, [native]), + compile_and_verify(Simple, Target, [debug_info]), + {ok,simple} = compile:file(Simple, [no_line_info]), %Coverage - ?line compile_and_verify(Simple, Target, []), - ?line compile_and_verify(Simple, Target, [native]), - ?line compile_and_verify(Simple, Target, [debug_info]), - ?line {ok,simple} = compile:file(Simple, [no_line_info]), %Coverage + {ok,simple} = compile:file(Simple, [{eprof,beam_z}]), %Coverage - ?line ok = file:set_cwd(Cwd), - ?line true = exists(Target), - ?line passed = run(Target, test, []), + ok = file:set_cwd(Cwd), + true = exists(Target), + passed = run(Target, test, []), %% Cleanup. - ?line ok = file:delete(Target), - ?line ok = file:del_dir(filename:dirname(Target)), + ok = file:delete(Target), + ok = file:del_dir(filename:dirname(Target)), %% There should not be any messages in the messages. receive Any -> - ?t:fail({unexpected,Any}) + ct:fail({unexpected,Any}) after 10 -> ok end, - ?line test_server:timetrap_cancel(Dog), ok. forms_2(Config) when is_list(Config) -> Src = "/foo/bar", AbsSrc = filename:absname(Src), - {ok,simple,Binary} = compile:forms([{attribute,1,module,simple}], - [binary,{source,Src}]), - code:load_binary(simple, Src, Binary), - Info = simple:module_info(compile), + Anno = erl_anno:new(1), + SimpleCode = [{attribute,Anno,module,simple}], + {ok,simple,Bin1} = compile:forms(SimpleCode, [binary,{source,Src}]), + + %% Load and test that the proper source is returned. + AbsSrc = forms_load_code(simple, Src, Bin1), + + %% Work in a deleted directory. + PrivDir = proplists:get_value(priv_dir, Config), + WorkDir = filename:join(PrivDir, ?FUNCTION_NAME), + ok = file:make_dir(WorkDir), + ok = file:set_cwd(WorkDir), + case os:type() of + {unix,_} -> os:cmd("rm -rf " ++ WorkDir); + _ -> ok + end, + {ok,simple,Bin2} = compile:forms(SimpleCode), + undefined = forms_load_code(simple, "ignore", Bin2), + + {ok,simple,Bin3} = compile:forms(SimpleCode, [{source,Src},report]), + case forms_load_code(simple, "ignore", Bin3) of + Src -> %Unix. + ok; + AbsSrc -> %Windows. + ok + end, - %% Test that the proper source is returned. - AbsSrc = proplists:get_value(source, Info), + ok. + + +forms_load_code(Mod, Src, Bin) -> + {module,Mod} = code:load_binary(Mod, Src, Bin), + Info = Mod:module_info(compile), + SourceOption = proplists:get_value(source, Info), %% Ensure that the options are not polluted with 'source'. [] = proplists:get_value(options, Info), @@ -138,124 +167,115 @@ forms_2(Config) when is_list(Config) -> %% Cleanup. true = code:delete(simple), false = code:purge(simple), - ok. + + SourceOption. + module_mismatch(Config) when is_list(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line File = filename:join(DataDir, "wrong_module_name.erl"), + DataDir = proplists:get_value(data_dir, Config), + File = filename:join(DataDir, "wrong_module_name.erl"), {error,[{"wrong_module_name.beam", [{none,compile,{module_name,arne,"wrong_module_name"}}]}], []} = compile:file(File, [return]), - ?line error = compile:file(File, [report]), + error = compile:file(File, [report]), - ?line {ok,arne,[]} = compile:file(File, + {ok,arne,[]} = compile:file(File, [return,no_error_module_mismatch]), ok. big_file(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(5)), - ?line DataDir = ?config(data_dir, Config), - ?line PrivDir = ?config(priv_dir, Config), - ?line Big = filename:join(DataDir, "big.erl"), - ?line Target = filename:join(PrivDir, "big.beam"), - ?line ok = file:set_cwd(PrivDir), - ?line compile_and_verify(Big, Target, []), - ?line compile_and_verify(Big, Target, [debug_info]), - ?line compile_and_verify(Big, Target, [no_postopt]), + {Big,Target} = get_files(Config, big, "big_file"), + ok = file:set_cwd(filename:dirname(Target)), + compile_and_verify(Big, Target, []), + compile_and_verify(Big, Target, [debug_info]), + compile_and_verify(Big, Target, [no_postopt]), %% Cleanup. - ?line ok = file:delete(Target), - ?line test_server:timetrap_cancel(Dog), + ok = file:delete(Target), ok. %% Tests that the {outdir, Dir} option works. outdir(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), - ?line {Simple, Target} = files(Config, "outdir"), - ?line {ok, simple} = compile:file(Simple, [{outdir, filename:dirname(Target)}]), - ?line true = exists(Target), - ?line passed = run(Target, test, []), - ?line ok = file:delete(Target), - ?line ok = file:del_dir(filename:dirname(Target)), - ?line test_server:timetrap_cancel(Dog), + {Simple, Target} = get_files(Config, simple, "outdir"), + {ok, simple} = compile:file(Simple, [{outdir, filename:dirname(Target)}]), + true = exists(Target), + passed = run(Target, test, []), + ok = file:delete(Target), + ok = file:del_dir(filename:dirname(Target)), ok. %% Tests that the binary option works. binary(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), - ?line {Simple, Target} = files(Config, "binary"), - ?line {ok, simple, Binary} = compile:file(Simple, [binary]), - ?line code:load_binary(simple, Target, Binary), - ?line passed = simple:test(), - ?line true = code:delete(simple), - ?line false = code:purge(simple), - ?line ok = file:del_dir(filename:dirname(Target)), - ?line test_server:timetrap_cancel(Dog), + {Simple, Target} = get_files(Config, simple, "binary"), + {ok, simple, Binary} = compile:file(Simple, [binary]), + code:load_binary(simple, Target, Binary), + passed = simple:test(), + true = code:delete(simple), + false = code:purge(simple), + ok = file:del_dir(filename:dirname(Target)), ok. %% Tests that the dependencies-Makefile-related options work. makedep(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), - ?line {Simple,Target} = files(Config, "makedep"), - ?line DataDir = ?config(data_dir, Config), - ?line SimpleRootname = filename:rootname(Simple), - ?line IncludeDir = filename:join(filename:dirname(Simple), "include"), - ?line IncludeOptions = [ - {d,need_foo}, - {d,foo_value,42}, - {d,include_generated}, - {i,IncludeDir} - ], + {Simple,Target} = get_files(Config, simple, "makedep"), + DataDir = proplists:get_value(data_dir, Config), + SimpleRootname = filename:rootname(Simple), + IncludeDir = filename:join(filename:dirname(Simple), "include"), + IncludeOptions = [ + {d,need_foo}, + {d,foo_value,42}, + {d,include_generated}, + {i,IncludeDir} + ], %% Basic rule. - ?line BasicMf1Name = SimpleRootname ++ "-basic1.mk", - ?line {ok,BasicMf1} = file:read_file(BasicMf1Name), - ?line {ok,_,Mf1} = compile:file(Simple, [binary,makedep]), - ?line BasicMf1 = makedep_canonicalize_result(Mf1, DataDir), + BasicMf1Name = SimpleRootname ++ "-basic1.mk", + {ok,BasicMf1} = file:read_file(BasicMf1Name), + {ok,_,Mf1} = compile:file(Simple, [binary,makedep]), + BasicMf1 = makedep_canonicalize_result(Mf1, DataDir), %% Basic rule with one existing header. - ?line BasicMf2Name = SimpleRootname ++ "-basic2.mk", - ?line {ok,BasicMf2} = file:read_file(BasicMf2Name), - ?line {ok,_,Mf2} = compile:file(Simple, [binary,makedep|IncludeOptions]), - ?line BasicMf2 = makedep_canonicalize_result(Mf2, DataDir), + BasicMf2Name = SimpleRootname ++ "-basic2.mk", + {ok,BasicMf2} = file:read_file(BasicMf2Name), + {ok,_,Mf2} = compile:file(Simple, [binary,makedep|IncludeOptions]), + BasicMf2 = makedep_canonicalize_result(Mf2, DataDir), %% Rule with one existing header and one missing header. - ?line MissingMfName = SimpleRootname ++ "-missing.mk", - ?line {ok,MissingMf} = file:read_file(MissingMfName), - ?line {ok,_,Mf3} = compile:file(Simple, + MissingMfName = SimpleRootname ++ "-missing.mk", + {ok,MissingMf} = file:read_file(MissingMfName), + {ok,_,Mf3} = compile:file(Simple, [binary,makedep,makedep_add_missing|IncludeOptions]), - ?line MissingMf = makedep_canonicalize_result(Mf3, DataDir), + MissingMf = makedep_canonicalize_result(Mf3, DataDir), %% Rule with modified target. - ?line TargetMf1Name = SimpleRootname ++ "-target1.mk", - ?line {ok,TargetMf1} = file:read_file(TargetMf1Name), - ?line {ok,_,Mf4} = compile:file(Simple, + TargetMf1Name = SimpleRootname ++ "-target1.mk", + {ok,TargetMf1} = file:read_file(TargetMf1Name), + {ok,_,Mf4} = compile:file(Simple, [binary,makedep,{makedep_target,"$target"}|IncludeOptions]), - ?line TargetMf1 = makedep_modify_target( + TargetMf1 = makedep_modify_target( makedep_canonicalize_result(Mf4, DataDir), "$$target"), %% Rule with quoted modified target. - ?line TargetMf2Name = SimpleRootname ++ "-target2.mk", - ?line {ok,TargetMf2} = file:read_file(TargetMf2Name), - ?line {ok,_,Mf5} = compile:file(Simple, + TargetMf2Name = SimpleRootname ++ "-target2.mk", + {ok,TargetMf2} = file:read_file(TargetMf2Name), + {ok,_,Mf5} = compile:file(Simple, [binary,makedep,{makedep_target,"$target"},makedep_quote_target| IncludeOptions]), - ?line TargetMf2 = makedep_modify_target( + TargetMf2 = makedep_modify_target( makedep_canonicalize_result(Mf5, DataDir), "$$target"), %% Basic rule written to some file. - ?line {ok,_} = compile:file(Simple, + {ok,_} = compile:file(Simple, [makedep,{makedep_output,Target}|IncludeOptions]), - ?line {ok,Mf6} = file:read_file(Target), - ?line BasicMf2 = makedep_canonicalize_result(Mf6, DataDir), + {ok,Mf6} = file:read_file(Target), + BasicMf2 = makedep_canonicalize_result(Mf6, DataDir), %% Rule with creating phony target. - ?line PhonyMfName = SimpleRootname ++ "-phony.mk", - ?line {ok,PhonyMf} = file:read_file(PhonyMfName), - ?line {ok,_,Mf7} = compile:file(Simple, + PhonyMfName = SimpleRootname ++ "-phony.mk", + {ok,PhonyMf} = file:read_file(PhonyMfName), + {ok,_,Mf7} = compile:file(Simple, [binary,makedep,makedep_phony|IncludeOptions]), - ?line PhonyMf = makedep_canonicalize_result(Mf7, DataDir), + PhonyMf = makedep_canonicalize_result(Mf7, DataDir), - ?line ok = file:delete(Target), - ?line ok = file:del_dir(filename:dirname(Target)), - ?line test_server:timetrap_cancel(Dog), + ok = file:delete(Target), + ok = file:del_dir(filename:dirname(Target)), ok. makedep_canonicalize_result(Mf, DataDir) -> @@ -275,30 +295,26 @@ makedep_modify_target(Mf, Target) -> %% Tests that conditional compilation, defining values, including files work. cond_and_ifdef(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(60)), - ?line {Simple, Target} = files(Config, "cond_and_ifdef"), - ?line IncludeDir = filename:join(filename:dirname(Simple), "include"), - ?line Options = [{outdir, filename:dirname(Target)}, + {Simple, Target} = get_files(Config, simple, "cond_and_ifdef"), + IncludeDir = filename:join(filename:dirname(Simple), "include"), + Options = [{outdir, filename:dirname(Target)}, {d, need_foo}, {d, foo_value, 42}, {i, IncludeDir}, report], - ?line {ok, simple} = compile:file(Simple, Options), - ?line true = exists(Target), - ?line {hiker, 42} = run(Target, foo, []), - ?line ok = file:delete(Target), - ?line ok = file:del_dir(filename:dirname(Target)), - ?line test_server:timetrap_cancel(Dog), + {ok, simple} = compile:file(Simple, Options), + true = exists(Target), + {hiker, 42} = run(Target, foo, []), + ok = file:delete(Target), + ok = file:del_dir(filename:dirname(Target)), ok. listings(Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:minutes(8)), - DataDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), ok = do_file_listings(DataDir, PrivDir, [ "simple", "small", "small_maps" ]), - test_server:timetrap_cancel(Dog), ok. do_file_listings(_, _, []) -> ok; @@ -324,6 +340,8 @@ do_file_listings(DataDir, PrivDir, [File|Files]) -> do_listing(Simple, TargetDir, dlife, ".life"), do_listing(Simple, TargetDir, dcg, ".codegen"), do_listing(Simple, TargetDir, dblk, ".block"), + do_listing(Simple, TargetDir, dexcept, ".except"), + do_listing(Simple, TargetDir, dbs, ".bs"), do_listing(Simple, TargetDir, dbool, ".bool"), do_listing(Simple, TargetDir, dtype, ".type"), do_listing(Simple, TargetDir, ddead, ".dead"), @@ -353,159 +371,146 @@ do_file_listings(DataDir, PrivDir, [File|Files]) -> do_file_listings(DataDir,PrivDir,Files). listings_big(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(10)), - ?line DataDir = ?config(data_dir, Config), - ?line PrivDir = ?config(priv_dir, Config), - ?line Big = filename:join(DataDir, big), - ?line TargetDir = filename:join(PrivDir, listings_big), - ?line ok = file:make_dir(TargetDir), - ?line do_listing(Big, TargetDir, 'S'), - ?line do_listing(Big, TargetDir, 'E'), - ?line do_listing(Big, TargetDir, 'P'), - ?line do_listing(Big, TargetDir, dkern, ".kernel"), - - ?line Target = filename:join(TargetDir, big), - ?line {ok,big} = compile:file(Target, [asm,{outdir,TargetDir}]), + {Big,Target} = get_files(Config, big, listings_big), + TargetDir = filename:dirname(Target), + do_listing(Big, TargetDir, 'S'), + do_listing(Big, TargetDir, 'E'), + do_listing(Big, TargetDir, 'P'), + do_listing(Big, TargetDir, dkern, ".kernel"), + + TargetNoext = filename:rootname(Target, code:objfile_extension()), + {ok,big} = compile:file(TargetNoext, [from_asm,{outdir,TargetDir}]), %% Cleanup. - ?line ok = file:delete(Target ++ ".beam"), - ?line lists:foreach(fun(F) -> ok = file:delete(F) end, - filelib:wildcard(filename:join(TargetDir, "*"))), - ?line ok = file:del_dir(TargetDir), - ?line test_server:timetrap_cancel(Dog), + ok = file:delete(Target), + lists:foreach(fun(F) -> ok = file:delete(F) end, + filelib:wildcard(filename:join(TargetDir, "*"))), + ok = file:del_dir(TargetDir), ok. other_output(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(8)), - ?line DataDir = ?config(data_dir, Config), - ?line PrivDir = ?config(priv_dir, Config), - ?line Simple = filename:join(DataDir, simple), - ?line TargetDir = filename:join(PrivDir, other_output), - ?line ok = file:make_dir(TargetDir), + {Simple,_Target} = get_files(Config, simple, "other_output"), io:put_chars("to_pp"), - ?line {ok,[],PP} = compile:file(Simple, [to_pp,binary,time]), - ?line [] = [E || E <- PP, - begin - case element(1, E) of - attribute -> false; - function -> false; - eof -> false - end - end], + {ok,[],PP} = compile:file(Simple, [to_pp,binary,time]), + [] = [E || E <- PP, + begin + case element(1, E) of + attribute -> false; + function -> false; + eof -> false + end + end], io:put_chars("to_exp (file)"), - ?line {ok,simple,Expand} = compile:file(Simple, [to_exp,binary,time]), - ?line case Expand of - {simple,Exports,Forms} when is_list(Exports), is_list(Forms) -> ok - end, + {ok,simple,Expand} = compile:file(Simple, [to_exp,binary,time]), + case Expand of + {simple,Exports,Forms} when is_list(Exports), is_list(Forms) -> ok + end, io:put_chars("to_exp (forms)"), - ?line {ok,simple,Expand} = compile:forms(PP, [to_exp,binary,time]), + {ok,simple,Expand} = compile:forms(PP, [to_exp,binary,time]), io:put_chars("to_core (file)"), - ?line {ok,simple,Core} = compile:file(Simple, [to_core,binary,time]), - ?line c_module = element(1, Core), - ?line {ok,_} = core_lint:module(Core), + {ok,simple,Core} = compile:file(Simple, [to_core,binary,time]), + c_module = element(1, Core), + {ok,_} = core_lint:module(Core), io:put_chars("to_core (forms)"), - ?line {ok,simple,Core} = compile:forms(PP, [to_core,binary,time]), + {ok,simple,Core} = compile:forms(PP, [to_core,binary,time]), io:put_chars("to_kernel (file)"), - ?line {ok,simple,Kernel} = compile:file(Simple, [to_kernel,binary,time]), - ?line k_mdef = element(1, Kernel), + {ok,simple,Kernel} = compile:file(Simple, [to_kernel,binary,time]), + k_mdef = element(1, Kernel), io:put_chars("to_kernel (forms)"), - ?line {ok,simple,Kernel} = compile:forms(PP, [to_kernel,binary,time]), + {ok,simple,Kernel} = compile:forms(PP, [to_kernel,binary,time]), io:put_chars("to_asm (file)"), - ?line {ok,simple,Asm} = compile:file(Simple, [to_asm,binary,time]), - ?line {simple,_,_,_,_} = Asm, + {ok,simple,Asm} = compile:file(Simple, [to_asm,binary,time]), + {simple,_,_,_,_} = Asm, io:put_chars("to_asm (forms)"), - ?line {ok,simple,Asm} = compile:forms(PP, [to_asm,binary,time]), + {ok,simple,Asm} = compile:forms(PP, [to_asm,binary,time]), - ?line test_server:timetrap_cancel(Dog), ok. encrypted_abstr(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(10)), - ?line {Simple,Target} = files(Config, "encrypted_abstr"), + {Simple,Target} = get_files(Config, simple, "encrypted_abstr"), Res = case has_crypto() of false -> %% No crypto. - ?line encrypted_abstr_no_crypto(Simple, Target), + encrypted_abstr_no_crypto(Simple, Target), {comment,"The crypto application is missing or broken"}; true -> %% Simulate not having crypto by removing %% the crypto application from the path. - ?line OldPath = code:get_path(), + OldPath = code:get_path(), try - ?line NewPath = OldPath -- [filename:dirname(code:which(crypto))], - ?line (catch crypto:stop()), - ?line code:delete(crypto), - ?line code:purge(crypto), - ?line code:set_path(NewPath), - ?line encrypted_abstr_no_crypto(Simple, Target) + NewPath = OldPath -- [filename:dirname(code:which(crypto))], + (catch crypto:stop()), + code:delete(crypto), + code:purge(crypto), + code:set_path(NewPath), + encrypted_abstr_no_crypto(Simple, Target) after code:set_path(OldPath) end, %% Now run the tests that require crypto. - ?line encrypted_abstr_1(Simple, Target), - ?line ok = file:delete(Target), - ?line ok = file:del_dir(filename:dirname(Target)) + encrypted_abstr_1(Simple, Target), + ok = file:delete(Target), + ok = file:del_dir(filename:dirname(Target)) end, %% Cleanup. - ?line test_server:timetrap_cancel(Dog), Res. encrypted_abstr_1(Simple, Target) -> - ?line TargetDir = filename:dirname(Target), - ?line Key = "ablurf123BX#$;3", - ?line install_crypto_key(Key), - ?line {ok,simple} = compile:file(Simple, + TargetDir = filename:dirname(Target), + Key = "ablurf123BX#$;3", + install_crypto_key(Key), + {ok,simple} = compile:file(Simple, [debug_info,{debug_info_key,Key}, {outdir,TargetDir}]), - ?line verify_abstract(Target), + verify_abstract(Target), - ?line {ok,simple} = compile:file(Simple, + {ok,simple} = compile:file(Simple, [{debug_info_key,Key}, {outdir,TargetDir}]), - ?line verify_abstract(Target), + verify_abstract(Target), - ?line {ok,simple} = compile:file(Simple, + {ok,simple} = compile:file(Simple, [debug_info,{debug_info_key,{des3_cbc,Key}}, {outdir,TargetDir}]), - ?line verify_abstract(Target), + verify_abstract(Target), - ?line {ok,{simple,[{compile_info,CInfo}]}} = + {ok,{simple,[{compile_info,CInfo}]}} = beam_lib:chunks(Target, [compile_info]), - ?line {value,{_,Opts}} = lists:keysearch(options, 1, CInfo), - ?line {value,{_,'********'}} = lists:keysearch(debug_info_key, 1, Opts), + {_,Opts} = lists:keyfind(options, 1, CInfo), + {_,'********'} = lists:keyfind(debug_info_key, 1, Opts), %% Try some illegal forms of crypto keys. - ?line error = compile:file(Simple, + error = compile:file(Simple, [debug_info,{debug_info_key,{blurf,"ss"}},report]), - ?line error = compile:file(Simple, + error = compile:file(Simple, [debug_info,{debug_info_key,{blurf,1,"ss"}},report]), - ?line error = compile:file(Simple, + error = compile:file(Simple, [debug_info,{debug_info_key,42},report]), %% Place the crypto key in .erlang.crypt. - ?line beam_lib:clear_crypto_key_fun(), - ?line {ok,OldCwd} = file:get_cwd(), - ?line ok = file:set_cwd(TargetDir), - - ?line error = compile:file(Simple, [encrypt_debug_info,report]), - - ?line NewKey = "better use another key here", - ?line write_crypt_file(["[{debug_info,des3_cbc,simple,\"",NewKey,"\"}].\n"]), - ?line {ok,simple} = compile:file(Simple, [encrypt_debug_info,report]), - ?line verify_abstract("simple.beam"), - ?line ok = file:delete(".erlang.crypt"), - ?line beam_lib:clear_crypto_key_fun(), - ?line {error,beam_lib,{key_missing_or_invalid,"simple.beam",abstract_code}} = + beam_lib:clear_crypto_key_fun(), + {ok,OldCwd} = file:get_cwd(), + ok = file:set_cwd(TargetDir), + + error = compile:file(Simple, [encrypt_debug_info,report]), + + NewKey = "better use another key here", + write_crypt_file(["[{debug_info,des3_cbc,simple,\"",NewKey,"\"}].\n"]), + {ok,simple} = compile:file(Simple, [encrypt_debug_info,report]), + verify_abstract("simple.beam"), + ok = file:delete(".erlang.crypt"), + beam_lib:clear_crypto_key_fun(), + {error,beam_lib,{key_missing_or_invalid,"simple.beam",abstract_code}} = beam_lib:chunks("simple.beam", [abstract_code]), - ?line ok = file:set_cwd(OldCwd), + ok = file:set_cwd(OldCwd), %% Test key compatibility by reading a BEAM file produced before %% the update to the new crypto functions. @@ -526,9 +531,9 @@ write_crypt_file(Contents0) -> encrypted_abstr_no_crypto(Simple, Target) -> io:format("simpe: ~p~n", [Simple]), - ?line TargetDir = filename:dirname(Target), - ?line Key = "ablurf123BX#$;3", - ?line error = compile:file(Simple, + TargetDir = filename:dirname(Target), + Key = "ablurf123BX#$;3", + error = compile:file(Simple, [debug_info,{debug_info_key,Key}, {outdir,TargetDir},report]), ok. @@ -540,7 +545,6 @@ verify_abstract(Target) -> has_crypto() -> try crypto:start(), - <<_,_,_,_,_>> = crypto:rand_bytes(5), crypto:stop(), true catch @@ -556,7 +560,7 @@ install_crypto_key(Key) -> %% Miscellanous tests, mainly to get better coverage. cover(Config) when is_list(Config) -> - ?line io:format("~p\n", [compile:options()]), + io:format("~p\n", [compile:options()]), ok. do_listing(Source, TargetDir, Type) -> @@ -567,31 +571,31 @@ do_listing(Source, TargetDir, Type, Ext) -> [Source, TargetDir, Type, Ext]), case compile:file(Source, [Type, time, {outdir, TargetDir}]) of {ok, _} -> ok; - Other -> test_server:fail({unexpected_result, Other}) + Other -> ct:fail({unexpected_result, Other}) end, SourceBase = filename:rootname(filename:basename(Source)), Target = filename:join(TargetDir, SourceBase ++ Ext), true = exists(Target). -files(Config, Name) -> - ?line code:delete(simple), - ?line code:purge(simple), - ?line DataDir = ?config(data_dir, Config), - ?line PrivDir = ?config(priv_dir, Config), - ?line Simple = filename:join(DataDir, "simple"), - ?line TargetDir = filename:join(PrivDir, Name), - ?line ok = file:make_dir(TargetDir), - ?line Target = filename:join(TargetDir, "simple"++code:objfile_extension()), - {Simple, Target}. - +get_files(Config, Module, OutputName) -> + code:delete(Module), + code:purge(Module), + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + Src = filename:join(DataDir, atom_to_list(Module)), + TargetDir = filename:join(PrivDir, OutputName), + ok = file:make_dir(TargetDir), + File = atom_to_list(Module) ++ code:objfile_extension(), + Target = filename:join(TargetDir, File), + {Src, Target}. run(Target, Func, Args) -> - ?line Module = list_to_atom(filename:rootname(filename:basename(Target))), - ?line {module, Module} = code:load_abs(filename:rootname(Target)), - ?line Result = (catch apply(Module, Func, Args)), - ?line true = code:delete(Module), - ?line false = code:purge(Module), + Module = list_to_atom(filename:rootname(filename:basename(Target))), + {module, Module} = code:load_abs(filename:rootname(Target)), + Result = (catch apply(Module, Func, Args)), + true = code:delete(Module), + false = code:purge(Module), Result. exists(Name) -> @@ -601,49 +605,27 @@ exists(Name) -> end. -%% Tests that the compiler does not accept -%% bad use of records. -bad_record_use1(Config) when is_list(Config) -> - ?line {ok, Cwd} = file:get_cwd(), - ?line file:set_cwd(?config(data_dir, Config)), - ?line true=exists("bad_record_use.erl"), - ?line Ret=c:c(bad_record_use), - ?line file:set_cwd(Cwd), - ?line error=Ret, - ok. - -%% Tests that the compiler does not accept -%% bad use of records. -bad_record_use2(Config) when is_list(Config) -> - ?line {ok, Cwd} = file:get_cwd(), - ?line file:set_cwd(?config(data_dir, Config)), - ?line true=exists("bad_record_use2.erl"), - ?line Ret=c:c(bad_record_use), - ?line file:set_cwd(Cwd), - ?line error=Ret, - ok. - strict_record(Config) when is_list(Config) -> - ?line Priv = ?config(priv_dir, Config), - ?line file:set_cwd(?config(data_dir, Config)), - ?line Opts = [{outdir,Priv},report_errors], + Priv = proplists:get_value(priv_dir, Config), + ok = file:set_cwd(proplists:get_value(data_dir, Config)), + Opts = [{outdir,Priv},report_errors], M = record_access, - ?line {ok,M} = c:c(M, [strict_record_tests|Opts]), - ?line Turtle = test_strict(), + {ok,M} = c:c(M, [strict_record_tests|Opts]), + Turtle = test_strict(), - ?line {ok,M} = c:c(M, [no_strict_record_tests|Opts]), - ?line Turtle = test_sloppy(), + {ok,M} = c:c(M, [no_strict_record_tests|Opts]), + Turtle = test_sloppy(), %% The option first given wins. - ?line {ok,M} = c:c(M, [no_strict_record_tests,strict_record_tests|Opts]), - ?line Turtle = test_sloppy(), - ?line {ok,M} = c:c(M, [strict_record_tests,no_strict_record_tests|Opts]), - ?line Turtle = test_strict(), + {ok,M} = c:c(M, [no_strict_record_tests,strict_record_tests|Opts]), + Turtle = test_sloppy(), + {ok,M} = c:c(M, [strict_record_tests,no_strict_record_tests|Opts]), + Turtle = test_strict(), %% Default (possibly influenced by ERL_COMPILER_OPTIONS). - ?line {ok,M} = c:c(M, [{outdir,Priv},report_errors]), - ?line try + {ok,M} = c:c(M, [{outdir,Priv},report_errors]), + try {1,2} = record_access:test(Turtle), {comment,"Default: no_strict_record_tests"} catch @@ -653,7 +635,7 @@ strict_record(Config) when is_list(Config) -> test_strict() -> Turtle = record_access:turtle(), - ?line try + try record_access:test(Turtle) catch error:{badrecord,tortoise} -> @@ -666,81 +648,41 @@ test_sloppy() -> {1,2} = record_access:test(Turtle), Turtle. -missing_testheap(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), - Opts = [{outdir,PrivDir}], - OldPath = code:get_path(), - try - code:add_patha(PrivDir), - c:c(filename:join(DataDir, "missing_testheap1"), Opts), - c:c(filename:join(DataDir, "missing_testheap2"), Opts), - ?line ok = test(fun() -> - missing_testheap1:f({a,self()},{state,true,b}) - end, {a,b}), - ?line ok = test(fun() -> - missing_testheap2:f({a,self()},16#80000000) end, - bigger) - after - code:set_path(OldPath), - file:delete(filename:join(PrivDir, "missing_testheap1.beam")), - file:delete(filename:join(PrivDir, "missing_testheap2.beam")) - end, - ok. - -test(Fun, Result) -> - test(500, Fun, Result, []). - -test(0, _, _, _) -> - ok; -test(Iter, Fun, Result, Filler) -> - spawn(?MODULE, init, [self(), Fun, list_to_tuple(Filler)]), - receive - {result, Result} -> - test(Iter-1, Fun, Result, [0|Filler]); - {result, Other} -> - io:format("Expected ~p; got ~p~n", [Result, Other]), - test_server:fail() - end. - -init(ReplyTo, Fun, _Filler) -> - ReplyTo ! {result, Fun()}. - env(Config) when is_list(Config) -> - ?line {Simple,Target} = files(Config, "file_1"), - ?line {ok,Cwd} = file:get_cwd(), - ?line ok = file:set_cwd(filename:dirname(Target)), + {Simple,Target} = get_files(Config, simple, env), + {ok,Cwd} = file:get_cwd(), + ok = file:set_cwd(filename:dirname(Target)), true = os:putenv("ERL_COMPILER_OPTIONS", "binary"), try env_1(Simple, Target) after true = os:putenv("ERL_COMPILER_OPTIONS", "ignore_me"), - file:set_cwd(Cwd), - file:delete(Target), - file:del_dir(filename:dirname(Target)) + file:set_cwd(Cwd), + file:delete(Target), + file:del_dir(filename:dirname(Target)) end, ok. env_1(Simple, Target) -> %% file - ?line {ok,simple,<<_/binary>>} = compile:file(Simple), - ?line {ok,simple} = compile:noenv_file(Simple, [debug_info]), - ?line true = exists(Target), - ?line {ok,{simple,[{abstract_code,Abstr0}]}} = + {ok,simple,<<_/binary>>} = compile:file(Simple), + {ok,simple} = compile:noenv_file(Simple, [debug_info]), + true = exists(Target), + {ok,{simple,[{abstract_code,Abstr0}]}} = beam_lib:chunks(Target, [abstract_code]), - ?line {raw_abstract_v1,Forms} = Abstr0, + {raw_abstract_v1,Forms} = Abstr0, %% forms - ?line true = os:putenv("ERL_COMPILER_OPTIONS", "strong_validation"), - ?line {ok,simple} = compile:forms(Forms), - ?line {ok,simple,<<"FOR1",_/binary>>} = compile:noenv_forms(Forms, []), + true = os:putenv("ERL_COMPILER_OPTIONS", "strong_validation"), + {ok,simple} = compile:forms(Forms), + {ok,simple,<<"FOR1",_/binary>>} = compile:noenv_forms(Forms, []), %% output_generated - ?line false = compile:output_generated([]), - ?line true = compile:noenv_output_generated([]), + false = compile:output_generated([]), + true = compile:noenv_output_generated([]), - ?line ok = file:delete(Target), + ok = file:delete(Target), ok. @@ -748,55 +690,192 @@ env_1(Simple, Target) -> %% compile the generated Core Erlang files. core(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(5)), - ?line PrivDir = ?config(priv_dir, Config), - ?line Outdir = filename:join(PrivDir, "core"), - ?line ok = file:make_dir(Outdir), - - ?line Wc = filename:join(filename:dirname(code:which(?MODULE)), "*.beam"), - ?line TestBeams = filelib:wildcard(Wc), - ?line Abstr = [begin {ok,{Mod,[{abstract_code, + PrivDir = proplists:get_value(priv_dir, Config), + Outdir = filename:join(PrivDir, "core"), + ok = file:make_dir(Outdir), + + TestBeams = get_unique_beam_files(), + Abstr = [begin {ok,{Mod,[{abstract_code, {raw_abstract_v1,Abstr}}]}} = beam_lib:chunks(Beam, [abstract_code]), {Mod,Abstr} end || Beam <- TestBeams], - ?line Res = test_lib:p_run(fun(F) -> do_core(F, Outdir) end, Abstr), - ?line test_server:timetrap_cancel(Dog), - Res. - + test_lib:p_run(fun(F) -> do_core(F, Outdir) end, Abstr). do_core({M,A}, Outdir) -> try - {ok,M,Core} = compile:forms(A, [to_core,report]), - CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), - CorePP = core_pp:format(Core), - ok = file:write_file(CoreFile, CorePP), - case compile:file(CoreFile, [clint,from_core,binary]) of - {ok,M,_} -> - ok = file:delete(CoreFile); - Other -> - io:format("*** core_lint failure '~p' for ~s\n", - [Other,CoreFile]), - error - end - catch Class:Error -> + do_core_1(M, A, Outdir) + catch + throw:{error,Error} -> + io:format("*** compilation failure '~p' for module ~s\n", + [Error,M]), + error; + Class:Error -> io:format("~p: ~p ~p\n~p\n", [M,Class,Error,erlang:get_stacktrace()]), error end. +do_core_1(M, A, Outdir) -> + {ok,M,Core0} = compile:forms(A, [to_core]), + CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), + CorePP = core_pp:format(Core0), + ok = file:write_file(CoreFile, CorePP), + + %% Parse the .core file and return the result as Core Erlang Terms. + Core = case compile:file(CoreFile, [report_errors,from_core,no_copt,to_core,binary]) of + {ok,M,Core1} -> Core1; + Other -> throw({error,Other}) + end, + ok = file:delete(CoreFile), + + %% Compile as usual (including optimizations). + compile_forms(Core, [clint,from_core,binary]), + + %% Don't optimize to test that we are not dependent + %% on the Core Erlang optmimization passes. + %% (Example of a previous bug: The core_parse pass + %% would not turn map literals into #c_literal{} + %% records; if sys_core_fold was run it would fix + %% that; if sys_core_fold was not run v3_kernel would + %% crash.) + compile_forms(Core, [clint,from_core,no_copt,binary]), + + ok. + +compile_forms(Forms, Opts) -> + case compile:forms(Forms, [report_errors|Opts]) of + {ok,[],_} -> ok; + Other -> throw({error,Other}) + end. + +%% Pretty-print core and read it back. Should be identical. + +core_roundtrip(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + Outdir = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)), + ok = file:make_dir(Outdir), + + TestBeams = get_unique_beam_files(), + test_lib:p_run(fun(F) -> do_core_roundtrip(F, Outdir) end, TestBeams). + +do_core_roundtrip(Beam, Outdir) -> + try + {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr}}]}} = + beam_lib:chunks(Beam, [abstract_code]), + do_core_roundtrip_1(Mod, Abstr, Outdir) + catch + throw:{error,Error} -> + io:format("*** compilation failure '~p' for file ~s\n", + [Error,Beam]), + error; + Class:Error -> + io:format("~p: ~p ~p\n~p\n", + [Beam,Class,Error,erlang:get_stacktrace()]), + error + end. + +do_core_roundtrip_1(Mod, Abstr, Outdir) -> + {ok,Mod,Core0} = compile:forms(Abstr, [to_core0]), + do_core_roundtrip_2(Mod, Core0, Outdir), + + %% Primarily, test that annotations are accepted for all + %% constructs. Secondarily, smoke test cerl_trees:label/1. + {Core1,_} = cerl_trees:label(Core0), + do_core_roundtrip_2(Mod, Core1, Outdir), + + %% Run the inliner to force generation of variables + %% with numeric names. + {ok,Mod,Core2} = compile:forms(Abstr, [inline,to_core]), + do_core_roundtrip_2(Mod, Core2, Outdir). + +do_core_roundtrip_2(M, Core0, Outdir) -> + CoreFile = filename:join(Outdir, atom_to_list(M)++".core"), + CorePP = core_pp:format_all(Core0), + ok = file:write_file(CoreFile, CorePP), + + %% Parse the .core file and return the result as Core Erlang Terms. + Core2 = case compile:file(CoreFile, [report_errors,from_core, + no_copt,to_core,binary]) of + {ok,M,Core1} -> Core1; + Other -> throw({error,Other}) + end, + Core = undo_var_translation(Core2), + ok = file:delete(CoreFile), + + case cmp_core(Core0, Core, M) of + true -> ok; + false -> error + end, + + ok. + +undo_var_translation(Tree) -> + F = fun(Node) -> + case cerl:is_c_var(Node) of + true -> + Name0 = cerl:var_name(Node), + try atom_to_list(Name0) of + "_X"++Name -> + cerl:update_c_var(Node, list_to_atom(Name)); + "_"++Name -> + cerl:update_c_var(Node, list_to_atom(Name)); + _ -> + Node + catch + error:badarg -> + Node + + end; + false -> + Node + end + end, + cerl_trees:map(F, Tree). + +cmp_core(E, E, _Mod) -> + true; +cmp_core(M1, M2, Mod) -> + cmp_core_fs(cerl:module_defs(M1), cerl:module_defs(M2), Mod). + +cmp_core_fs([F1|T1], [F2|T2], Mod) -> + cmp_core_f(F1, F2, Mod) andalso cmp_core_fs(T1, T2, Mod); +cmp_core_fs([], [], _Mod) -> + true; +cmp_core_fs(_, _, _Mod) -> + false. + +cmp_core_f(E, E, _Mod) -> + true; +cmp_core_f({Name,F1}, {Name,F2}, Mod) -> + case diff(F1, F2) of + F1 -> + true; + Diff -> + io:format("~p ~p:\n~p\n", [Mod,Name,Diff]), + false + end. + +diff(E, E) -> + E; +diff([H1|T1], [H2|T2]) -> + [diff(H1, H2)|diff(T1, T2)]; +diff(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> + L = diff(tuple_to_list(T1), tuple_to_list(T2)), + list_to_tuple(L); +diff(E1, E2) -> + {'DIFF',E1,E2}. + + %% Compile to Beam assembly language (.S) and then try to %% run .S through the compiler again. asm(Config) when is_list(Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(20)), - ?line PrivDir = ?config(priv_dir, Config), - ?line Outdir = filename:join(PrivDir, "asm"), - ?line ok = file:make_dir(Outdir), - - ?line Wc = filename:join(filename:dirname(code:which(?MODULE)), "*.beam"), - ?line TestBeams = filelib:wildcard(Wc), - ?line Res = test_lib:p_run(fun(F) -> do_asm(F, Outdir) end, TestBeams), - ?line test_server:timetrap_cancel(Dog), + PrivDir = proplists:get_value(priv_dir, Config), + Outdir = filename:join(PrivDir, "asm"), + ok = file:make_dir(Outdir), + + TestBeams = get_unique_beam_files(), + Res = test_lib:p_run(fun(F) -> do_asm(F, Outdir) end, TestBeams), Res. @@ -824,7 +903,7 @@ do_asm(Beam, Outdir) -> end. sys_pre_attributes(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), File = filename:join(DataDir, "attributes.erl"), Mod = attributes, CommonOpts = [binary,report,verbose, @@ -854,6 +933,183 @@ sys_pre_attributes(Config) -> [report,verbose]), ok. +%% Test the dialyzer option to cover more code. +dialyzer(Config) -> + Priv = proplists:get_value(priv_dir, Config), + ok = file:set_cwd(proplists:get_value(data_dir, Config)), + Opts = [{outdir,Priv},report_errors], + M = dialyzer_test, + {ok,M} = c:c(M, [dialyzer|Opts]), + [{a,b,c}] = M:M(), + + %% Cover huge line numbers without the 'dialyzer' option. + {ok,M} = c:c(M, Opts), + [{a,b,c}] = M:M(), + ok. + + +%% Test that warnings contain filenames and line numbers. +warnings(_Config) -> + Files = get_unique_files(".erl"), + test_lib:p_run(fun do_warnings/1, Files). + +do_warnings(F) -> + {ok,_,_,Ws} = compile:file(F, [binary,bin_opt_info,return]), + do_warnings_1(Ws, F). + +do_warnings_1([{"no_file",Ws}|_], F) -> + io:format("~s:\nMissing file for warnings: ~p\n", + [F,Ws]), + error; +do_warnings_1([{Name,Ws}|T], F) -> + case filename:extension(Name) of + ".erl" -> + do_warnings_2(Ws, T, F); + _ -> + io:format("~s:\nNo .erl extension\n", [F]), + error + end; +do_warnings_1([], _) -> ok. + +do_warnings_2([{Int,_,_}=W|T], Next, F) -> + if + is_integer(Int) -> + do_warnings_2(T, Next, F); + true -> + io:format("~s:\nMissing line number: ~p\n", + [F,W]), + error + end; +do_warnings_2([], Next, F) -> + do_warnings_1(Next, F). + + +%% Test that the compile:pre_load/0 function (used by 'erlc') +%% pre-loads the modules that are used by a typical compilation. + +pre_load_check(Config) -> + case test_server:is_cover() of + true -> + {skip,"Cover is running"}; + false -> + try + do_pre_load_check(Config) + after + dbg:stop_clear() + end + end. + +do_pre_load_check(Config) -> + DataDir = ?config(data_dir, Config), + Simple = filename:join(DataDir, "simple.erl"), + Big = filename:join(DataDir, "big.erl"), + {ok,_} = dbg:tracer(process, {fun pre_load_trace/2,[]}), + dbg:p(self(), call), + dbg:p(new, call), + {ok,_} = dbg:tpl({?MODULE,get_trace_data,0}, []), + {ok,_} = dbg:tp({code,ensure_modules_loaded,1}, []), + + %% Compile a simple module using the erl_compile interface + %% to find out the modules that are pre-loaded by + %% compile:pre_load/0. + Opts = #options{specific=[binary]}, + {ok,simple,_} = compile:compile(Simple, "", Opts), + [{code,ensure_modules_loaded,[PreLoaded0]}] = get_trace_data(), + PreLoaded1 = ordsets:from_list(PreLoaded0), + + %% Since 'compile' is the function doing the pre-loaded, + %% it is useless to include it in the list. + case ordsets:is_element(compile, PreLoaded1) of + true -> + io:put_chars("The 'compile' module should not be included " + "in the list of modules to be pre-loaded."), + ?t:fail(compile); + false -> + [] + end, + PreLoaded = ordsets:add_element(compile, PreLoaded1), + + %% Now unload all pre-loaded modules and all modules in + %% compiler application. Then compile a module to find + %% which modules that get loaded. + CompilerMods = compiler_modules(), + Unload = ordsets:union(ordsets:from_list(CompilerMods), PreLoaded), + _ = [begin + code:delete(M), + code:purge(M) + end || M <- Unload], + + {ok,_} = dbg:ctp({code,ensure_modules_loaded,1}), + {ok,_} = dbg:tp({code,ensure_loaded,1}, []), + {ok,big,_} = compile:file(Big, [binary]), + WasLoaded0 = get_trace_data(), + WasLoaded1 = [M || {code,ensure_loaded,[M]} <- WasLoaded0], + WasLoaded = ordsets:from_list(WasLoaded1), + + %% Check for modules that should have been pre-loaded. + case ordsets:subtract(WasLoaded, PreLoaded) of + [] -> + ok; + [_|_]=NotPreLoaded -> + io:format("The following modules were used " + "but not pre-loaded:\n~p\n", + [NotPreLoaded]), + ?t:fail({not_preload,NotPreLoaded}) + end, + + %% Check for modules that should not be pre-loaded. + case ordsets:subtract(PreLoaded, WasLoaded) of + [] -> + ok; + [_|_]=NotUsed -> + io:format("The following modules were pre-loaded" + " but not used:\n~p\n", + [NotUsed]), + ?t:fail({not_used,NotUsed}) + end, + + ok. + +get_trace_data() -> + %% Apparantely, doing a receive at the beginning of + %% a traced function can cause extra trace messages. + %% To avoid that, don't do the receive in this function. + do_get_trace_data(). + +do_get_trace_data() -> + receive + {trace_data,Data} -> Data + end. + +pre_load_trace({trace,Pid,call,{?MODULE,get_trace_data,[]}}, Acc) -> + Pid ! {trace_data,Acc}, + []; +pre_load_trace({trace,_,call,MFA}, Acc) -> + [MFA|Acc]. + +compiler_modules() -> + Wc = filename:join([code:lib_dir(compiler),"ebin","*.beam"]), + Ms = filelib:wildcard(Wc), + FN = filename, + [list_to_atom(FN:rootname(FN:basename(M), ".beam")) || M <- Ms]. + +%% Test that ERL_COMPILER_OPTIONS are correctly retrieved +%% by env_compiler_options/0 + +env_compiler_options(_Config) -> + Cases = [ + {"bin_opt_info", [bin_opt_info]}, + {"'S'", ['S']}, + {"{source, \"test.erl\"}", [{source, "test.erl"}]}, + {"[{d,macro_one,1},{d,macro_two}]", [{d, macro_one, 1}, {d, macro_two}]}, + {"[warn_export_all, warn_export_vars]", [warn_export_all, warn_export_vars]} + ], + F = fun({Env, Expected}) -> + true = os:putenv("ERL_COMPILER_OPTIONS", Env), + Expected = compile:env_compiler_options() + end, + lists:foreach(F, Cases). + %%% %%% Utilities. %%% @@ -865,3 +1121,14 @@ compile_and_verify(Name, Target, Opts) -> beam_lib:chunks(Target, [compile_info]), {options,BeamOpts} = lists:keyfind(options, 1, CInfo), Opts = BeamOpts. + +get_unique_beam_files() -> + get_unique_files(".beam"). + +get_unique_files(Ext) -> + Wc = filename:join(filename:dirname(code:which(?MODULE)), "*"++Ext), + [F || F <- filelib:wildcard(Wc), not is_cloned(F, Ext)]. + +is_cloned(File, Ext) -> + Mod = list_to_atom(filename:basename(File, Ext)), + test_lib:is_cloned_mod(Mod). diff --git a/lib/compiler/test/compile_SUITE_data/attributes.erl b/lib/compiler/test/compile_SUITE_data/attributes.erl index 9c3451d272..140965c97d 100644 --- a/lib/compiler/test/compile_SUITE_data/attributes.erl +++ b/lib/compiler/test/compile_SUITE_data/attributes.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compile_SUITE_data/bad_record_use.erl b/lib/compiler/test/compile_SUITE_data/bad_record_use.erl deleted file mode 100644 index c2adbfa8de..0000000000 --- a/lib/compiler/test/compile_SUITE_data/bad_record_use.erl +++ /dev/null @@ -1,28 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(bad_record_use). --export([test/0]). - --record(bad_use, {a=undefined, - b=undefined, - c=undefined}). - -test() -> - NewRecord=#bad_use{a=1, b=2, a=2}. - diff --git a/lib/compiler/test/compile_SUITE_data/bad_record_use2.erl b/lib/compiler/test/compile_SUITE_data/bad_record_use2.erl deleted file mode 100644 index 1de44b2df4..0000000000 --- a/lib/compiler/test/compile_SUITE_data/bad_record_use2.erl +++ /dev/null @@ -1,29 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(bad_record_use2). --export([test/0]). - --record(bad_use, {a=undefined, - b=undefined, - c=undefined}). - -test() -> - R=#bad_use{a=1, b=2}, - R2=R#bad_use{a=1, b=2, a=2}, - ok. diff --git a/lib/compiler/test/compile_SUITE_data/big.erl b/lib/compiler/test/compile_SUITE_data/big.erl index 4cd8e15f13..2e54ee8660 100644 --- a/lib/compiler/test/compile_SUITE_data/big.erl +++ b/lib/compiler/test/compile_SUITE_data/big.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compile_SUITE_data/dialyzer_test.erl b/lib/compiler/test/compile_SUITE_data/dialyzer_test.erl new file mode 100644 index 0000000000..0df737ba4c --- /dev/null +++ b/lib/compiler/test/compile_SUITE_data/dialyzer_test.erl @@ -0,0 +1,40 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. 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% +%% + +-module(dialyzer_test). +-export([?MODULE/0,turtle/0,test/1,huge/1]). + +-record(turtle, {a,b,c}). +-record(tortoise, {a,b,c}). + +?MODULE() -> + [{a,b,c}]. + +turtle() -> + #turtle{a=1,b=2,c=3}. + +test(T) -> + {T#tortoise.a,T#tortoise.b}. + +-file("dialyzer_test", 100000000). + +huge(X) -> + #turtle{a=42,b=100,c=511}, + X#tortoise.a. diff --git a/lib/compiler/test/compile_SUITE_data/include/simple.hrl b/lib/compiler/test/compile_SUITE_data/include/simple.hrl index cbe6e4f1a4..065378f820 100644 --- a/lib/compiler/test/compile_SUITE_data/include/simple.hrl +++ b/lib/compiler/test/compile_SUITE_data/include/simple.hrl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compile_SUITE_data/missing_testheap1.erl b/lib/compiler/test/compile_SUITE_data/missing_testheap1.erl deleted file mode 100644 index 65ee11541d..0000000000 --- a/lib/compiler/test/compile_SUITE_data/missing_testheap1.erl +++ /dev/null @@ -1,35 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(missing_testheap1). - --compile(export_all). -%%-export([Function/Arity, ...]). --record(state,{e1,e2}). - -f({a,DpId},State) when State == -#state{e1=true, - e2=a} -> - {a,a}; - -f({a,DpId},State) when State == -#state{e1=true, - e2=b} -> - {a,b}. - - diff --git a/lib/compiler/test/compile_SUITE_data/missing_testheap2.erl b/lib/compiler/test/compile_SUITE_data/missing_testheap2.erl deleted file mode 100644 index 014210fa5a..0000000000 --- a/lib/compiler/test/compile_SUITE_data/missing_testheap2.erl +++ /dev/null @@ -1,29 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% --module(missing_testheap2). - --compile(export_all). - -f({a,DpId},16#7fffffff) -> - big; - -f({a,DpId},16#80000000) -> - bigger. - - diff --git a/lib/compiler/test/compile_SUITE_data/record_access.erl b/lib/compiler/test/compile_SUITE_data/record_access.erl index c89f9ad7c7..537e9c8536 100644 --- a/lib/compiler/test/compile_SUITE_data/record_access.erl +++ b/lib/compiler/test/compile_SUITE_data/record_access.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compile_SUITE_data/simple.erl b/lib/compiler/test/compile_SUITE_data/simple.erl index 0c1c70a778..d8324dafaf 100644 --- a/lib/compiler/test/compile_SUITE_data/simple.erl +++ b/lib/compiler/test/compile_SUITE_data/simple.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/compile_SUITE_data/wrong_module_name.erl b/lib/compiler/test/compile_SUITE_data/wrong_module_name.erl index ea437556a4..77dd4747f0 100644 --- a/lib/compiler/test/compile_SUITE_data/wrong_module_name.erl +++ b/lib/compiler/test/compile_SUITE_data/wrong_module_name.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009. All Rights Reserved. +%% Copyright Ericsson AB 2009-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl index 428ad65364..b768f49e2c 100644 --- a/lib/compiler/test/core_SUITE.erl +++ b/lib/compiler/test/core_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -24,23 +25,24 @@ dehydrated_itracer/1,nested_tries/1, seq_in_guard/1,make_effect_seq/1,eval_is_boolean/1, unsafe_case/1,nomatch_shadow/1,reversed_annos/1, - map_core_test/1,eval_case/1,bad_boolean_guard/1]). + map_core_test/1,eval_case/1,bad_boolean_guard/1, + bs_shadowed_size_var/1 + ]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(comp(N), N(Config) when is_list(Config) -> try_it(N, Config)). init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = test_server:timetrap(?t:minutes(5)), - [{watchdog,Dog}|Config]. + Config. end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,5}}]. all() -> test_lib:recompile(?MODULE), @@ -50,7 +52,8 @@ groups() -> [{p,test_lib:parallel(), [dehydrated_itracer,nested_tries,seq_in_guard,make_effect_seq, eval_is_boolean,unsafe_case,nomatch_shadow,reversed_annos, - map_core_test,eval_case,bad_boolean_guard + map_core_test,eval_case,bad_boolean_guard, + bs_shadowed_size_var ]}]. @@ -78,13 +81,19 @@ end_per_group(_GroupName, Config) -> ?comp(map_core_test). ?comp(eval_case). ?comp(bad_boolean_guard). +?comp(bs_shadowed_size_var). + try_it(Mod, Conf) -> - Src = filename:join(?config(data_dir, Conf), atom_to_list(Mod)), + Src = filename:join(proplists:get_value(data_dir, Conf), + atom_to_list(Mod)), compile_and_load(Src, []), compile_and_load(Src, [no_copt]). compile_and_load(Src, Opts) -> {ok,Mod,Bin} = compile:file(Src, [from_core,report,time,binary|Opts]), {module,Mod} = code:load_binary(Mod, Mod, Bin), - ok = Mod:Mod(). + ok = Mod:Mod(), + _ = code:delete(Mod), + _ = code:purge(Mod), + ok. diff --git a/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core b/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core new file mode 100644 index 0000000000..0ade037e05 --- /dev/null +++ b/lib/compiler/test/core_SUITE_data/bs_shadowed_size_var.core @@ -0,0 +1,66 @@ +module 'bs_shadowed_size_var' + ['filter_essentials'/1, + 'bs_shadowed_size_var'/0] + attributes [] + +%% bs_shadowed_size_var() -> +%% [42|<<"abcde">>] = Mod:filter_essentials([<<42:32>>|<<5:32,"abcde">>]), +%% ok. + +'bs_shadowed_size_var'/0 = + fun () -> + case <> of + <> when 'true' -> + case apply 'filter_essentials'/1 + ([#{#<0>(8,1,'integer',['unsigned'|['big']]), + #<0>(8,1,'integer',['unsigned'|['big']]), + #<0>(8,1,'integer',['unsigned'|['big']]), + #<42>(8,1,'integer',['unsigned'|['big']])}#|#{#<0>(8,1,'integer',['unsigned'|['big']]), + #<0>(8,1,'integer',['unsigned'|['big']]), + #<0>(8,1,'integer',['unsigned'|['big']]), + #<5>(8,1,'integer',['unsigned'|['big']]), + #<97>(8,1,'integer',['unsigned'|['big']]), + #<98>(8,1,'integer',['unsigned'|['big']]), + #<99>(8,1,'integer',['unsigned'|['big']]), + #<100>(8,1,'integer',['unsigned'|['big']]), + #<101>(8,1,'integer',['unsigned'|['big']])}#]) of + <[42|#{#<97>(8,1,'integer',['unsigned'|['big']]), + #<98>(8,1,'integer',['unsigned'|['big']]), + #<99>(8,1,'integer',['unsigned'|['big']]), + #<100>(8,1,'integer',['unsigned'|['big']]), + #<101>(8,1,'integer',['unsigned'|['big']])}#]> when 'true' -> + 'ok' + ( <_cor0> when 'true' -> + primop 'match_fail' + ({'badmatch',_cor0}) + -| ['compiler_generated'] ) + end + ( <> when 'true' -> + ( primop 'match_fail' + ({'function_clause'}) + -| [{'function_name',{'bs_shadowed_size_var',0}}] ) + -| ['compiler_generated'] ) + end + +%% Reduced code from beam_asm inlined using the old inliner. + +'filter_essentials'/1 = + fun (_cor0) -> + case _cor0 of + <[#{#<Sz>(32,1,'integer',['unsigned','big']) }#|T]> when 'true' -> + let <_cor4> = + case T of + %% Variable 'Sz' repeated here. Should work. + <#{#<Sz>(32,1,'integer',['unsigned','big']), + #<Data>(Sz,8,'binary',['unsigned','big'])}#> when 'true' -> + Data + <_cor5> when 'true' -> + primop 'match_fail' + ({'case_clause',{_cor5}}) + end + in [Sz|_cor4] + <_cor5> when 'true' -> + primop 'match_fail' + ({'function_clause',_cor5}) + end +end diff --git a/lib/compiler/test/core_SUITE_data/map_core_test.core b/lib/compiler/test/core_SUITE_data/map_core_test.core index 2aa853d450..a75f6cf24f 100644 --- a/lib/compiler/test/core_SUITE_data/map_core_test.core +++ b/lib/compiler/test/core_SUITE_data/map_core_test.core @@ -7,11 +7,11 @@ module 'map_core_test' ['map_core_test'/0, fun () -> let <_cor0> = %% Line 15 - ~{::<'check','ok'>,::<1337,#{#<104>(8,1,'integer',['unsigned'|['big']]), + ~{'check'=>'ok',1337=>#{#<104>(8,1,'integer',['unsigned'|['big']]), #<101>(8,1,'integer',['unsigned'|['big']]), #<108>(8,1,'integer',['unsigned'|['big']]), #<108>(8,1,'integer',['unsigned'|['big']]), - #<111>(8,1,'integer',['unsigned'|['big']])}#>,::<'val',0>}~ + #<111>(8,1,'integer',['unsigned'|['big']])}#,'val'=>0}~ in let <M> = %% Line 15 apply 'id'/1 @@ -23,7 +23,7 @@ module 'map_core_test' ['map_core_test'/0, in %% Line 16 case apply 'call'/2 (M, _cor2) of - <~{~<1337,#{#<104>(8,1,'integer',['unsigned'|['big']]), + <~{1337:=#{#<104>(8,1,'integer',['unsigned'|['big']]), #<101>(8,1,'integer',['unsigned'|['big']]), #<108>(8,1,'integer',['unsigned'|['big']]), #<108>(8,1,'integer',['unsigned'|['big']]), @@ -39,7 +39,7 @@ module 'map_core_test' ['map_core_test'/0, #<32>(8,1,'integer',['unsigned'|['big']]), #<53>(8,1,'integer',['unsigned'|['big']]), #<32>(8,1,'integer',['unsigned'|['big']]), - #<54>(8,1,'integer',['unsigned'|['big']])}#>,~<'check','ok'>,~<'val',21>}~> when 'true' -> + #<54>(8,1,'integer',['unsigned'|['big']])}#,'check':='ok','val':=21}~> when 'true' -> %% Line 17 'ok' ( <_cor3> when 'true' -> @@ -51,7 +51,7 @@ module 'map_core_test' ['map_core_test'/0, %% Line 20 fun (_cor1,_cor0) -> case <_cor1,_cor0> of - <M = ~{~<1337,Bin>,~<'check',_cor8>,~<'val',Val>}~,[V|Vs]> when 'true' -> + <M = ~{1337:=Bin,'check':=_cor8,'val':=Val}~,[V|Vs]> when 'true' -> let <_cor3> = %% Line 21 call 'erlang':'+' @@ -67,7 +67,7 @@ module 'map_core_test' ['map_core_test'/0, (Val, V) in let <_cor5> = %% Line 21 - ~{~<1337,_cor4>,~<'val',_cor2>|M}~ + ~{1337:=_cor4,'val':=_cor2|M}~ in %% Line 21 apply 'call'/2 (_cor5, Vs) diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index 2de17e7653..376d2c8e9a 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -23,11 +24,13 @@ t_element/1,setelement/1,t_length/1,append/1,t_apply/1,bifs/1, eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1, unused_multiple_values_error/1,unused_multiple_values/1, - multiple_aliases/1,redundant_boolean_clauses/1,mixed_matching_clauses/1]). + multiple_aliases/1,redundant_boolean_clauses/1, + mixed_matching_clauses/1,unnecessary_building/1, + no_no_file/1]). -export([foo/0,foo/1,foo/2,foo/3]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -36,11 +39,13 @@ all() -> [{group,p}]. groups() -> - [{p,test_lib:parallel(), + [{p,[parallel], [t_element,setelement,t_length,append,t_apply,bifs, eq,nested_call_in_case,guard_try_catch,coverage, unused_multiple_values_error,unused_multiple_values, - multiple_aliases,redundant_boolean_clauses,mixed_matching_clauses]}]. + multiple_aliases,redundant_boolean_clauses, + mixed_matching_clauses,unnecessary_building, + no_no_file]}]. init_per_suite(Config) -> @@ -58,8 +63,8 @@ end_per_group(_GroupName, Config) -> t_element(Config) when is_list(Config) -> X = make_ref(), - ?line X = id(element(1, {X,y,z})), - ?line b = id(element(2, {a,b,c,d})), + X = id(element(1, {X,y,z})), + b = id(element(2, {a,b,c,d})), (fun() -> case {a,#{k=>X}} of {a,#{k:=X}}=Tuple -> @@ -70,40 +75,42 @@ t_element(Config) when is_list(Config) -> %% No optimization, but should work. Tuple = id({x,y,z}), Pos = id(3), - ?line x = id(element(1, Tuple)), - ?line c = id(element(Pos, {a,b,c,d})), - ?line X = id(element(Pos, {a,b,X,d})), - ?line z = id(element(Pos, Tuple)), + x = id(element(1, Tuple)), + c = id(element(Pos, {a,b,c,d})), + X = id(element(Pos, {a,b,X,d})), + z = id(element(Pos, Tuple)), %% Calls that will fail. - ?line {'EXIT',{badarg,_}} = (catch element(5, {a,b,c,d})), - ?line {'EXIT',{badarg,_}} = (catch element(5, {a,b,X,d})), - ?line {'EXIT',{badarg,_}} = (catch element(5.0, {a,b,X,d})), + {'EXIT',{badarg,_}} = (catch element(5, {a,b,c,d})), + {'EXIT',{badarg,_}} = (catch element(5, {a,b,X,d})), + {'EXIT',{badarg,_}} = (catch element(5.0, {a,b,X,d})), {'EXIT',{badarg,_}} = (catch element(2, not_a_tuple)), {'EXIT',{badarg,_}} = (catch element(2, [])), {'EXIT',{badarg,_}} = (catch element(2, Tuple == 3)), case id({a,b,c}) of {_,_,_}=Tup -> - ?line {'EXIT',{badarg,_}} = (catch element(4, Tup)) + {'EXIT',{badarg,_}} = (catch element(4, Tup)) end, + {'EXIT',{badarg,_}} = (catch element(1, tuple_size(Tuple))), ok. setelement(Config) when is_list(Config) -> X = id(b), New = id([1,2,3]), - ?line {y,b,c} = id(setelement(1, {a,b,c}, y)), - ?line {y,b,c} = id(setelement(1, {a,X,c}, y)), - ?line {a,y,c} = id(setelement(2, {a,X,c}, y)), - ?line {a,[1,2,3],c} = id(setelement(2, {a,b,c}, New)), - ?line {a,[1,2,3],c} = id(setelement(2, {a,X,c}, New)), - ?line {a,b,[1,2,3]} = id(setelement(3, {a,b,c}, New)), - ?line {a,b,[1,2,3]} = id(setelement(3, {a,X,c}, New)), + {y,b,c} = id(setelement(1, {a,b,c}, y)), + {y,b,c} = id(setelement(1, {a,X,c}, y)), + {a,y,c} = id(setelement(2, {a,X,c}, y)), + {a,[1,2,3],c} = id(setelement(2, {a,b,c}, New)), + {a,[1,2,3],c} = id(setelement(2, {a,X,c}, New)), + {a,b,[1,2,3]} = id(setelement(3, {a,b,c}, New)), + {a,b,[1,2,3]} = id(setelement(3, {a,X,c}, New)), - ?line {'EXIT',{badarg,_}} = (catch setelement_crash({a,b,c,d,e,f})), - ?line error = setelement_crash_2({a,b,c,d,e,f}, <<42>>), + {'EXIT',{badarg,_}} = (catch setelement_crash({a,b,c,d,e,f})), + error = setelement_crash_2({a,b,c,d,e,f}, <<42>>), {'EXIT',{badarg,_}} = (catch setelement(1, not_a_tuple, New)), + {'EXIT',{badarg,_}} = (catch setelement(3, {a,b}, New)), ok. @@ -127,19 +134,19 @@ setelement_crash_2(Tuple, Bin) -> t_length(Config) when is_list(Config) -> Blurf = id({blurf,a,b}), Tail = id([42,43,44,45]), - ?line 0 = id(length([])), - ?line 1 = id(length([x])), - ?line 2 = id(length([x,Blurf])), - ?line 4 = id(length([x,Blurf,a,b])), + 0 = id(length([])), + 1 = id(length([x])), + 2 = id(length([x,Blurf])), + 4 = id(length([x,Blurf,a,b])), %% No or partial optimization. - ?line 4 = length(Tail), - ?line 5 = id(length([x|Tail])), + 4 = length(Tail), + 5 = id(length([x|Tail])), %% Will fail. - ?line {'EXIT',{badarg,_}} = (catch id(length([a,b|c]))), - ?line {'EXIT',{badarg,_}} = (catch id(length([a,Blurf|c]))), - ?line {'EXIT',{badarg,_}} = (catch id(length(atom))), + {'EXIT',{badarg,_}} = (catch id(length([a,b|c]))), + {'EXIT',{badarg,_}} = (catch id(length([a,Blurf|c]))), + {'EXIT',{badarg,_}} = (catch id(length(atom))), ok. @@ -151,34 +158,34 @@ t_length(Config) when is_list(Config) -> append(Config) when is_list(Config) -> A = id(0), - ?line [a,b,c,d,e,f,g,h,i,j,k] = id(?APPEND([a,b,c,d,e,f],[g,h,i,j,k])), - ?line [a,b,c,d,e] = id(?APPEND([a,b,c],id([d,e]))), - ?line [0,1,2,3,4,5,6] = id(?APPEND([A,1,2,3],[4,5,6])), - ?line {'EXIT',{badarg,_}} = (catch id(?APPEND([A|blurf],[4,5,6]))), + [a,b,c,d,e,f,g,h,i,j,k] = id(?APPEND([a,b,c,d,e,f],[g,h,i,j,k])), + [a,b,c,d,e] = id(?APPEND([a,b,c],id([d,e]))), + [0,1,2,3,4,5,6] = id(?APPEND([A,1,2,3],[4,5,6])), + {'EXIT',{badarg,_}} = (catch id(?APPEND([A|blurf],[4,5,6]))), ok. t_apply(Config) when is_list(Config) -> - ?line ok = apply(?MODULE, foo, []), - ?line 4 = apply(?MODULE, foo, [3]), - ?line 7 = apply(?MODULE, foo, [3,4]), - ?line 12 = apply(?MODULE, foo, [id(8),4]), - ?line 21 = apply(?MODULE, foo, [8,id(9),4]), - ?line 20 = apply(?MODULE, foo, [8,8,id(4)]), - ?line 24 = apply(?MODULE, foo, [id(10),10,4]), + ok = apply(?MODULE, foo, []), + 4 = apply(?MODULE, foo, [3]), + 7 = apply(?MODULE, foo, [3,4]), + 12 = apply(?MODULE, foo, [id(8),4]), + 21 = apply(?MODULE, foo, [8,id(9),4]), + 20 = apply(?MODULE, foo, [8,8,id(4)]), + 24 = apply(?MODULE, foo, [id(10),10,4]), M = id(?MODULE), - ?line ok = apply(M, foo, []), - ?line 4 = apply(M, foo, [3]), - ?line 16.0 = apply(M, foo, [12.0,4]), + ok = apply(M, foo, []), + 4 = apply(M, foo, [3]), + 16.0 = apply(M, foo, [12.0,4]), %% Will fail. - ?line {'EXIT',{badarg,_}} = (catch apply([a,b,c], foo, [])), - ?line {'EXIT',{badarg,_}} = (catch apply(42, foo, [])), - ?line {'EXIT',{badarg,_}} = (catch apply(?MODULE, 45, [xx])), - ?line {'EXIT',{badarg,_}} = (catch apply(?MODULE, foo, {a,b})), - ?line {'EXIT',{badarg,_}} = (catch apply(M, M, [1009|10010])), - ?line {'EXIT',{badarg,_}} = (catch apply(?MODULE, foo, [10000|9999])), - ?line {'EXIT',{badarg,_}} = (catch apply(?MODULE, foo, a)), + {'EXIT',{badarg,_}} = (catch apply([a,b,c], foo, [])), + {'EXIT',{badarg,_}} = (catch apply(42, foo, [])), + {'EXIT',{badarg,_}} = (catch apply(?MODULE, 45, [xx])), + {'EXIT',{badarg,_}} = (catch apply(?MODULE, foo, {a,b})), + {'EXIT',{badarg,_}} = (catch apply(M, M, [1009|10010])), + {'EXIT',{badarg,_}} = (catch apply(?MODULE, foo, [10000|9999])), + {'EXIT',{badarg,_}} = (catch apply(?MODULE, foo, a)), ok. @@ -195,20 +202,23 @@ foo(A, B, C) -> A + B + C. bifs(Config) when is_list(Config) -> - ?line <<1,2,3,4>> = id(list_to_binary([1,2,3,4])), + <<1,2,3,4>> = id(list_to_binary([1,2,3,4])), + K = {a,key}, + V = {a,value}, + {ok,#{K:=V}} = id(list_to_tuple([ok,#{K=>V}])), ok. -define(CMP_SAME(A0, B), (fun(A) -> true = A == B, false = A /= B end)(id(A0))). -define(CMP_DIFF(A0, B), (fun(A) -> false = A == B, true = A /= B end)(id(A0))). eq(Config) when is_list(Config) -> - ?line ?CMP_SAME([a,b,c], [a,b,c]), - ?line ?CMP_SAME([42.0], [42.0]), - ?line ?CMP_SAME([42], [42]), - ?line ?CMP_SAME([42.0], [42]), + ?CMP_SAME([a,b,c], [a,b,c]), + ?CMP_SAME([42.0], [42.0]), + ?CMP_SAME([42], [42]), + ?CMP_SAME([42.0], [42]), - ?line ?CMP_DIFF(a, [a]), - ?line ?CMP_DIFF(a, {1,2,3}), + ?CMP_DIFF(a, [a]), + ?CMP_DIFF(a, {1,2,3}), ?CMP_SAME(#{a=>1.0,b=>2}, #{b=>2.0,a=>1}), ?CMP_SAME(#{a=>[1.0],b=>[2]}, #{b=>[2.0],a=>[1]}), @@ -224,15 +234,17 @@ eq(Config) when is_list(Config) -> %% OTP-7117. nested_call_in_case(Config) when is_list(Config) -> - ?line PrivDir = ?config(priv_dir, Config), - ?line Dir = filename:dirname(code:which(?MODULE)), - ?line Core = filename:join(Dir, "nested_call_in_case"), - ?line Opts = [from_core,{outdir,PrivDir}|test_lib:opt_opts(?MODULE)], - ?line io:format("~p", [Opts]), - ?line {ok,Mod} = c:c(Core, Opts), - ?line yes = Mod:a([1,2,3], 2), - ?line no = Mod:a([1,2,3], 4), - ?line {'EXIT',_} = (catch Mod:a(not_a_list, 42)), + PrivDir = proplists:get_value(priv_dir, Config), + Dir = test_lib:get_data_dir(Config), + Core = filename:join(Dir, "nested_call_in_case"), + Opts = [from_core,{outdir,PrivDir}|test_lib:opt_opts(?MODULE)], + io:format("~p", [Opts]), + {ok,Mod} = c:c(Core, Opts), + yes = Mod:a([1,2,3], 2), + no = Mod:a([1,2,3], 4), + {'EXIT',_} = (catch Mod:a(not_a_list, 42)), + _ = code:delete(Mod), + _ = code:purge(Mod), ok. guard_try_catch(_Config) -> @@ -252,15 +264,20 @@ do_guard_try_catch(K, V) -> false end. +-record(cover_opt_guard_try, {list=[]}). + coverage(Config) when is_list(Config) -> - ?line {'EXIT',{{case_clause,{a,b,c}},_}} = + {'EXIT',{{case_clause,{a,b,c}},_}} = (catch cover_will_match_list_type({a,b,c})), - ?line {'EXIT',{{case_clause,{a,b,c,d}},_}} = + {'EXIT',{{case_clause,{a,b,c,d}},_}} = (catch cover_will_match_list_type({a,b,c,d})), - ?line a = cover_remove_non_vars_alias({a,b,c}), - ?line error = cover_will_match_lit_list(), + a = cover_remove_non_vars_alias({a,b,c}), + error = cover_will_match_lit_list(), {ok,[a]} = cover_is_safe_bool_expr(a), + ok = cover_opt_guard_try(#cover_opt_guard_try{list=[a]}), + error = cover_opt_guard_try(#cover_opt_guard_try{list=[]}), + %% Make sure that we don't attempt to make literals %% out of pids. (Putting a pid into a #c_literal{} %% would crash later compiler passes.) @@ -273,6 +290,12 @@ coverage(Config) when is_list(Config) -> error = bsm_an_inlined(<<1,2,3>>, Config), error = bsm_an_inlined([], Config), + %% Cover eval_rel_op/4. + Tuple = id({a,b}), + false = case Tuple of + {_,_} -> + Tuple =:= true + end, ok. cover_will_match_list_type(A) -> @@ -314,12 +337,20 @@ cover_is_safe_bool_expr(X) -> false end. +cover_opt_guard_try(Msg) -> + if + length(Msg#cover_opt_guard_try.list) =/= 1 -> + error; + true -> + ok + end. + bsm_an_inlined(<<_:8>>, _) -> ok; bsm_an_inlined(_, _) -> error. unused_multiple_values_error(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), - Dir = filename:dirname(code:which(?MODULE)), + PrivDir = proplists:get_value(priv_dir, Config), + Dir = test_lib:get_data_dir(Config), Core = filename:join(Dir, "unused_multiple_values_error"), Opts = [no_copt,clint,return,from_core,{outdir,PrivDir} |test_lib:opt_opts(?MODULE)], @@ -400,4 +431,72 @@ mixed_matching_clauses(Config) when is_list(Config) -> end, ok. +unnecessary_building(Config) when is_list(Config) -> + Term1 = do_unnecessary_building_1(test_lib:id(a)), + [{a,a},{a,a}] = Term1, + 7 = erts_debug:size(Term1), + + %% The Input term should not be rebuilt (thus, it should + %% only be counted once in the size of the combined term). + Input = test_lib:id({a,b,c}), + Term2 = test_lib:id(do_unnecessary_building_2(Input)), + {b,[{a,b,c},none],x} = Term2, + 4+4+4+2 = erts_debug:size([Term2|Input]), + + ok. + +do_unnecessary_building_1(S) -> + %% The tuple must only be built once. + F0 = F1 = {S,S}, + [F0,F1]. + +do_unnecessary_building_2({a,_,_}=T) -> + %% The T term should not be rebuilt. + {b, + [_,_] = [T,none], + x}. + +%% This test tests that v3_core has provided annotations and that +%% sys_core_fold retains them, so that warnings produced by +%% sys_core_fold will have proper filenames and line numbers. Thus, no +%% "no_file" warnings. +no_no_file(_Config) -> + {'EXIT',{{case_clause,0},_}} = (catch source(true, any)), + surgery = (tim(#{reduction => any}))(), + + false = soul(#{[] => true}), + {'EXIT',{{case_clause,true},_}} = (catch soul(#{[] => false})), + + ok = experiment(), + ok. + +source(true, Activities) -> + case 0 of + Activities when [] -> + Activities + end. + +tim(#{reduction := Emergency}) -> + try + fun() -> surgery end + catch + _ when [] -> + planet + end. + +soul(#{[] := Properly}) -> + not case true of + Properly -> true; + Properly -> 0 + end. + +experiment() -> + case kingdom of + _ -> + +case "map" of + _ -> 0.0 + end + end, + ok. + id(I) -> I. diff --git a/lib/compiler/test/nested_call_in_case.core b/lib/compiler/test/core_fold_SUITE_data/nested_call_in_case.core index 5c6b6909bd..c46906b2ed 100644 --- a/lib/compiler/test/nested_call_in_case.core +++ b/lib/compiler/test/core_fold_SUITE_data/nested_call_in_case.core @@ -16,6 +16,3 @@ module 'nested_call_in_case' ['a'/2] -| ['compiler_generated'] ) end end - - - diff --git a/lib/compiler/test/unused_multiple_values_error.core b/lib/compiler/test/core_fold_SUITE_data/unused_multiple_values_error.core index e06587c936..e06587c936 100644 --- a/lib/compiler/test/unused_multiple_values_error.core +++ b/lib/compiler/test/core_fold_SUITE_data/unused_multiple_values_error.core diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl index bd877bb528..01c779b181 100644 --- a/lib/compiler/test/error_SUITE.erl +++ b/lib/compiler/test/error_SUITE.erl @@ -1,29 +1,29 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2012. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% http://www.apache.org/licenses/LICENSE-2.0 %% -%% %CopyrightEnd% +%% 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% -module(error_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, head_mismatch_line/1,warnings_as_errors/1, bif_clashes/1, - transforms/1,forbidden_maps/1,bad_utf8/1]). + transforms/1,maps_warnings/1,bad_utf8/1]). %% Used by transforms/1 test case. -export([parse_transform/2]). @@ -37,7 +37,7 @@ all() -> groups() -> [{p,test_lib:parallel(), [head_mismatch_line,warnings_as_errors,bif_clashes, - transforms,forbidden_maps,bad_utf8]}]. + transforms,maps_warnings,bad_utf8]}]. init_per_suite(Config) -> Config. @@ -65,7 +65,7 @@ bif_clashes(Config) when is_list(Config) -> [return_warnings], {error, [{4, erl_lint,{call_to_redefined_old_bif,{length,1}}}], []} }], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), Ts1 = [{bif_clashes2, <<" -export([t/0]). @@ -76,7 +76,7 @@ bif_clashes(Config) when is_list(Config) -> [return_warnings], {error, [{3, erl_lint,{redefine_old_bif_import,{length,1}}}], []} }], - ?line [] = run(Config, Ts1), + [] = run(Config, Ts1), Ts00 = [{bif_clashes3, <<" -export([t/0]). @@ -89,7 +89,7 @@ bif_clashes(Config) when is_list(Config) -> ">>, [return_warnings], []}], - ?line [] = run(Config, Ts00), + [] = run(Config, Ts00), Ts11 = [{bif_clashes4, <<" -export([t/0]). @@ -100,7 +100,7 @@ bif_clashes(Config) when is_list(Config) -> ">>, [return_warnings], []}], - ?line [] = run(Config, Ts11), + [] = run(Config, Ts11), Ts000 = [{bif_clashes5, <<" -export([t/0]). @@ -113,7 +113,7 @@ bif_clashes(Config) when is_list(Config) -> [return_warnings], {warning, [{4, erl_lint,{call_to_redefined_bif,{binary_part,3}}}]} }], - ?line [] = run(Config, Ts000), + [] = run(Config, Ts000), Ts111 = [{bif_clashes6, <<" -export([t/0]). @@ -124,7 +124,7 @@ bif_clashes(Config) when is_list(Config) -> [return_warnings], {warning, [{3, erl_lint,{redefine_bif_import,{binary_part,3}}}]} }], - ?line [] = run(Config, Ts111), + [] = run(Config, Ts111), Ts2 = [{bif_clashes7, <<" -export([t/0]). @@ -139,7 +139,7 @@ bif_clashes(Config) when is_list(Config) -> {error, [{7,erl_lint,{define_import,{length,1}}}], []} }], - ?line [] = run2(Config, Ts2), + [] = run2(Config, Ts2), Ts3 = [{bif_clashes8, <<" -export([t/1]). @@ -153,7 +153,7 @@ bif_clashes(Config) when is_list(Config) -> {error, [{4,erl_lint,{illegal_guard_local_call,{length,1}}}], []} }], - ?line [] = run2(Config, Ts3), + [] = run2(Config, Ts3), Ts4 = [{bif_clashes9, <<" -export([t/1]). @@ -166,7 +166,7 @@ bif_clashes(Config) when is_list(Config) -> {error, [{5,erl_lint,{illegal_guard_local_call,{length,1}}}], []} }], - ?line [] = run2(Config, Ts4), + [] = run2(Config, Ts4), ok. @@ -175,23 +175,23 @@ bif_clashes(Config) when is_list(Config) -> %% Tests that a head mismatch is reported on the correct line (OTP-2125). head_mismatch_line(Config) when is_list(Config) -> - ?line [E|_] = get_compilation_errors(Config, "head_mismatch_line"), - ?line {26, Mod, Reason} = E, - ?line Mod:format_error(Reason), + [E|_] = get_compilation_errors(Config, "head_mismatch_line"), + {26, Mod, Reason} = E, + Mod:format_error(Reason), ok. %% Compiles a test file and returns the list of errors. get_compilation_errors(Config, Filename) -> - ?line DataDir = ?config(data_dir, Config), - ?line File = filename:join(DataDir, Filename), - ?line {error, [{_Name, E}|_], []} = compile:file(File, [return_errors]), + DataDir = proplists:get_value(data_dir, Config), + File = filename:join(DataDir, Filename), + {error, [{_Name, E}|_], []} = compile:file(File, [return_errors]), E. warnings_as_errors(Config) when is_list(Config) -> - ?line TestFile = test_filename(Config), - ?line BeamFile = filename:rootname(TestFile, ".erl") ++ ".beam", - ?line OutDir = ?config(priv_dir, Config), + TestFile = test_filename(Config), + BeamFile = filename:rootname(TestFile, ".erl") ++ ".beam", + OutDir = proplists:get_value(priv_dir, Config), Ts1 = [{warnings_as_errors, <<" @@ -203,8 +203,8 @@ warnings_as_errors(Config) when is_list(Config) -> {error, [], [{3,erl_lint,{unused_var,'A'}}]} }], - ?line [] = run(Ts1, TestFile, write_beam), - ?line false = filelib:is_regular(BeamFile), + [] = run(Ts1, TestFile, write_beam), + false = filelib:is_regular(BeamFile), Ts2 = [{warning_unused_var, <<" @@ -216,9 +216,9 @@ warnings_as_errors(Config) when is_list(Config) -> {warning, [{3,erl_lint,{unused_var,'A'}}]} }], - ?line [] = run(Ts2, TestFile, write_beam), - ?line true = filelib:is_regular(BeamFile), - ?line ok = file:delete(BeamFile), + [] = run(Ts2, TestFile, write_beam), + true = filelib:is_regular(BeamFile), + ok = file:delete(BeamFile), ok. @@ -235,23 +235,44 @@ transforms(Config) -> ">>, {error,[{none,compile,{parse_transform,?MODULE,{too_bad,_}}}],[]} = run_test(Ts2, test_filename(Config), [], dont_write_beam), + Ts3 = <<" + -compile({parse_transform,",?MODULE_STRING,"}). + ">>, + {error,[{none,compile,{parse_transform,?MODULE,{undef,_}}}],[]} = + run_test(Ts3, test_filename(Config), [call_undef], dont_write_beam), ok. -parse_transform(_, _) -> - error(too_bad). +parse_transform(_, Opts) -> + case lists:member(call_undef, Opts) of + false -> error(too_bad); + true -> camembert:délicieux() + end. -forbidden_maps(Config) when is_list(Config) -> - Ts1 = [{map_illegal_use_of_pattern, +maps_warnings(Config) when is_list(Config) -> + Ts1 = [{map_ok_use_of_pattern, <<" - -export([t/0]). + -export([t/1]). + t(K) -> + #{K := 1 = V} = id(#{<<\"hi all\">> => 1}), + V. + id(I) -> I. + ">>, + [return], + []}, + {map_illegal_use_of_pattern, + <<" + -export([t/0,t/2]). + t(K,#{ K := V }) -> V. t() -> V = 32, #{<<\"hi\",V,\"all\">> := 1} = id(#{<<\"hi all\">> => 1}). id(I) -> I. ">>, [return], - {error,[{5,erl_lint,{illegal_map_key_variable,'V'}}], []}}], + {error,[{3,erl_lint,{unbound_var,'K'}}, + {6,erl_lint,illegal_map_key}],[]}} + ], [] = run2(Config, Ts1), ok. @@ -274,7 +295,7 @@ bad_utf8(Config) -> run(Config, Tests) -> - ?line File = test_filename(Config), + File = test_filename(Config), run(Tests, File, dont_write_beam). run(Tests, File, WriteBeam) -> @@ -283,7 +304,7 @@ run(Tests, File, WriteBeam) -> E -> BadL; Bad -> - ?t:format("~nTest ~p failed. Expected~n ~p~n" + io:format("~nTest ~p failed. Expected~n ~p~n" "but got~n ~p~n", [N, E, Bad]), fail() end @@ -291,7 +312,7 @@ run(Tests, File, WriteBeam) -> lists:foldl(F, [], Tests). run2(Config, Tests) -> - ?line File = test_filename(Config), + File = test_filename(Config), run2(Tests, File, dont_write_beam). run2(Tests, File, WriteBeam) -> @@ -300,7 +321,7 @@ run2(Tests, File, WriteBeam) -> E -> BadL; Bad -> - ?t:format("~nTest ~p failed. Expected~n ~p~n" + io:format("~nTest ~p failed. Expected~n ~p~n" "but got~n ~p~n", [N, E, Bad]), fail() end @@ -317,56 +338,45 @@ filter(X) -> test_filename(Conf) -> Filename = ["errors_test_",test_lib:uniq(),".erl"], - DataDir = ?config(priv_dir, Conf), + DataDir = proplists:get_value(priv_dir, Conf), filename:join(DataDir, Filename). run_test(Test0, File, Warnings, WriteBeam) -> ModName = filename:rootname(filename:basename(File), ".erl"), Mod = list_to_atom(ModName), Test = ["-module(",ModName,"). ",Test0], - ?line Opts = case WriteBeam of - dont_write_beam -> - [binary,return_errors|Warnings]; - write_beam -> - [return_errors|Warnings] - end, - ?line ok = file:write_file(File, Test), + Opts = case WriteBeam of + dont_write_beam -> + [binary,return_errors|Warnings]; + write_beam -> + [return_errors|Warnings] + end, + ok = file:write_file(File, Test), %% Compile once just to print all errors and warnings. - ?line compile:file(File, [binary,report|Warnings]), + compile:file(File, [binary,report|Warnings]), %% Test result of compilation. io:format("~p\n", [Opts]), - ?line Res = case compile:file(File, Opts) of - {ok,Mod,_,[{_File,Ws}]} -> - %io:format("compile:file(~s,~p) ->~n~p~n", - % [File,Opts,Ws]), - {warning,Ws}; - {ok,Mod,_,[]} -> - %io:format("compile:file(~s,~p) ->~n~p~n", - % [File,Opts,Ws]), - []; - {ok,Mod,[{_File,Ws}]} -> - {warning,Ws}; - {ok,Mod,[]} -> - []; - {error,[{XFile,Es}],Ws} = _ZZ when is_list(XFile) -> - %io:format("compile:file(~s,~p) ->~n~p~n", - % [File,Opts,_ZZ]), - {error,Es,Ws}; - {error,[{XFile,Es1},{XFile,Es2}],Ws} = _ZZ - when is_list(XFile) -> - %io:format("compile:file(~s,~p) ->~n~p~n", - % [File,Opts,_ZZ]), - {error,Es1++Es2,Ws}; - {error,Es,[{_File,Ws}]} = _ZZ-> - %io:format("compile:file(~s,~p) ->~n~p~n", - % [File,Opts,_ZZ]), - {error,Es,Ws} - end, + Res = case compile:file(File, Opts) of + {ok,Mod,_,[{_File,Ws}]} -> + {warning,Ws}; + {ok,Mod,_,[]} -> + []; + {ok,Mod,[{_File,Ws}]} -> + {warning,Ws}; + {ok,Mod,[]} -> + []; + {error,[{XFile,Es}],Ws} = _ZZ when is_list(XFile) -> + {error,Es,Ws}; + {error,[{XFile,Es1},{XFile,Es2}],Ws} = _ZZ + when is_list(XFile) -> + {error,Es1++Es2,Ws}; + {error,Es,[{_File,Ws}]} = _ZZ-> + {error,Es,Ws} + end, file:delete(File), Res. fail() -> - io:format("failed~n"), - ?t:fail(). + ct:fail(failed). diff --git a/lib/compiler/test/error_SUITE_data/head_mismatch_line.erl b/lib/compiler/test/error_SUITE_data/head_mismatch_line.erl index 619c1329a4..5676d60daa 100644 --- a/lib/compiler/test/error_SUITE_data/head_mismatch_line.erl +++ b/lib/compiler/test/error_SUITE_data/head_mismatch_line.erl @@ -1,21 +1,21 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% -%% -module(head_mismatch_line). -export([foo/1, bar/2]). diff --git a/lib/compiler/test/float_SUITE.erl b/lib/compiler/test/float_SUITE.erl index afc04fd440..f6095947ca 100644 --- a/lib/compiler/test/float_SUITE.erl +++ b/lib/compiler/test/float_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -21,7 +22,7 @@ init_per_group/2,end_per_group/2, pending/1,bif_calls/1,math_functions/1,mixed_float_and_int/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -50,11 +51,11 @@ end_per_group(_GroupName, Config) -> %% Shows the effect of pending exceptions on the x86. pending(Config) when is_list(Config) -> - ?line case catch float_mul(1, 1.1e300, 3.14e300) of - {'EXIT',{badarith,_}} -> ok; - Other -> ?t:fail({expected_exception,Other}) - end, - ?line 0.0 = float_sub(2.0). + case catch float_mul(1, 1.1e300, 3.14e300) of + {'EXIT',{badarith,_}} -> ok; + Other -> ct:fail({expected_exception,Other}) + end, + 0.0 = float_sub(2.0). float_sub(A)-> catch A - 2.0. @@ -68,11 +69,11 @@ float_mul(Iter, A, B) when is_float(A), is_float(B) -> %% Thanks to Mikael Pettersson and Tobias Lindahl (HiPE). bif_calls(Config) when is_list(Config) -> - ?line {'EXIT',{badarith,_}} = (catch bad_arith(2.0, 1.7)), - ?line {'EXIT',{badarith,_}} = (catch bad_arith_again(2.0, [])), - ?line {'EXIT',{badarith,_}} = (catch bad_arith_xor(2.0, [])), - ?line {'EXIT',{badarith,_}} = (catch bad_arith_hd(2.0, [])), - ?line {'EXIT',{badarith,_}} = (catch bad_negate(2.0, 1.7)), + {'EXIT',{badarith,_}} = (catch bad_arith(2.0, 1.7)), + {'EXIT',{badarith,_}} = (catch bad_arith_again(2.0, [])), + {'EXIT',{badarith,_}} = (catch bad_arith_xor(2.0, [])), + {'EXIT',{badarith,_}} = (catch bad_arith_hd(2.0, [])), + {'EXIT',{badarith,_}} = (catch bad_negate(2.0, 1.7)), ok. bad_arith(X, Y) when is_float(X) -> @@ -113,49 +114,51 @@ bad_negate(X, Y) when is_float(X) -> math_functions(Config) when is_list(Config) -> %% Mostly silly coverage. - ?line 0.0 = math:tan(0), - ?line 0.0 = math:atan2(0, 1), - ?line 0.0 = math:sinh(0), - ?line 1.0 = math:cosh(0), - ?line 0.0 = math:tanh(0), - ?line 1.0 = math:log10(10), - ?line -1.0 = math:cos(math:pi()), - ?line 1.0 = math:exp(0), - ?line 1.0 = math:pow(math:pi(), 0), - ?line 0.0 = math:log(1), - ?line 0.0 = math:asin(0), - ?line 0.0 = math:acos(1), - ?line ?OPTIONAL(0.0, math:asinh(0)), - ?line ?OPTIONAL(0.0, math:acosh(1)), - ?line ?OPTIONAL(0.0, math:atanh(0)), - ?line ?OPTIONAL(0.0, math:erf(0)), - ?line ?OPTIONAL(1.0, math:erfc(0)), - - ?line 0.0 = math:tan(id(0)), - ?line 0.0 = math:atan2(id(0), 1), - ?line 0.0 = math:sinh(id(0)), - ?line 1.0 = math:cosh(id(0)), - ?line 0.0 = math:tanh(id(0)), - ?line 1.0 = math:log10(id(10)), - ?line 1.0 = math:exp(id(0)), - ?line 0.0 = math:log(id(1)), - ?line 0.0 = math:asin(id(0)), - ?line 0.0 = math:acos(id(1)), - ?line ?OPTIONAL(0.0, math:asinh(id(0))), - ?line ?OPTIONAL(0.0, math:acosh(id(1))), - ?line ?OPTIONAL(0.0, math:atanh(id(0))), - ?line ?OPTIONAL(0.0, math:erf(id(0))), - ?line ?OPTIONAL(1.0, math:erfc(id(0))), + 0.0 = math:tan(0), + 0.0 = math:atan2(0, 1), + 0.0 = math:sinh(0), + 1.0 = math:cosh(0), + 0.0 = math:tanh(0), + 1.0 = math:log2(2), + 1.0 = math:log10(10), + -1.0 = math:cos(math:pi()), + 1.0 = math:exp(0), + 1.0 = math:pow(math:pi(), 0), + 0.0 = math:log(1), + 0.0 = math:asin(0), + 0.0 = math:acos(1), + ?OPTIONAL(0.0, math:asinh(0)), + ?OPTIONAL(0.0, math:acosh(1)), + ?OPTIONAL(0.0, math:atanh(0)), + ?OPTIONAL(0.0, math:erf(0)), + ?OPTIONAL(1.0, math:erfc(0)), + + 0.0 = math:tan(id(0)), + 0.0 = math:atan2(id(0), 1), + 0.0 = math:sinh(id(0)), + 1.0 = math:cosh(id(0)), + 0.0 = math:tanh(id(0)), + 1.0 = math:log2(id(2)), + 1.0 = math:log10(id(10)), + 1.0 = math:exp(id(0)), + 0.0 = math:log(id(1)), + 0.0 = math:asin(id(0)), + 0.0 = math:acos(id(1)), + ?OPTIONAL(0.0, math:asinh(id(0))), + ?OPTIONAL(0.0, math:acosh(id(1))), + ?OPTIONAL(0.0, math:atanh(id(0))), + ?OPTIONAL(0.0, math:erf(id(0))), + ?OPTIONAL(1.0, math:erfc(id(0))), %% Only for coverage (of beam_type.erl). - ?line {'EXIT',{undef,_}} = (catch math:fnurfla(0)), - ?line {'EXIT',{undef,_}} = (catch math:fnurfla(0, 0)), - ?line {'EXIT',{badarg,_}} = (catch float(kalle)), - ?line {'EXIT',{badarith,_}} = (catch name/1), + {'EXIT',{undef,_}} = (catch math:fnurfla(0)), + {'EXIT',{undef,_}} = (catch math:fnurfla(0, 0)), + {'EXIT',{badarg,_}} = (catch float(kalle)), + {'EXIT',{badarith,_}} = (catch name/1), ok. mixed_float_and_int(Config) when is_list(Config) -> - ?line 129.0 = pc(77, 23, 5), + 129.0 = pc(77, 23, 5), ok. pc(Cov, NotCov, X) -> diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl index a3e9b7fe4e..16474adf5b 100644 --- a/lib/compiler/test/fun_SUITE.erl +++ b/lib/compiler/test/fun_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -21,21 +22,23 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 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]). + external/1,eep37/1,eep37_dup/1,badarity/1,badfun/1]). %% Internal exports. -export([call_me/1,dup1/0,dup2/0]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> test_lib:recompile(?MODULE), - [test1,overwritten_fun,otp_7202,bif_fun,external,eep37,eep37_dup,badarity]. + [{group,p}]. -groups() -> - []. +groups() -> + [{p,[parallel], + [test1,overwritten_fun,otp_7202,bif_fun,external,eep37, + eep37_dup,badarity,badfun]}]. init_per_suite(Config) -> Config. @@ -59,9 +62,8 @@ l1() -> ?T((begin G = fun(1=0) -> ok end, {'EXIT',_} = (catch G(2)), ok end), ok) ]. -test1(suite) -> []; test1(Config) when is_list(Config) -> - ?line lists:foreach(fun one_test/1, eval_list(l1(), [])), + lists:foreach(fun one_test/1, eval_list(l1(), [])), ok. evaluate(Str, Vars) -> @@ -92,7 +94,7 @@ one_test({C, E, Str, Correct}) -> true -> io:format("ERROR: Compiled: ~p. Expected ~p. Got ~p.~n", [Str, Correct, C]), - test_server:fail(comp) + ct:fail(comp) end, if E == Correct -> @@ -100,7 +102,7 @@ one_test({C, E, Str, Correct}) -> true -> io:format("ERROR: Interpreted: ~p. Expected ~p. Got ~p.~n", [Str, Correct, E]), - test_server:fail(comp) + ct:fail(comp) end. -record(b, {c}). @@ -108,9 +110,9 @@ one_test({C, E, Str, Correct}) -> %% OTP-7102. (Thanks to Simon Cornish.) overwritten_fun(Config) when is_list(Config) -> - ?line {a2,a} = overwritten_fun_1(a), - ?line {a2,{b,c}} = overwritten_fun_1(#b{c=c}), - ?line one = overwritten_fun_1(#b{c=[]}), + {a2,a} = overwritten_fun_1(a), + {a2,{b,c}} = overwritten_fun_1(#b{c=c}), + one = overwritten_fun_1(#b{c=[]}), ok. overwritten_fun_1(A) -> @@ -152,8 +154,8 @@ otp_7202_func() -> no_value. bif_fun(Config) when is_list(Config) -> - ?line F = fun abs/1, - ?line 5 = F(-5), + F = fun abs/1, + 5 = F(-5), ok. -define(APPLY(M, F, A), (fun(Fun) -> {ok,{a,b}} = Fun({a,b}) end)(fun M:F/A)). @@ -221,5 +223,25 @@ badarity(Config) when is_list(Config) -> {'EXIT',{{badarity,{_,[]}},_}} = (catch (fun badarity/1)()), ok. +badfun(_Config) -> + X = not_a_fun, + expect_badfun(42, catch 42()), + expect_badfun(42.0, catch 42.0(1)), + expect_badfun(X, catch X()), + expect_badfun(X, catch X(1)), + Len = length(atom_to_list(X)), + expect_badfun(Len, catch begin length(atom_to_list(X)) end(1)), + + expect_badfun(42, catch 42(put(?FUNCTION_NAME, yes))), + yes = erase(?FUNCTION_NAME), + + expect_badfun(X, catch X(put(?FUNCTION_NAME, of_course))), + of_course = erase(?FUNCTION_NAME), + + ok. + +expect_badfun(Term, Exit) -> + {'EXIT',{{badfun,Term},_}} = Exit. + id(I) -> I. diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index 34bfdeb1e5..6302f82f29 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2013. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% -module(guard_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, @@ -27,13 +28,14 @@ or_guard/1,more_or_guards/1, complex_or_guards/1,and_guard/1, xor_guard/1,more_xor_guards/1, - old_guard_tests/1, + old_guard_tests/1,complex_guard/1, build_in_guard/1,gbif/1, t_is_boolean/1,is_function_2/1, - tricky/1,rel_ops/1,literal_type_tests/1, + tricky/1,rel_ops/1,rel_op_combinations/1,literal_type_tests/1, basic_andalso_orelse/1,traverse_dcd/1, check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1, - bad_constants/1,bad_guards/1]). + bad_constants/1,bad_guards/1, + guard_in_catch/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -42,15 +44,17 @@ all() -> [{group,p}]. groups() -> - [{p,test_lib:parallel(), + [{p,[parallel], [misc,const_cond,basic_not,complex_not,nested_nots, semicolon,complex_semicolon,comma,or_guard, more_or_guards,complex_or_guards,and_guard,xor_guard, - more_xor_guards,build_in_guard,old_guard_tests,gbif, - t_is_boolean,is_function_2,tricky,rel_ops, + more_xor_guards,build_in_guard, + old_guard_tests,complex_guard,gbif, + t_is_boolean,is_function_2,tricky, + rel_ops,rel_op_combinations, literal_type_tests,basic_andalso_orelse,traverse_dcd, check_qlc_hrl,andalso_semi,t_tuple_size,binary_part, - bad_constants,bad_guards]}]. + bad_constants,bad_guards,guard_in_catch]}]. init_per_suite(Config) -> Config. @@ -66,23 +70,23 @@ end_per_group(_GroupName, Config) -> misc(Config) when is_list(Config) -> - ?line 42 = case id(42) of - X when -X -> ok; - X -> X - end, - ?line {a,b,c} = misc_1([{{a,b,c}},{[4]},{[3]},{-2}]), - ?line none = misc_1([{{a,b,c}},{[4]},{[3]},{-3}]), - ?line none = misc_1([{{a,b,c}},{[4]},{[7]},{-2}]), - ?line none = misc_1([{{a,b,c}},{[4]},{[3]},{[1,2,3]}]), - - ?line {ok,buf,<<>>} = get_data({o,true,raw}, 0, buf), - ?line {ok,buf,<<>>} = get_data({o,true,raw}, 42, buf), - ?line {ok,buf,<<>>} = get_data({o,false,raw}, 0, buf), - ?line error = get_data({o,false,raw}, 42, buf), - ?line {ok,buf,<<>>} = get_data({o,true,0}, 0, buf), - ?line {ok,buf,<<>>} = get_data({o,true,0}, 42, buf), - ?line {ok,buf,<<>>} = get_data({o,false,0}, 0, buf), - ?line error = get_data({o,false,0}, 42, buf), + 42 = case id(42) of + X when -X -> ok; + X -> X + end, + {a,b,c} = misc_1([{{a,b,c}},{[4]},{[3]},{-2}]), + none = misc_1([{{a,b,c}},{[4]},{[3]},{-3}]), + none = misc_1([{{a,b,c}},{[4]},{[7]},{-2}]), + none = misc_1([{{a,b,c}},{[4]},{[3]},{[1,2,3]}]), + + {ok,buf,<<>>} = get_data({o,true,raw}, 0, buf), + {ok,buf,<<>>} = get_data({o,true,raw}, 42, buf), + {ok,buf,<<>>} = get_data({o,false,raw}, 0, buf), + error = get_data({o,false,raw}, 42, buf), + {ok,buf,<<>>} = get_data({o,true,0}, 0, buf), + {ok,buf,<<>>} = get_data({o,true,0}, 42, buf), + {ok,buf,<<>>} = get_data({o,false,0}, 0, buf), + error = get_data({o,false,0}, 42, buf), ok. @@ -104,11 +108,11 @@ get_data({o,Active,Raw}, BytesToRead, Buffer) end. const_cond(Config) when is_list(Config) -> - ?line ok = const_cond({}, 0), - ?line ok = const_cond({a}, 1), - ?line error = const_cond({a,b}, 3), - ?line error = const_cond({a}, 0), - ?line error = const_cond({a,b}, 1), + ok = const_cond({}, 0), + ok = const_cond({a}, 1), + error = const_cond({a,b}, 3), + error = const_cond({a}, 0), + error = const_cond({a,b}, 1), ok. const_cond(T, Sz) -> @@ -129,80 +133,80 @@ basic_not(Config) when is_list(Config) -> D = id(5), ATuple = {False,True,Glurf}, - ?line check(fun() -> if not false -> ok; true -> error end end, ok), - ?line check(fun() -> if not true -> ok; true -> error end end, error), - ?line check(fun() -> if not False -> ok; true -> error end end, ok), - ?line check(fun() -> if not True -> ok; true -> error end end, error), + check(fun() -> if not false -> ok; true -> error end end, ok), + check(fun() -> if not true -> ok; true -> error end end, error), + check(fun() -> if not False -> ok; true -> error end end, ok), + check(fun() -> if not True -> ok; true -> error end end, error), - ?line check(fun() -> if A > B -> gt; A < B -> lt; A == B -> eq end end, lt), - ?line check(fun() -> if A > C -> gt; A < C -> lt; A == C -> eq end end, gt), - ?line check(fun() -> if A > D -> gt; A < D -> lt; A == D -> eq end end, eq), + check(fun() -> if A > B -> gt; A < B -> lt; A == B -> eq end end, lt), + check(fun() -> if A > C -> gt; A < C -> lt; A == C -> eq end end, gt), + check(fun() -> if A > D -> gt; A < D -> lt; A == D -> eq end end, eq), - ?line check(fun() -> if not (7 > 453) -> le; not (7 < 453) -> ge; - not (7 == 453) -> ne; true -> eq end end, le), - ?line check(fun() -> if not (7 > -8) -> le; not (7 < -8) -> ge; - not (7 == -8) -> ne; true -> eq end end, ge), - ?line check(fun() -> if not (7 > 7) -> le; not (7 < 7) -> ge; - not (7 == 7) -> ne; true -> eq end end, le), + check(fun() -> if not (7 > 453) -> le; not (7 < 453) -> ge; + not (7 == 453) -> ne; true -> eq end end, le), + check(fun() -> if not (7 > -8) -> le; not (7 < -8) -> ge; + not (7 == -8) -> ne; true -> eq end end, ge), + check(fun() -> if not (7 > 7) -> le; not (7 < 7) -> ge; + not (7 == 7) -> ne; true -> eq end end, le), - ?line check(fun() -> if not (A > B) -> le; not (A < B) -> ge; - not (A == B) -> ne; true -> eq end end, le), - ?line check(fun() -> if not (A > C) -> le; not (A < C) -> ge; - not (A == C) -> ne; true -> eq end end, ge), - ?line check(fun() -> if not (A > D) -> le; not (A < D) -> ge; - not (A == D) -> ne; true -> eq end end, le), + check(fun() -> if not (A > B) -> le; not (A < B) -> ge; + not (A == B) -> ne; true -> eq end end, le), + check(fun() -> if not (A > C) -> le; not (A < C) -> ge; + not (A == C) -> ne; true -> eq end end, ge), + check(fun() -> if not (A > D) -> le; not (A < D) -> ge; + not (A == D) -> ne; true -> eq end end, le), - ?line check(fun() -> if not element(1, ATuple) -> ok; true -> error end end, ok), - ?line check(fun() -> if not element(2, ATuple) -> ok; true -> error end end, error), - ?line check(fun() -> if not element(3, ATuple) -> ok; true -> error end end, error), + check(fun() -> if not element(1, ATuple) -> ok; true -> error end end, ok), + check(fun() -> if not element(2, ATuple) -> ok; true -> error end end, error), + check(fun() -> if not element(3, ATuple) -> ok; true -> error end end, error), - ?line check(fun() -> if not glurf -> ok; true -> error end end, error), - ?line check(fun() -> if not Glurf -> ok; true -> error end end, error), + check(fun() -> if not glurf -> ok; true -> error end end, error), + check(fun() -> if not Glurf -> ok; true -> error end end, error), ok. complex_not(Config) when is_list(Config) -> ATuple = id({false,true,gurka}), - ?line check(fun() -> if not(element(1, ATuple)) -> ok; true -> error end end, ok), - ?line check(fun() -> if not(element(2, ATuple)) -> ok; true -> error end end, error), + check(fun() -> if not(element(1, ATuple)) -> ok; true -> error end end, ok), + check(fun() -> if not(element(2, ATuple)) -> ok; true -> error end end, error), - ?line check(fun() -> if not(element(3, ATuple) == gurka) -> ok; - true -> error end end, error), - ?line check(fun() -> if not(element(3, ATuple) =/= gurka) -> ok; - true -> error end end, ok), + check(fun() -> if not(element(3, ATuple) == gurka) -> ok; + true -> error end end, error), + check(fun() -> if not(element(3, ATuple) =/= gurka) -> ok; + true -> error end end, ok), - ?line check(fun() -> if {a,not(element(2, ATuple))} == {a,false} -> ok; - true -> error end end, ok), - ?line check(fun() -> if {a,not(element(1, ATuple))} == {a,false} -> ok; - true -> error end end, error), + check(fun() -> if {a,not(element(2, ATuple))} == {a,false} -> ok; + true -> error end end, ok), + check(fun() -> if {a,not(element(1, ATuple))} == {a,false} -> ok; + true -> error end end, error), - ?line check(fun() -> if not(element(1, ATuple) or element(3, ATuple)) -> ok; - true -> error end end, error), + check(fun() -> if not(element(1, ATuple) or element(3, ATuple)) -> ok; + true -> error end end, error), %% orelse - ?line check(fun() -> if not(element(1, ATuple) orelse element(3, ATuple)) -> ok; - true -> error end end, error), + check(fun() -> if not(element(1, ATuple) orelse element(3, ATuple)) -> ok; + true -> error end end, error), ok. nested_nots(Config) when is_list(Config) -> - ?line true = nested_not_1(0, 0), - ?line true = nested_not_1(0, 1), - ?line true = nested_not_1(a, b), - ?line true = nested_not_1(10, 0), - ?line false = nested_not_1(z, a), - ?line false = nested_not_1(3.4, {anything,goes}), - ?line false = nested_not_1(3.4, atom), - ?line true = nested_not_1(3.0, [list]), - - ?line true = nested_not_2(false, false, 42), - ?line true = nested_not_2(false, true, 42), - ?line true = nested_not_2(true, false, 42), - ?line true = nested_not_2(true, true, 42), - ?line true = nested_not_2(false, false, atom), - ?line false = nested_not_2(false, true, atom), - ?line false = nested_not_2(true, false, atom), - ?line false = nested_not_2(true, true, atom), + true = nested_not_1(0, 0), + true = nested_not_1(0, 1), + true = nested_not_1(a, b), + true = nested_not_1(10, 0), + false = nested_not_1(z, a), + false = nested_not_1(3.4, {anything,goes}), + false = nested_not_1(3.4, atom), + true = nested_not_1(3.0, [list]), + + true = nested_not_2(false, false, 42), + true = nested_not_2(false, true, 42), + true = nested_not_2(true, false, 42), + true = nested_not_2(true, true, 42), + true = nested_not_2(false, false, atom), + false = nested_not_2(false, true, atom), + false = nested_not_2(true, false, atom), + false = nested_not_2(true, true, atom), ok. nested_not_1(X, Y) when not (((X>Y) or not(is_atom(X))) and @@ -224,113 +228,121 @@ semicolon(Config) when is_list(Config) -> %% True/false combined using ';' (literal atoms). - ?line check(fun() -> if true; false -> ok end end, ok), - ?line check(fun() -> if false; true -> ok end end, ok), - ?line check(fun() -> if true; true -> ok end end, ok), - ?line check(fun() -> if false; false -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = (catch if false; false -> ok end), - exit - end, exit), + check(fun() -> if true; false -> ok end end, ok), + check(fun() -> if false; true -> ok end end, ok), + check(fun() -> if true; true -> ok end end, ok), + check(fun() -> if false; false -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = (catch if false; false -> ok end), + exit + end, exit), %% True/false combined used ';'. True = id(true), False = id(false), - ?line check(fun() -> if True; False -> ok end end, ok), - ?line check(fun() -> if False; True -> ok end end, ok), - ?line check(fun() -> if True; True -> ok end end, ok), - ?line check(fun() -> if False; False -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = (catch if False; False -> ok end), - exit - end, exit), + check(fun() -> if True; False -> ok end end, ok), + check(fun() -> if False; True -> ok end end, ok), + check(fun() -> if True; True -> ok end end, ok), + check(fun() -> if False; False -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = (catch if False; False -> ok end), + exit + end, exit), %% Combine true/false with a non-boolean value. Glurf = id(glurf), - ?line check(fun() -> if True; Glurf -> ok end end, ok), - ?line check(fun() -> if Glurf; True -> ok end end, ok), - ?line check(fun() -> if Glurf; Glurf -> ok; true -> error end end, error), - ?line check(fun() -> if False; Glurf -> ok; true -> error end end, error), - ?line check(fun() -> if Glurf; False -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = (catch if Glurf; Glurf -> ok end), - exit - end, exit), + check(fun() -> if True; Glurf -> ok end end, ok), + check(fun() -> if Glurf; True -> ok end end, ok), + check(fun() -> if Glurf; Glurf -> ok; true -> error end end, error), + check(fun() -> if False; Glurf -> ok; true -> error end end, error), + check(fun() -> if Glurf; False -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = (catch if Glurf; Glurf -> ok end), + exit + end, exit), %% Combine true/false with errors. ATuple = id({false,true,gurka}), - ?line check(fun() -> if True; element(42, ATuple) -> ok end end, ok), - ?line check(fun() -> if element(42, ATuple); True -> ok end end, ok), - ?line check(fun() -> if element(42, ATuple); element(42, ATuple) -> ok; - true -> error end end, error), - ?line check(fun() -> if False; element(42, ATuple) -> ok; - true -> error end end, error), - ?line check(fun() -> if element(42, ATuple); - False -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = - (catch if element(42, ATuple); - element(42, ATuple) -> ok end), - exit - end, exit), + check(fun() -> if True; element(42, ATuple) -> ok end end, ok), + check(fun() -> if element(42, ATuple); True -> ok end end, ok), + check(fun() -> if element(42, ATuple); element(42, ATuple) -> ok; + true -> error end end, error), + check(fun() -> if False; element(42, ATuple) -> ok; + true -> error end end, error), + check(fun() -> if element(42, ATuple); + False -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = + (catch if element(42, ATuple); + element(42, ATuple) -> ok end), + exit + end, exit), ok. complex_semicolon(Config) when is_list(Config) -> - ?line ok = csemi1(int, {blurf}), - ?line ok = csemi1(string, {blurf}), - ?line ok = csemi1(float, [a]), - ?line error = csemi1(35, 42), + ok = csemi1(int, {blurf}), + ok = csemi1(string, {blurf}), + ok = csemi1(float, [a]), + error = csemi1(35, 42), %% 2 - ?line ok = csemi2({}, {a,b,c}), - ?line ok = csemi2({1,3.5}, {a,b,c}), - ?line ok = csemi2(dum, {a,b,c}), + ok = csemi2({}, {a,b,c}), + ok = csemi2({1,3.5}, {a,b,c}), + ok = csemi2(dum, {a,b,c}), - ?line ok = csemi2({45,-19.3}, {}), - ?line ok = csemi2({45,-19.3}, {dum}), - ?line ok = csemi2({45,-19.3}, {dum,dum}), + ok = csemi2({45,-19.3}, {}), + ok = csemi2({45,-19.3}, {dum}), + ok = csemi2({45,-19.3}, {dum,dum}), - ?line error = csemi2({45}, {dum}), - ?line error = csemi2([], {dum}), - ?line error = csemi2({dum}, []), - ?line error = csemi2([], []), + error = csemi2({45}, {dum}), + error = csemi2([], {dum}), + error = csemi2({dum}, []), + error = csemi2([], []), %% 3 - ?line csemi3(fun csemi3a/4), - ?line csemi3(fun csemi3b/4), - ?line csemi3(fun csemi3c/4), + csemi3(fun csemi3a/4), + csemi3(fun csemi3b/4), + csemi3(fun csemi3c/4), %% 4 - ?line csemi4(fun csemi4a/4), - ?line csemi4(fun csemi4b/4), - ?line csemi4(fun csemi4c/4), - ?line csemi4(fun csemi4d/4), + csemi4(fun csemi4a/4), + csemi4(fun csemi4b/4), + csemi4(fun csemi4c/4), + csemi4(fun csemi4d/4), %% 4, 'orelse' instead of 'or' - ?line csemi4_orelse(fun csemi4_orelse_a/4), - ?line csemi4_orelse(fun csemi4_orelse_b/4), - ?line csemi4_orelse(fun csemi4_orelse_c/4), - ?line csemi4_orelse(fun csemi4_orelse_d/4), + csemi4_orelse(fun csemi4_orelse_a/4), + csemi4_orelse(fun csemi4_orelse_b/4), + csemi4_orelse(fun csemi4_orelse_c/4), + csemi4_orelse(fun csemi4_orelse_d/4), %% 5 - ?line error = csemi5(0, 0), - ?line ok = csemi5(5, 0), - ?line ok = csemi5(4, -4), - ?line ok = csemi5(10, -4), + error = csemi5(0, 0), + ok = csemi5(5, 0), + ok = csemi5(4, -4), + ok = csemi5(10, -4), %% 6 - ?line error = csemi6({a}, 0), - ?line ok = csemi6({a,b}, 0), - ?line ok = csemi6({}, 3), - ?line ok = csemi6({a,b,c}, 3), - + error = csemi6({a}, 0), + ok = csemi6({a,b}, 0), + ok = csemi6({}, 3), + ok = csemi6({a,b,c}, 3), + + %% 7 + error = csemi7(#{a=>1}, 1, 0), + error = csemi7(<<>>, 1, 0), + ok = csemi7(#{a=>1}, 3, 0), + ok = csemi7(#{a=>1}, 0, 3), + ok = csemi7(#{a=>1}, 3, 3), + ok = csemi7(#{a=>1, b=>3}, 0, 0), + ok. csemi1(Type, Val) when is_list(Val), Type == float; @@ -416,7 +428,7 @@ csemi4_orelse(Test) -> ok = Test({}, 2, blurf, 0), ok = Test({}, 2, {1}, 2), - ?line error = Test([], 1, {}, 0), + error = Test([], 1, {}, 0), ok. @@ -442,76 +454,79 @@ csemi5(_, _) -> error. csemi6(A, B) when hd([tuple_size(A)]) > 1; abs(B) > 2 -> ok; csemi6(_, _) -> error. +csemi7(A, B, C) when A#{a:=B} > #{a=>1}; abs(C) > 2 -> ok; +csemi7(_, _, _) -> error. + comma(Config) when is_list(Config) -> %% ',' combinations of literal true/false. - ?line check(fun() -> if true, false -> ok; true -> error end end, error), - ?line check(fun() -> if false, true -> ok; true -> error end end, error), - ?line check(fun() -> if true, true -> ok end end, ok), - ?line check(fun() -> if false, false -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = - (catch if true, false -> ok; - false, true -> ok; - false, false -> ok - end), - exit - end, exit), + check(fun() -> if true, false -> ok; true -> error end end, error), + check(fun() -> if false, true -> ok; true -> error end end, error), + check(fun() -> if true, true -> ok end end, ok), + check(fun() -> if false, false -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = + (catch if true, false -> ok; + false, true -> ok; + false, false -> ok + end), + exit + end, exit), %% ',' combinations of true/false in variables. True = id(true), False = id(false), - ?line check(fun() -> if True, False -> ok; true -> error end end, error), - ?line check(fun() -> if False, True -> ok; true -> error end end, error), - ?line check(fun() -> if True, True -> ok end end, ok), - ?line check(fun() -> if False, False -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = - (catch if True, False -> ok; - False, True -> ok; - False, False -> ok - end), - exit - end, exit), + check(fun() -> if True, False -> ok; true -> error end end, error), + check(fun() -> if False, True -> ok; true -> error end end, error), + check(fun() -> if True, True -> ok end end, ok), + check(fun() -> if False, False -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = + (catch if True, False -> ok; + False, True -> ok; + False, False -> ok + end), + exit + end, exit), %% ',' combinations of true/false, and non-boolean in variables. Glurf = id(glurf), - ?line check(fun() -> if True, Glurf -> ok; true -> error end end, error), - ?line check(fun() -> if Glurf, True -> ok; true -> error end end, error), - ?line check(fun() -> if True, True -> ok end end, ok), - ?line check(fun() -> if Glurf, Glurf -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = - (catch if True, Glurf -> ok; - Glurf, True -> ok; - Glurf, Glurf -> ok - end), - exit - end, exit), + check(fun() -> if True, Glurf -> ok; true -> error end end, error), + check(fun() -> if Glurf, True -> ok; true -> error end end, error), + check(fun() -> if True, True -> ok end end, ok), + check(fun() -> if Glurf, Glurf -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = + (catch if True, Glurf -> ok; + Glurf, True -> ok; + Glurf, Glurf -> ok + end), + exit + end, exit), %% ',' combinations of true/false with errors. ATuple = id({a,b,c}), - ?line check(fun() -> if True, element(42, ATuple) -> ok; - true -> error end end, error), - ?line check(fun() -> if element(42, ATuple), True -> ok; - true -> error end end, error), - ?line check(fun() -> if True, True -> ok end end, ok), - ?line check(fun() -> if element(42, ATuple), element(42, ATuple) -> ok; - true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = - (catch if True, element(42, ATuple) -> ok; - element(42, ATuple), True -> ok; - element(42, ATuple), element(42, ATuple) -> ok - end), - exit - end, exit), + check(fun() -> if True, element(42, ATuple) -> ok; + true -> error end end, error), + check(fun() -> if element(42, ATuple), True -> ok; + true -> error end end, error), + check(fun() -> if True, True -> ok end end, ok), + check(fun() -> if element(42, ATuple), element(42, ATuple) -> ok; + true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = + (catch if True, element(42, ATuple) -> ok; + element(42, ATuple), True -> ok; + element(42, ATuple), element(42, ATuple) -> ok + end), + exit + end, exit), ok. @@ -521,35 +536,35 @@ or_guard(Config) when is_list(Config) -> Glurf = id(glurf), %% 'or' combinations of literal true/false. - ?line check(fun() -> if true or false -> ok end end, ok), - ?line check(fun() -> if false or true -> ok end end, ok), - ?line check(fun() -> if true or true -> ok end end, ok), - ?line check(fun() -> if false or false -> ok; true -> error end end, error), + check(fun() -> if true or false -> ok end end, ok), + check(fun() -> if false or true -> ok end end, ok), + check(fun() -> if true or true -> ok end end, ok), + check(fun() -> if false or false -> ok; true -> error end end, error), - ?line check(fun() -> if glurf or true -> ok; true -> error end end, error), - ?line check(fun() -> if true or glurf -> ok; true -> error end end, error), - ?line check(fun() -> if glurf or glurf -> ok; true -> error end end, error), + check(fun() -> if glurf or true -> ok; true -> error end end, error), + check(fun() -> if true or glurf -> ok; true -> error end end, error), + check(fun() -> if glurf or glurf -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = (catch if false or false -> ok end), - exit - end, exit), + check(fun() -> + {'EXIT',{if_clause,_}} = (catch if false or false -> ok end), + exit + end, exit), %% 'or' combinations using variables containing true/false. - ?line check(fun() -> if True or False -> ok end end, ok), - ?line check(fun() -> if False or True -> ok end end, ok), - ?line check(fun() -> if True or True -> ok end end, ok), - ?line check(fun() -> if False or False -> ok; true -> error end end, error), + check(fun() -> if True or False -> ok end end, ok), + check(fun() -> if False or True -> ok end end, ok), + check(fun() -> if True or True -> ok end end, ok), + check(fun() -> if False or False -> ok; true -> error end end, error), - ?line check(fun() -> if True or Glurf -> ok; true -> error end end, error), - ?line check(fun() -> if Glurf or True -> ok; true -> error end end, error), - ?line check(fun() -> if Glurf or Glurf -> ok; true -> error end end, error), + check(fun() -> if True or Glurf -> ok; true -> error end end, error), + check(fun() -> if Glurf or True -> ok; true -> error end end, error), + check(fun() -> if Glurf or Glurf -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = (catch if False or False -> ok end), - exit - end, exit), + check(fun() -> + {'EXIT',{if_clause,_}} = (catch if False or False -> ok end), + exit + end, exit), ok. @@ -558,142 +573,142 @@ more_or_guards(Config) when is_list(Config) -> False = id(false), ATuple = id({false,true,gurka}), - ?line check(fun() -> - if element(42, ATuple) or False -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if False or element(42, ATuple) -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if element(18, ATuple) or element(42, ATuple) -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if True or element(42, ATuple) -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if element(42, ATuple) or True -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if element(1, ATuple) or element(42, ATuple) or True -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if element(1, ATuple) or True or element(42, ATuple) -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if - (<<False:8>> == <<0>>) or element(2, ATuple) -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if - element(2, ATuple) or (<<True:8>> == <<1>>) -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if element(2, ATuple) or element(42, ATuple) -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if - element(1, ATuple) or - element(2, ATuple) or - element(19, ATuple) -> ok; - true -> error end - end, error), + check(fun() -> + if element(42, ATuple) or False -> ok; + true -> error end + end, error), + + check(fun() -> + if False or element(42, ATuple) -> ok; + true -> error end + end, error), + + check(fun() -> + if element(18, ATuple) or element(42, ATuple) -> ok; + true -> error end + end, error), + + check(fun() -> + if True or element(42, ATuple) -> ok; + true -> error end + end, error), + + check(fun() -> + if element(42, ATuple) or True -> ok; + true -> error end + end, error), + + check(fun() -> + if element(1, ATuple) or element(42, ATuple) or True -> ok; + true -> error end + end, error), + + check(fun() -> + if element(1, ATuple) or True or element(42, ATuple) -> ok; + true -> error end + end, error), + + check(fun() -> + if + (<<False:8>> == <<0>>) or element(2, ATuple) -> ok; + true -> error end + end, error), + + check(fun() -> + if + element(2, ATuple) or (<<True:8>> == <<1>>) -> ok; + true -> error end + end, error), + + check(fun() -> + if element(2, ATuple) or element(42, ATuple) -> ok; + true -> error end + end, error), + + check(fun() -> + if + element(1, ATuple) or + element(2, ATuple) or + element(19, ATuple) -> ok; + true -> error end + end, error), ok. complex_or_guards(Config) when is_list(Config) -> %% complex_or_1/2 - ?line ok = complex_or_1({a,b,c,d}, {1,2,3}), - ?line ok = complex_or_1({a,b,c,d}, {1}), - ?line ok = complex_or_1({a}, {1,2,3}), - ?line error = complex_or_1({a}, {1}), + ok = complex_or_1({a,b,c,d}, {1,2,3}), + ok = complex_or_1({a,b,c,d}, {1}), + ok = complex_or_1({a}, {1,2,3}), + error = complex_or_1({a}, {1}), - ?line error = complex_or_1(1, 2), - ?line error = complex_or_1([], {a,b,c,d}), - ?line error = complex_or_1({a,b,c,d}, []), + error = complex_or_1(1, 2), + error = complex_or_1([], {a,b,c,d}), + error = complex_or_1({a,b,c,d}, []), %% complex_or_2/1 - ?line ok = complex_or_2({true,{}}), - ?line ok = complex_or_2({false,{a}}), - ?line ok = complex_or_2({false,{a,b,c}}), - ?line ok = complex_or_2({true,{a,b,c,d}}), + ok = complex_or_2({true,{}}), + ok = complex_or_2({false,{a}}), + ok = complex_or_2({false,{a,b,c}}), + ok = complex_or_2({true,{a,b,c,d}}), - ?line error = complex_or_2({blurf,{a,b,c}}), + error = complex_or_2({blurf,{a,b,c}}), - ?line error = complex_or_2({true}), - ?line error = complex_or_2({true,no_tuple}), - ?line error = complex_or_2({true,[]}), + error = complex_or_2({true}), + error = complex_or_2({true,no_tuple}), + error = complex_or_2({true,[]}), %% complex_or_3/2 - ?line ok = complex_or_3({true}, {}), - ?line ok = complex_or_3({false}, {a}), - ?line ok = complex_or_3({false}, {a,b,c}), - ?line ok = complex_or_3({true}, {a,b,c,d}), - ?line ok = complex_or_3({false}, <<1,2,3>>), - ?line ok = complex_or_3({true}, <<1,2,3,4>>), + ok = complex_or_3({true}, {}), + ok = complex_or_3({false}, {a}), + ok = complex_or_3({false}, {a,b,c}), + ok = complex_or_3({true}, {a,b,c,d}), + ok = complex_or_3({false}, <<1,2,3>>), + ok = complex_or_3({true}, <<1,2,3,4>>), - ?line error = complex_or_3(blurf, {a,b,c}), + error = complex_or_3(blurf, {a,b,c}), - ?line error = complex_or_3({false}, <<1,2,3,4>>), - ?line error = complex_or_3([], <<1,2>>), - ?line error = complex_or_3({true}, 45), - ?line error = complex_or_3(<<>>, <<>>), + error = complex_or_3({false}, <<1,2,3,4>>), + error = complex_or_3([], <<1,2>>), + error = complex_or_3({true}, 45), + error = complex_or_3(<<>>, <<>>), %% complex_or_4/2 - ?line ok = complex_or_4(<<1,2,3>>, {true}), - ?line ok = complex_or_4(<<1,2,3>>, {false}), - ?line ok = complex_or_4(<<1,2,3>>, {true}), - ?line ok = complex_or_4({1,2,3}, {true}), - ?line error = complex_or_4({1,2,3,4}, {false}), + ok = complex_or_4(<<1,2,3>>, {true}), + ok = complex_or_4(<<1,2,3>>, {false}), + ok = complex_or_4(<<1,2,3>>, {true}), + ok = complex_or_4({1,2,3}, {true}), + error = complex_or_4({1,2,3,4}, {false}), - ?line error = complex_or_4(<<1,2,3,4>>, []), - ?line error = complex_or_4([], {true}), + error = complex_or_4(<<1,2,3,4>>, []), + error = complex_or_4([], {true}), %% complex_or_5/2 - ?line ok = complex_or_5(<<1>>, {false}), - ?line ok = complex_or_5(<<1,2,3>>, {true}), - ?line ok = complex_or_5(<<1,2,3,4>>, {false}), - ?line ok = complex_or_5({1,2,3}, {false}), - ?line ok = complex_or_5({1,2,3,4}, {false}), + ok = complex_or_5(<<1>>, {false}), + ok = complex_or_5(<<1,2,3>>, {true}), + ok = complex_or_5(<<1,2,3,4>>, {false}), + ok = complex_or_5({1,2,3}, {false}), + ok = complex_or_5({1,2,3,4}, {false}), - ?line error = complex_or_5(blurf, {false}), - ?line error = complex_or_5(<<1>>, klarf), - ?line error = complex_or_5(blurf, klarf), + error = complex_or_5(blurf, {false}), + error = complex_or_5(<<1>>, klarf), + error = complex_or_5(blurf, klarf), %% complex_or_6/2 - ?line ok = complex_or_6({true,true}, {1,2,3,4}), - ?line ok = complex_or_6({true,true}, <<1,2,3,4>>), - ?line ok = complex_or_6({false,false}, <<1,2,3,4>>), - ?line ok = complex_or_6({false,true}, <<1>>), - ?line ok = complex_or_6({true,false}, {1}), - ?line ok = complex_or_6({true,true}, {1}), + ok = complex_or_6({true,true}, {1,2,3,4}), + ok = complex_or_6({true,true}, <<1,2,3,4>>), + ok = complex_or_6({false,false}, <<1,2,3,4>>), + ok = complex_or_6({false,true}, <<1>>), + ok = complex_or_6({true,false}, {1}), + ok = complex_or_6({true,true}, {1}), - ?line error = complex_or_6({false,false}, {1}), + error = complex_or_6({false,false}, {1}), - ?line error = complex_or_6({true}, {1,2,3,4}), - ?line error = complex_or_6({}, {1,2,3,4}), - ?line error = complex_or_6([], {1,2,3,4}), - ?line error = complex_or_6([], {1,2,3,4}), - ?line error = complex_or_6({true,false}, klurf), + error = complex_or_6({true}, {1,2,3,4}), + error = complex_or_6({}, {1,2,3,4}), + error = complex_or_6([], {1,2,3,4}), + error = complex_or_6([], {1,2,3,4}), + error = complex_or_6({true,false}, klurf), ok. @@ -739,79 +754,79 @@ and_guard(Config) when is_list(Config) -> %% 'and' combinations of literal true/false. - ?line check(fun() -> if true and false -> ok; true -> error end end, error), - ?line check(fun() -> if false and true -> ok; true -> error end end, error), - ?line check(fun() -> if true and true -> ok end end, ok), - ?line check(fun() -> if false and false -> ok; true -> error end end, error), - - ?line check(fun() -> if glurf and true -> ok; true -> error end end, error), - ?line check(fun() -> if true and glurf -> ok; true -> error end end, error), - ?line check(fun() -> if glurf and glurf -> ok; true -> error end end, error), - - ?line check(fun() -> - {'EXIT',{if_clause,_}} = - (catch if true and false -> ok; - false and true -> ok; - false and false -> ok - end), - exit - end, exit), + check(fun() -> if true and false -> ok; true -> error end end, error), + check(fun() -> if false and true -> ok; true -> error end end, error), + check(fun() -> if true and true -> ok end end, ok), + check(fun() -> if false and false -> ok; true -> error end end, error), + + check(fun() -> if glurf and true -> ok; true -> error end end, error), + check(fun() -> if true and glurf -> ok; true -> error end end, error), + check(fun() -> if glurf and glurf -> ok; true -> error end end, error), + + check(fun() -> + {'EXIT',{if_clause,_}} = + (catch if true and false -> ok; + false and true -> ok; + false and false -> ok + end), + exit + end, exit), %% 'and' combinations of true/false in variables. True = id(true), False = id(false), - ?line check(fun() -> if True and False -> ok; true -> error end end, error), - ?line check(fun() -> if False and True -> ok; true -> error end end, error), - ?line check(fun() -> if True and True -> ok end end, ok), - ?line check(fun() -> if False and False -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = - (catch if True and False -> ok; - False and True -> ok; - False and False -> ok - end), - exit - end, exit), + check(fun() -> if True and False -> ok; true -> error end end, error), + check(fun() -> if False and True -> ok; true -> error end end, error), + check(fun() -> if True and True -> ok end end, ok), + check(fun() -> if False and False -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = + (catch if True and False -> ok; + False and True -> ok; + False and False -> ok + end), + exit + end, exit), %% 'and' combinations of true/false and a non-boolean in variables. Glurf = id(glurf), - ?line check(fun() -> if True and Glurf -> ok; true -> error end end, error), - ?line check(fun() -> if Glurf and True -> ok; true -> error end end, error), - ?line check(fun() -> if True and True -> ok end end, ok), - ?line check(fun() -> if Glurf and Glurf -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = - (catch if True and Glurf -> ok; - Glurf and True -> ok; - Glurf and Glurf -> ok - end), - exit - end, exit), + check(fun() -> if True and Glurf -> ok; true -> error end end, error), + check(fun() -> if Glurf and True -> ok; true -> error end end, error), + check(fun() -> if True and True -> ok end end, ok), + check(fun() -> if Glurf and Glurf -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = + (catch if True and Glurf -> ok; + Glurf and True -> ok; + Glurf and Glurf -> ok + end), + exit + end, exit), %% 'and' combinations of true/false with errors. ATuple = id({a,b,c}), - ?line check(fun() -> if True and element(42, ATuple) -> ok; - true -> error end end, error), - ?line check(fun() -> if element(42, ATuple) and True -> ok; - true -> error end end, error), - ?line check(fun() -> if True and True -> ok end end, ok), - ?line check(fun() -> if element(42, ATuple) and element(42, ATuple) -> ok; - true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = - (catch if True and element(42, ATuple) -> ok; - element(42, ATuple) and True -> ok; - element(42, ATuple) and element(42, ATuple) -> ok - end), + check(fun() -> if True and element(42, ATuple) -> ok; + true -> error end end, error), + check(fun() -> if element(42, ATuple) and True -> ok; + true -> error end end, error), + check(fun() -> if True and True -> ok end end, ok), + check(fun() -> if element(42, ATuple) and element(42, ATuple) -> ok; + true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = + (catch if True and element(42, ATuple) -> ok; + element(42, ATuple) and True -> ok; + element(42, ATuple) and element(42, ATuple) -> ok + end), exit end, exit), - ?line ok = relprod({'Set',a,b}, {'Set',a,b}), + ok = relprod({'Set',a,b}, {'Set',a,b}), ok = and_same_var(42), {'EXIT',{if_clause,_}} = (catch and_same_var(x)), @@ -830,18 +845,18 @@ relprod(R1, R2) when (erlang:size(R1) =:= 3) and (erlang:element(1,R1) =:= 'Set' xor_guard(Config) when is_list(Config) -> %% 'xor' combinations of literal true/false. - ?line check(fun() -> if true xor false -> ok end end, ok), - ?line check(fun() -> if false xor true -> ok end end, ok), - ?line check(fun() -> if true xor true -> ok; true -> error end end, error), - ?line check(fun() -> if false xor false -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = (catch if false xor false -> ok end), - exit - end, exit), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = (catch if true xor true -> ok end), - exit - end, exit), + check(fun() -> if true xor false -> ok end end, ok), + check(fun() -> if false xor true -> ok end end, ok), + check(fun() -> if true xor true -> ok; true -> error end end, error), + check(fun() -> if false xor false -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = (catch if false xor false -> ok end), + exit + end, exit), + check(fun() -> + {'EXIT',{if_clause,_}} = (catch if true xor true -> ok end), + exit + end, exit), %% 'xor' combinations using variables containing true/false. @@ -849,18 +864,18 @@ xor_guard(Config) when is_list(Config) -> True = id(true), False = id(false), - ?line check(fun() -> if True xor False -> ok end end, ok), - ?line check(fun() -> if False xor True -> ok end end, ok), - ?line check(fun() -> if True xor True -> ok; true -> error end end, error), - ?line check(fun() -> if False xor False -> ok; true -> error end end, error), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = (catch if False xor False -> ok end), - exit - end, exit), - ?line check(fun() -> - {'EXIT',{if_clause,_}} = (catch if True xor True -> ok end), - exit - end, exit), + check(fun() -> if True xor False -> ok end end, ok), + check(fun() -> if False xor True -> ok end end, ok), + check(fun() -> if True xor True -> ok; true -> error end end, error), + check(fun() -> if False xor False -> ok; true -> error end end, error), + check(fun() -> + {'EXIT',{if_clause,_}} = (catch if False xor False -> ok end), + exit + end, exit), + check(fun() -> + {'EXIT',{if_clause,_}} = (catch if True xor True -> ok end), + exit + end, exit), ok. @@ -869,53 +884,53 @@ more_xor_guards(Config) when is_list(Config) -> False = id(false), ATuple = id({false,true,gurka}), - ?line check(fun() -> - if element(42, ATuple) xor False -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if False xor element(42, ATuple) xor False -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if element(18, ATuple) xor element(42, ATuple) -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if True xor element(42, ATuple) -> ok; - true -> error end - end, error), - - ?line check(fun() -> - if element(42, ATuple) xor True -> ok; - true -> error end - end, error), + check(fun() -> + if element(42, ATuple) xor False -> ok; + true -> error end + end, error), + + check(fun() -> + if False xor element(42, ATuple) xor False -> ok; + true -> error end + end, error), + + check(fun() -> + if element(18, ATuple) xor element(42, ATuple) -> ok; + true -> error end + end, error), + + check(fun() -> + if True xor element(42, ATuple) -> ok; + true -> error end + end, error), + + check(fun() -> + if element(42, ATuple) xor True -> ok; + true -> error end + end, error), ok. build_in_guard(Config) when is_list(Config) -> SubBin = <<5.0/float>>, - ?line B = <<1,SubBin/binary,3.5/float>>, - ?line if - B =:= <<1,SubBin/binary,3.5/float>> -> ok - end. + B = <<1,SubBin/binary,3.5/float>>, + if + B =:= <<1,SubBin/binary,3.5/float>> -> ok + end. old_guard_tests(Config) when list(Config) -> %% Check that all the old guard tests are still recognized. - ?line list = og(Config), - ?line atom = og(an_atom), - ?line binary = og(<<1,2>>), - ?line float = og(3.14), - ?line integer = og(43), - ?line a_function = og(fun() -> ok end), - ?line pid = og(self()), - ?line reference = og(make_ref()), - ?line tuple = og({}), - - ?line number = on(45.333), - ?line number = on(-19), + list = og(Config), + atom = og(an_atom), + binary = og(<<1,2>>), + float = og(3.14), + integer = og(43), + a_function = og(fun() -> ok end), + pid = og(self()), + reference = og(make_ref()), + tuple = og({}), + + number = on(45.333), + number = on(-19), ok. og(V) when atom(V) -> atom; @@ -933,9 +948,29 @@ og(_) -> what. on(V) when number(V) -> number; on(_) -> not_number. +complex_guard(_Config) -> + _ = [true = do_complex_guard(X, Y, Z) || + X <- [4,5], Y <- [4,5], Z <- [4,5]], + _ = [true = do_complex_guard(X, Y, Z) || + X <- [1,2,3], Y <- [1,2,3], Z <- [1,2,3]], + _ = [catch do_complex_guard(X, Y, Z) || + X <- [1,2,3,4,5], Y <- [0,6], Z <- [1,2,3,4,5]], + ok. + +do_complex_guard(X1, Y1, Z1) -> + if + ((X1 =:= 4) or (X1 =:= 5)) and + ((Y1 =:= 4) or (Y1 =:= 5)) and + ((Z1 =:= 4) or (Z1 =:= 5)) or + ((X1 =:= 1) or (X1 =:= 2) or (X1 =:= 3)) and + ((Y1 =:= 1) or (Y1 =:= 2) or (Y1 =:= 3)) and + ((Z1 =:= 1) or (Z1 =:= 2) or (Z1 =:= 3)) -> + true + end. + gbif(Config) when is_list(Config) -> - ?line error = gbif_1(1, {false,true}), - ?line ok = gbif_1(2, {false,true}), + error = gbif_1(1, {false,true}), + ok = gbif_1(2, {false,true}), ok. gbif_1(P, T) when element(P, T) -> ok; @@ -943,57 +978,57 @@ gbif_1(_, _) -> error. t_is_boolean(Config) when is_list(Config) -> - ?line true = is_boolean(true), - ?line true = is_boolean(false), - ?line true = is_boolean(id(true)), - ?line true = is_boolean(id(false)), - - ?line false = is_boolean(glurf), - ?line false = is_boolean(id(glurf)), - - ?line false = is_boolean([]), - ?line false = is_boolean(id([])), - ?line false = is_boolean(42), - ?line false = is_boolean(id(-42)), - - ?line false = is_boolean(math:pi()), - ?line false = is_boolean(384793478934378924978439789873478934897), - - ?line false = is_boolean(id(self())), - ?line false = is_boolean(id({x,y,z})), - ?line false = is_boolean(id([a,b,c])), - ?line false = is_boolean(id(make_ref())), - ?line false = is_boolean(id(<<1,2,3>>)), - ?line false = is_boolean({id(x),y,z}), - ?line false = is_boolean([id(a),b,c]), - - ?line ok = bool(true), - ?line ok = bool(false), - ?line ok = bool(id(true)), - ?line ok = bool(id(false)), - - ?line error = bool(glurf), - ?line error = bool(id(glurf)), - - ?line error = bool([]), - ?line error = bool(id([])), - ?line error = bool(42), - ?line error = bool(id(-42)), - - ?line error = bool(math:pi()), - ?line error = bool(384793478934378924978439789873478934897), - - ?line error = bool(id(self())), - ?line error = bool(id({x,y,z})), - ?line error = bool(id([a,b,c])), - ?line error = bool(id(make_ref())), - ?line error = bool(id(<<1,2,3>>)), - - ?line true = my_is_bool(true), - ?line true = my_is_bool(false), - ?line false = my_is_bool([]), - ?line false = my_is_bool([1,2,3,4]), - ?line false = my_is_bool({a,b,c}), + true = is_boolean(true), + true = is_boolean(false), + true = is_boolean(id(true)), + true = is_boolean(id(false)), + + false = is_boolean(glurf), + false = is_boolean(id(glurf)), + + false = is_boolean([]), + false = is_boolean(id([])), + false = is_boolean(42), + false = is_boolean(id(-42)), + + false = is_boolean(math:pi()), + false = is_boolean(384793478934378924978439789873478934897), + + false = is_boolean(id(self())), + false = is_boolean(id({x,y,z})), + false = is_boolean(id([a,b,c])), + false = is_boolean(id(make_ref())), + false = is_boolean(id(<<1,2,3>>)), + false = is_boolean({id(x),y,z}), + false = is_boolean([id(a),b,c]), + + ok = bool(true), + ok = bool(false), + ok = bool(id(true)), + ok = bool(id(false)), + + error = bool(glurf), + error = bool(id(glurf)), + + error = bool([]), + error = bool(id([])), + error = bool(42), + error = bool(id(-42)), + + error = bool(math:pi()), + error = bool(384793478934378924978439789873478934897), + + error = bool(id(self())), + error = bool(id({x,y,z})), + error = bool(id([a,b,c])), + error = bool(id(make_ref())), + error = bool(id(<<1,2,3>>)), + + true = my_is_bool(true), + true = my_is_bool(false), + false = my_is_bool([]), + false = my_is_bool([1,2,3,4]), + false = my_is_bool({a,b,c}), ok. @@ -1034,18 +1069,18 @@ is_function_2(Config) when is_list(Config) -> end. tricky(Config) when is_list(Config) -> - ?line not_ok = tricky_1(1, 2), - ?line not_ok = tricky_1(1, blurf), - ?line not_ok = tricky_1(foo, 2), - ?line not_ok = tricky_1(a, b), - - ?line error = tricky_2(0.5), - ?line error = tricky_2(a), - ?line error = tricky_2({a,b,c}), - - ?line false = rb(100000, [1], 42), - ?line true = rb(100000, [], 42), - ?line true = rb(555, [a,b,c], 19), + not_ok = tricky_1(1, 2), + not_ok = tricky_1(1, blurf), + not_ok = tricky_1(foo, 2), + not_ok = tricky_1(a, b), + + error = tricky_2(0.5), + error = tricky_2(a), + error = tricky_2({a,b,c}), + + false = rb(100000, [1], 42), + true = rb(100000, [], 42), + true = rb(555, [a,b,c], 19), ok. tricky_1(X, Y) when abs((X == 1) or (Y == 2)) -> ok; @@ -1083,45 +1118,270 @@ rb(_, _, _) -> false. rel_ops(Config) when is_list(Config) -> - ?line ?T(=/=, 1, 1.0), - ?line ?F(=/=, 2, 2), - ?line ?F(=/=, {a}, {a}), + ?T(=/=, 1, 1.0), + ?F(=/=, 2, 2), + ?F(=/=, {a}, {a}), - ?line ?F(/=, a, a), - ?line ?F(/=, 0, 0.0), - ?line ?T(/=, 0, 1), - ?line ?F(/=, {a}, {a}), + ?F(/=, a, a), + ?F(/=, 0, 0.0), + ?T(/=, 0, 1), + ?F(/=, {a}, {a}), - ?line ?T(==, 1, 1.0), - ?line ?F(==, a, {}), + ?T(==, 1, 1.0), + ?F(==, a, {}), - ?line ?F(=:=, 1, 1.0), - ?line ?T(=:=, 42.0, 42.0), + ?F(=:=, 1, 1.0), + ?T(=:=, 42.0, 42.0), - ?line ?F(>, a, b), - ?line ?T(>, 42, 1.0), - ?line ?F(>, 42, 42.0), + ?F(>, a, b), + ?T(>, 42, 1.0), + ?F(>, 42, 42.0), - ?line ?T(<, a, b), - ?line ?F(<, 42, 1.0), - ?line ?F(<, 42, 42.0), + ?T(<, a, b), + ?F(<, 42, 1.0), + ?F(<, 42, 42.0), - ?line ?T(=<, 1.5, 5), - ?line ?F(=<, -9, -100.344), - ?line ?T(=<, 42, 42.0), + ?T(=<, 1.5, 5), + ?F(=<, -9, -100.344), + ?T(=<, 42, 42.0), - ?line ?T(>=, 42, 42.0), - ?line ?F(>=, a, b), - ?line ?T(>=, 1.0, 0), + ?T(>=, 42, 42.0), + ?F(>=, a, b), + ?T(>=, 1.0, 0), %% Coverage of beam_block:is_exact_eq_ok/1 and collect/1. - ?line true = any_atom /= id(42), - ?line true = [] /= id(42), + true = any_atom /= id(42), + true = [] /= id(42), ok. -undef(TestOp). +rel_op_combinations(Config) when is_list(Config) -> + Digits0 = lists:seq(16#0030, 16#0039) ++ + lists:seq(16#0660, 16#0669) ++ + lists:seq(16#06F0, 16#06F9), + Digits = gb_sets:from_list(Digits0), + rel_op_combinations_1(16#0700, Digits), + + BrokenRange0 = lists:seq(3, 5) ++ + lists:seq(10, 12) ++ lists:seq(14, 20), + BrokenRange = gb_sets:from_list(BrokenRange0), + rel_op_combinations_2(30, BrokenRange), + + Red0 = [{I,2*I} || I <- lists:seq(0, 50)] ++ + [{I,5*I} || I <- lists:seq(51, 80)], + Red = gb_trees:from_orddict(Red0), + rel_op_combinations_3(100, Red). + +rel_op_combinations_1(0, _) -> + ok; +rel_op_combinations_1(N, Digits) -> + Bool = gb_sets:is_member(N, Digits), + Bool = is_digit_1(N), + Bool = is_digit_2(N), + Bool = is_digit_3(N), + Bool = is_digit_4(N), + Bool = is_digit_5(N), + Bool = is_digit_6(N), + Bool = is_digit_7(N), + Bool = is_digit_8(N), + rel_op_combinations_1(N-1, Digits). + +is_digit_1(X) when 16#0660 =< X, X =< 16#0669 -> true; +is_digit_1(X) when 16#0030 =< X, X =< 16#0039 -> true; +is_digit_1(X) when 16#06F0 =< X, X =< 16#06F9 -> true; +is_digit_1(_) -> false. + +is_digit_2(X) when (16#0030-1) < X, X =< 16#0039 -> true; +is_digit_2(X) when (16#0660-1) < X, X =< 16#0669 -> true; +is_digit_2(X) when (16#06F0-1) < X, X =< 16#06F9 -> true; +is_digit_2(_) -> false. + +is_digit_3(X) when 16#0660 =< X, X < (16#0669+1) -> true; +is_digit_3(X) when 16#0030 =< X, X < (16#0039+1) -> true; +is_digit_3(X) when 16#06F0 =< X, X < (16#06F9+1) -> true; +is_digit_3(_) -> false. + +is_digit_4(X) when (16#0660-1) < X, X < (16#0669+1) -> true; +is_digit_4(X) when (16#0030-1) < X, X < (16#0039+1) -> true; +is_digit_4(X) when (16#06F0-1) < X, X < (16#06F9+1) -> true; +is_digit_4(_) -> false. + +is_digit_5(X) when X >= 16#0660, X =< 16#0669 -> true; +is_digit_5(X) when X >= 16#0030, X =< 16#0039 -> true; +is_digit_5(X) when X >= 16#06F0, X =< 16#06F9 -> true; +is_digit_5(_) -> false. + +is_digit_6(X) when X > (16#0660-1), X =< 16#0669 -> true; +is_digit_6(X) when X > (16#0030-1), X =< 16#0039 -> true; +is_digit_6(X) when X > (16#06F0-1), X =< 16#06F9 -> true; +is_digit_6(_) -> false. + +is_digit_7(X) when 16#0660 =< X, X =< 16#0669 -> true; +is_digit_7(X) when 16#0030 =< X, X =< 16#003A, X =/= 16#003A -> true; +is_digit_7(X) when 16#06F0 =< X, X =< 16#06F9 -> true; +is_digit_7(_) -> false. + +is_digit_8(X) when X =< 16#0039, X > (16#0030-1) -> true; +is_digit_8(X) when X =< 16#06F9, X > (16#06F0-1) -> true; +is_digit_8(X) when X =< 16#0669, X > (16#0660-1) -> true; +is_digit_8(16#0670) -> false; +is_digit_8(_) -> false. + +rel_op_combinations_2(0, _) -> + ok; +rel_op_combinations_2(N, Range) -> + Bool = gb_sets:is_member(N, Range), + Bool = broken_range_1(N), + Bool = broken_range_2(N), + Bool = broken_range_3(N), + Bool = broken_range_4(N), + Bool = broken_range_5(N), + Bool = broken_range_6(N), + Bool = broken_range_7(N), + Bool = broken_range_8(N), + Bool = broken_range_9(N), + Bool = broken_range_10(N), + Bool = broken_range_11(N), + Bool = broken_range_12(N), + Bool = broken_range_13(N), + rel_op_combinations_2(N-1, Range). + +broken_range_1(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_1(X) when X >= 3, X =< 5 -> true; +broken_range_1(_) -> false. + +broken_range_2(X) when X >= 10, X =< 12 -> true; +broken_range_2(X) when X >= 14, X =< 20 -> true; +broken_range_2(X) when X >= 3, X =< 5 -> true; +broken_range_2(_) -> false. + +broken_range_3(X) when X >= 10, X =< 12 -> true; +broken_range_3(X) when X >= 14, X < 21 -> true; +broken_range_3(3) -> true; +broken_range_3(4) -> true; +broken_range_3(5) -> true; +broken_range_3(_) -> false. + +broken_range_4(X) when X =< 5, X >= 3 -> true; +broken_range_4(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_4(X) when X =< 100 -> false; +broken_range_4(_) -> false. + +broken_range_5(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_5(X) when X > 2, X =< 5 -> true; +broken_range_5(_) -> false. + +broken_range_6(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_6(X) when X > 2, X < 6 -> true; +broken_range_6(_) -> false. + +broken_range_7(X) when X > 2, X < 6 -> true; +broken_range_7(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_7(X) when X > 30 -> false; +broken_range_7(_) -> false. + +broken_range_8(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_8(X) when X =:= 3 -> true; +broken_range_8(X) when X >= 3, X =< 5 -> true; +broken_range_8(_) -> false. + +broken_range_9(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_9(X) when X =:= 13 -> false; +broken_range_9(X) when X >= 3, X =< 5 -> true; +broken_range_9(_) -> false. + +broken_range_10(X) when X >= 3, X =< 5 -> true; +broken_range_10(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_10(X) when X =/= 13 -> false; +broken_range_10(_) -> false. + +broken_range_11(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_11(X) when is_tuple(X), X =:= 10 -> true; +broken_range_11(X) when X >= 3, X =< 5 -> true; +broken_range_11(_) -> false. + +broken_range_12(X) when X >= 3, X =< 5 -> true; +broken_range_12(X) when X >= 10, X =< 20, X =/= 13 -> true; +broken_range_12(X) when X < 30, X > 20 -> false; +broken_range_12(_) -> false. + +broken_range_13(X) when X >= 10, X =< 20, 13 =/= X -> true; +broken_range_13(X) when X >= 3, X =< 5 -> true; +broken_range_13(_) -> false. + +rel_op_combinations_3(0, _) -> + ok; +rel_op_combinations_3(N, Red) -> + Val = case gb_trees:lookup(N, Red) of + none -> none; + {value,V} -> V + end, + Val = redundant_1(N), + Val = redundant_2(N), + Val = redundant_3(N), + Val = redundant_4(N), + Val = redundant_5(N), + Val = redundant_6(N), + Val = redundant_7(N), + Val = redundant_8(N), + Val = redundant_9(N), + Val = redundant_10(N), + Val = redundant_11(N), + rel_op_combinations_3(N-1, Red). + +redundant_1(X) when X >= 51, X =< 80 -> 5*X; +redundant_1(X) when X < 51 -> 2*X; +redundant_1(_) -> none. + +redundant_2(X) when X < 51 -> 2*X; +redundant_2(X) when X >= 51, X =< 80 -> 5*X; +redundant_2(_) -> none. + +redundant_3(X) when X < 51 -> 2*X; +redundant_3(X) when X =< 80, X >= 51 -> 5*X; +redundant_3(X) when X =/= 100 -> none; +redundant_3(_) -> none. + +redundant_4(X) when X < 51 -> 2*X; +redundant_4(X) when X =< 80, X > 50 -> 5*X; +redundant_4(X) when X =/= 100 -> none; +redundant_4(_) -> none. + +redundant_5(X) when X < 51 -> 2*X; +redundant_5(X) when X > 50, X < 81 -> 5*X; +redundant_5(X) when X =< 10 -> none; +redundant_5(_) -> none. + +redundant_6(X) when X > 50, X =< 80 -> 5*X; +redundant_6(X) when X < 51 -> 2*X; +redundant_6(_) -> none. + +redundant_7(X) when is_integer(X), X >= 51, X =< 80 -> 5*X; +redundant_7(X) when is_integer(X), X < 51 -> 2*X; +redundant_7(_) -> none. + +redundant_8(X) when X >= 51, X =< 80 -> 5*X; +redundant_8(X) when X < 51 -> 2*X; +redundant_8(_) -> none. + +redundant_9(X) when X >= 51, X =< 80 -> 5*X; +redundant_9(X) when X < 51 -> 2*X; +redundant_9(90) -> none; +redundant_9(X) when X =/= 90 -> none; +redundant_9(_) -> none. + +redundant_10(X) when X >= 51, X =< 80 -> 5*X; +redundant_10(X) when X < 51 -> 2*X; +redundant_10(90) -> none; +redundant_10(X) when X =:= 90 -> none; +redundant_10(_) -> none. + +redundant_11(X) when X < 51 -> 2*X; +redundant_11(X) when X =:= 10 -> 2*X; +redundant_11(X) when X >= 51, X =< 80 -> 5*X; +redundant_11(_) -> none. %% Test type tests on literal values. (From emulator test suites.) literal_type_tests(Config) when is_list(Config) -> @@ -1132,34 +1392,35 @@ literal_type_tests(Config) when is_list(Config) -> literal_type_tests_1(Config) -> %% Generate an Erlang module with all different type of type tests. - ?line Tests = make_test([{T,L} || T <- type_tests(), L <- literals()] ++ + Tests = make_test([{T,L} || T <- type_tests(), L <- literals()] ++ [{is_function,L1,L2} || L1 <- literals(), L2 <- literals()]), - ?line Mod = literal_test, - ?line Func = {function, 0, test, 0, [{clause,0,[],[],Tests}]}, - ?line Form = [{attribute,0,module,Mod}, - {attribute,0,compile,export_all}, - Func, {eof,0}], + Mod = literal_test, + Anno = erl_anno:new(0), + Func = {function, Anno, test, 0, [{clause,Anno,[],[],Tests}]}, + Form = [{attribute,Anno,module,Mod}, + {attribute,Anno,compile,export_all}, + Func, {eof,Anno}], %% Print generated code for inspection. - ?line lists:foreach(fun (F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Form), + lists:foreach(fun (F) -> io:put_chars([erl_pp:form(F),"\n"]) end, Form), %% Test compile:form/1. This implies full optimization (default). - ?line {ok,Mod,Code1} = compile:forms(Form), - ?line smoke_disasm(Config, Mod, Code1), - ?line {module,Mod} = code:load_binary(Mod, Mod, Code1), - ?line Mod:test(), - ?line true = code:delete(Mod), - ?line code:purge(Mod), + {ok,Mod,Code1} = compile:forms(Form), + smoke_disasm(Config, Mod, Code1), + {module,Mod} = code:load_binary(Mod, Mod, Code1), + Mod:test(), + true = code:delete(Mod), + code:purge(Mod), %% Test compile:form/2. Turn off all optimizations. - ?line {ok,Mod,Code2} = compile:forms(Form, [binary,report,time, + {ok,Mod,Code2} = compile:forms(Form, [binary,report,time, no_copt,no_postopt]), - ?line smoke_disasm(Config, Mod, Code2), - ?line {module,Mod} = code:load_binary(Mod, Mod, Code2), - ?line Mod:test(), - ?line true = code:delete(Mod), - ?line code:purge(Mod), + smoke_disasm(Config, Mod, Code2), + {module,Mod} = code:load_binary(Mod, Mod, Code2), + Mod:test(), + true = code:delete(Mod), + code:purge(Mod), ok. make_test([{T,L1,L2}|Ts]) -> @@ -1174,7 +1435,8 @@ test(T, L) -> {ok,Toks,_Line} = erl_scan:string(S), {ok,E} = erl_parse:parse_exprs(Toks), {value,Val,_Bs} = erl_eval:exprs(E, []), - {match,0,{atom,0,Val},hd(E)}. + Anno = erl_anno:new(0), + {match,Anno,{atom,Anno,Val},hd(E)}. test(T, L1, L2) -> S0 = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p,~p}]), if ~w(~w, ~w) -> true; true -> false end end. ", [T,L1,L2,T,L1,L2]), @@ -1182,10 +1444,11 @@ test(T, L1, L2) -> {ok,Toks,_Line} = erl_scan:string(S), {ok,E} = erl_parse:parse_exprs(Toks), {value,Val,_Bs} = erl_eval:exprs(E, []), - {match,0,{atom,0,Val},hd(E)}. + Anno = erl_anno:new(0), + {match,Anno,{atom,Anno,Val},hd(E)}. smoke_disasm(Config, Mod, Bin) -> - Priv = ?config(priv_dir, Config), + Priv = proplists:get_value(priv_dir, Config), File = filename:join(Priv, atom_to_list(Mod)++".beam"), ok = file:write_file(File, Bin), test_lib:smoke_disasm(File). @@ -1217,30 +1480,30 @@ type_tests() -> is_function]. basic_andalso_orelse(Config) when is_list(Config) -> - ?line T = id({type,integers,23,42}), - ?line 65 = if - ((element(1, T) =:= type) andalso (tuple_size(T) =:= 4) andalso - element(2, T)) == integers -> - element(3, T) + element(4, T); - true -> error - end, - ?line 65 = case [] of - [] when ((element(1, T) =:= type) andalso (tuple_size(T) =:= 4) andalso - element(2, T)) == integers -> - element(3, T) + element(4, T) - end, - - ?line 42 = basic_rt({type,integers,40,2}), - ?line 5.0 = basic_rt({vector,{3.0,4.0}}), - ?line 20 = basic_rt(['+',3,7]), - ?line {'Set',a,b} = basic_rt({{'Set',a,b},{'Set',a,b}}), - ?line 12 = basic_rt({klurf,4}), - - ?line error = basic_rt({type,integers,40,2,3}), - ?line error = basic_rt({kalle,integers,40,2}), - ?line error = basic_rt({kalle,integers,40,2}), - ?line error = basic_rt({1,2}), - ?line error = basic_rt([]), + T = id({type,integers,23,42}), + 65 = if + ((element(1, T) =:= type) andalso (tuple_size(T) =:= 4) andalso + element(2, T)) == integers -> + element(3, T) + element(4, T); + true -> error + end, + 65 = case [] of + [] when ((element(1, T) =:= type) andalso (tuple_size(T) =:= 4) andalso + element(2, T)) == integers -> + element(3, T) + element(4, T) + end, + + 42 = basic_rt({type,integers,40,2}), + 5.0 = basic_rt({vector,{3.0,4.0}}), + 20 = basic_rt(['+',3,7]), + {'Set',a,b} = basic_rt({{'Set',a,b},{'Set',a,b}}), + 12 = basic_rt({klurf,4}), + + error = basic_rt({type,integers,40,2,3}), + error = basic_rt({kalle,integers,40,2}), + error = basic_rt({kalle,integers,40,2}), + error = basic_rt({1,2}), + error = basic_rt([]), RelProdBody = fun(R1, R2) -> @@ -1251,7 +1514,7 @@ basic_andalso_orelse(Config) when is_list(Config) -> end end, - ?line ok = RelProdBody({'Set',a,b}, {'Set',a,b}), + ok = RelProdBody({'Set',a,b}, {'Set',a,b}), %% 'andalso'/'orelse' with calls known to fail already at compile time. %% Used to crash the code generator. @@ -1322,14 +1585,14 @@ traverse_dcd({Cont,Recs},Log,Fun) -> check_qlc_hrl(Config) when is_list(Config) -> St = {r1,false,dum}, - ?line foo = cqlc(qlc, q, [{lc,1,2,3}], St), - ?line foo = cqlc(qlc, q, [{lc,1,2,3},b], St), - ?line St = cqlc(qlc, q, [], St), - ?line St = cqlc(qlc, blurf, [{lc,1,2,3},b], St), - ?line St = cqlc(q, q, [{lc,1,2,3},b], St), - ?line St = cqlc(qlc, q, [{lc,1,2,3},b,c], St), - ?line St = cqlc(qlc, q, [a,b], St), - ?line {r1,true,kalle} = cqlc(qlc, q, [{lc,1,2,3},b], {r1,true,kalle}), + foo = cqlc(qlc, q, [{lc,1,2,3}], St), + foo = cqlc(qlc, q, [{lc,1,2,3},b], St), + St = cqlc(qlc, q, [], St), + St = cqlc(qlc, blurf, [{lc,1,2,3},b], St), + St = cqlc(q, q, [{lc,1,2,3},b], St), + St = cqlc(qlc, q, [{lc,1,2,3},b,c], St), + St = cqlc(qlc, q, [a,b], St), + {r1,true,kalle} = cqlc(qlc, q, [{lc,1,2,3},b], {r1,true,kalle}), ok. %% From erl_lint.erl; original name was check_qlc_hrl/4. @@ -1346,13 +1609,13 @@ cqlc(M, F, As, St) -> %% OTP-7679: Thanks to Hunter Morris. andalso_semi(Config) when is_list(Config) -> - ?line ok = andalso_semi_foo(0), - ?line ok = andalso_semi_foo(1), - ?line fc(catch andalso_semi_foo(2)), + ok = andalso_semi_foo(0), + ok = andalso_semi_foo(1), + fc(catch andalso_semi_foo(2)), - ?line ok = andalso_semi_bar([a,b,c]), - ?line ok = andalso_semi_bar(1), - ?line fc(catch andalso_semi_bar([a,b])), + ok = andalso_semi_bar([a,b,c]), + ok = andalso_semi_bar(1), + fc(catch andalso_semi_bar([a,b])), ok. andalso_semi_foo(Bar) when is_integer(Bar) andalso Bar =:= 0; Bar =:= 1 -> @@ -1363,20 +1626,22 @@ andalso_semi_bar(Bar) when is_list(Bar) andalso length(Bar) =:= 3; Bar =:= 1 -> t_tuple_size(Config) when is_list(Config) -> - ?line 10 = do_tuple_size({1,2,3,4}), - ?line fc(catch do_tuple_size({1,2,3})), - ?line fc(catch do_tuple_size(42)), + 10 = do_tuple_size({1,2,3,4}), + fc(catch do_tuple_size({1,2,3})), + fc(catch do_tuple_size(42)), - ?line error = ludicrous_tuple_size({a,b,c}), - ?line error = ludicrous_tuple_size([a,b,c]), + error = ludicrous_tuple_size({a,b,c}), + error = ludicrous_tuple_size([a,b,c]), %% Test the "unsafe case" - the register assigned the tuple size is %% not killed. - ?line DataDir = test_lib:get_data_dir(Config), - ?line File = filename:join(DataDir, "guard_SUITE_tuple_size"), - ?line {ok,Mod,Code} = compile:file(File, [from_asm,binary]), - ?line code:load_binary(Mod, File, Code), - ?line 14 = Mod:t({1,2,3,4}), + DataDir = test_lib:get_data_dir(Config), + File = filename:join(DataDir, "guard_SUITE_tuple_size"), + {ok,Mod,Code} = compile:file(File, [from_asm,binary]), + code:load_binary(Mod, File, Code), + 14 = Mod:t({1,2,3,4}), + _ = code:delete(Mod), + _ = code:purge(Mod), ok. @@ -1403,71 +1668,70 @@ mask_error({'EXIT',{Err,_}}) -> mask_error(Else) -> Else. -binary_part(doc) -> - ["Tests the binary_part/2,3 guard (GC) bif's"]; +%% Test the binary_part/2,3 guard (GC) BIFs. binary_part(Config) when is_list(Config) -> %% This is more or less a copy of what the guard_SUITE in emulator %% does to cover the guard bif's - ?line 1 = bptest(<<1,2,3>>), - ?line 2 = bptest(<<2,1,3>>), - ?line error = bptest(<<1>>), - ?line error = bptest(<<>>), - ?line error = bptest(apa), - ?line 3 = bptest(<<2,3,3>>), + 1 = bptest(<<1,2,3>>), + 2 = bptest(<<2,1,3>>), + error = bptest(<<1>>), + error = bptest(<<>>), + error = bptest(apa), + 3 = bptest(<<2,3,3>>), % With one variable (pos) - ?line 1 = bptest(<<1,2,3>>,1), - ?line 2 = bptest(<<2,1,3>>,1), - ?line error = bptest(<<1>>,1), - ?line error = bptest(<<>>,1), - ?line error = bptest(apa,1), - ?line 3 = bptest(<<2,3,3>>,1), + 1 = bptest(<<1,2,3>>,1), + 2 = bptest(<<2,1,3>>,1), + error = bptest(<<1>>,1), + error = bptest(<<>>,1), + error = bptest(apa,1), + 3 = bptest(<<2,3,3>>,1), % With one variable (length) - ?line 1 = bptesty(<<1,2,3>>,1), - ?line 2 = bptesty(<<2,1,3>>,1), - ?line error = bptesty(<<1>>,1), - ?line error = bptesty(<<>>,1), - ?line error = bptesty(apa,1), - ?line 3 = bptesty(<<2,3,3>>,2), + 1 = bptesty(<<1,2,3>>,1), + 2 = bptesty(<<2,1,3>>,1), + error = bptesty(<<1>>,1), + error = bptesty(<<>>,1), + error = bptesty(apa,1), + 3 = bptesty(<<2,3,3>>,2), % With one variable (whole tuple) - ?line 1 = bptestx(<<1,2,3>>,{1,1}), - ?line 2 = bptestx(<<2,1,3>>,{1,1}), - ?line error = bptestx(<<1>>,{1,1}), - ?line error = bptestx(<<>>,{1,1}), - ?line error = bptestx(apa,{1,1}), - ?line 3 = bptestx(<<2,3,3>>,{1,2}), + 1 = bptestx(<<1,2,3>>,{1,1}), + 2 = bptestx(<<2,1,3>>,{1,1}), + error = bptestx(<<1>>,{1,1}), + error = bptestx(<<>>,{1,1}), + error = bptestx(apa,{1,1}), + 3 = bptestx(<<2,3,3>>,{1,2}), % With two variables - ?line 1 = bptest(<<1,2,3>>,1,1), - ?line 2 = bptest(<<2,1,3>>,1,1), - ?line error = bptest(<<1>>,1,1), - ?line error = bptest(<<>>,1,1), - ?line error = bptest(apa,1,1), - ?line 3 = bptest(<<2,3,3>>,1,2), + 1 = bptest(<<1,2,3>>,1,1), + 2 = bptest(<<2,1,3>>,1,1), + error = bptest(<<1>>,1,1), + error = bptest(<<>>,1,1), + error = bptest(apa,1,1), + 3 = bptest(<<2,3,3>>,1,2), % Direct (autoimported) call, these will be evaluated by the compiler... - ?line <<2>> = binary_part(<<1,2,3>>,1,1), - ?line <<1>> = binary_part(<<2,1,3>>,1,1), + <<2>> = binary_part(<<1,2,3>>,1,1), + <<1>> = binary_part(<<2,1,3>>,1,1), % Compiler warnings due to constant evaluation expected (3) - ?line badarg = ?MASK_ERROR(binary_part(<<1>>,1,1)), - ?line badarg = ?MASK_ERROR(binary_part(<<>>,1,1)), - ?line badarg = ?MASK_ERROR(binary_part(apa,1,1)), - ?line <<3,3>> = binary_part(<<2,3,3>>,1,2), + badarg = ?MASK_ERROR(binary_part(<<1>>,1,1)), + badarg = ?MASK_ERROR(binary_part(<<>>,1,1)), + badarg = ?MASK_ERROR(binary_part(apa,1,1)), + <<3,3>> = binary_part(<<2,3,3>>,1,2), % Direct call through apply - ?line <<2>> = apply(erlang,binary_part,[<<1,2,3>>,1,1]), - ?line <<1>> = apply(erlang,binary_part,[<<2,1,3>>,1,1]), + <<2>> = apply(erlang,binary_part,[<<1,2,3>>,1,1]), + <<1>> = apply(erlang,binary_part,[<<2,1,3>>,1,1]), % Compiler warnings due to constant evaluation expected (3) - ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<1>>,1,1])), - ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<>>,1,1])), - ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[apa,1,1])), - ?line <<3,3>> = apply(erlang,binary_part,[<<2,3,3>>,1,2]), + badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<1>>,1,1])), + badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<>>,1,1])), + badarg = ?MASK_ERROR(apply(erlang,binary_part,[apa,1,1])), + <<3,3>> = apply(erlang,binary_part,[<<2,3,3>>,1,2]), % Constant propagation - ?line Bin = <<1,2,3>>, - ?line ok = if + Bin = <<1,2,3>>, + ok = if binary_part(Bin,1,1) =:= <<2>> -> ok; %% Compiler warning, clause cannot match (expected) true -> error end, - ?line ok = if + ok = if binary_part(Bin,{1,1}) =:= <<2>> -> ok; %% Compiler warning, clause cannot match (expected) @@ -1534,24 +1798,24 @@ bptest(_,_,_) -> -define(FAILING(C), if - C -> ?t:fail(should_fail); + C -> ct:fail(should_fail); true -> ok end, if - true, C -> ?t:fail(should_fail); + true, C -> ct:fail(should_fail); true -> ok end). bad_constants(Config) when is_list(Config) -> - ?line ?FAILING(false), - ?line ?FAILING([]), - ?line ?FAILING([a]), - ?line ?FAILING([Config]), - ?line ?FAILING({a,b}), - ?line ?FAILING({a,Config}), - ?line ?FAILING(<<1>>), - ?line ?FAILING(42), - ?line ?FAILING(3.14), + ?FAILING(false), + ?FAILING([]), + ?FAILING([a]), + ?FAILING([Config]), + ?FAILING({a,b}), + ?FAILING({a,Config}), + ?FAILING(<<1>>), + ?FAILING(42), + ?FAILING(3.14), ok. bad_guards(Config) when is_list(Config) -> @@ -1565,6 +1829,14 @@ bad_guards(Config) when is_list(Config) -> fc(catch bad_guards_2(#{a=>0,b=>0}, [x])), fc(catch bad_guards_2(not_a_map, [x])), fc(catch bad_guards_2(42, [x])), + + fc(catch bad_guards_3(#{a=>0,b=>0}, [])), + fc(catch bad_guards_3(#{a=>0,b=>0}, [x])), + fc(catch bad_guards_3(not_a_map, [x])), + fc(catch bad_guards_3(42, [x])), + + fc(catch bad_guards_4()), + ok. %% beam_bool used to produce GC BIF instructions whose @@ -1576,6 +1848,71 @@ bad_guards_1(X, [_]) when {{X}}, -X -> bad_guards_2(M, [_]) when M#{a := 0, b => 0}, map_size(M) -> ok. +%% beam_type used to produce an GC BIF instruction whose Live operand +%% included uninitialized registers. + +bad_guards_3(M, [_]) when is_map(M) andalso M#{a := 0, b => 0}, length(M) -> + ok. + +%% v3_codegen would generate a jump to the failure label, but +%% without initializing x(0). The code at the failure label expected +%% x(0) to be initialized. + +bad_guards_4() when not (error#{}); {not 0.0} -> freedom. + +%% Building maps in a guard in a 'catch' would crash v3_codegen. + +guard_in_catch(_Config) -> + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_1(#{}), + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_1(#{a=>b}), + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_1(atom), + + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_2(#{}), + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_2(#{a=>b}), + {'EXIT',{if_clause,_}} = do_guard_in_catch_map_2(atom), + + {'EXIT',{if_clause,_}} = (catch do_guard_in_catch_map_3()), + + {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(42), + {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(<<1,2,3>>), + {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(atom), + {'EXIT',{if_clause,_}} = do_guard_in_catch_bin(#{}), + + ok. + +do_guard_in_catch_map_1(From) -> + catch + if + From#{[] => sufficient} -> + saint + end. + +do_guard_in_catch_map_2(From) -> + catch + if + From#{From => sufficient} -> + saint + end. + +do_guard_in_catch_map_3() -> + try + if [] -> solo end + catch + Friendly when Friendly#{0 => []} -> minutes + after + membership + end. + +do_guard_in_catch_bin(From) -> + %% Would not crash v3_codegen, but there would be an unnecessary + %% 'move' to a Y register. + catch + if + <<From:32>> -> + saint + end. + + %% Call this function to turn off constant propagation. id(I) -> I. @@ -1585,7 +1922,7 @@ check(F, Result) -> Other -> io:format("Expected: ~p\n", [Result]), io:format(" Got: ~p\n", [Other]), - test_server:fail() + ct:fail(check_failed) end. fc({'EXIT',{function_clause,_}}) -> ok; diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl index 47851e680b..ae59cc8026 100644 --- a/lib/compiler/test/inline_SUITE.erl +++ b/lib/compiler/test/inline_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -20,7 +21,7 @@ -module(inline_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -compile(export_all). -compile({inline,[badarg/2]}). @@ -46,8 +47,8 @@ init_per_suite(Config) -> [{testing_node,Node}|Config]. end_per_suite(Config) -> - Node = ?config(testing_node, Config), - ?t:stop_node(Node), + Node = proplists:get_value(testing_node, Config), + test_server:stop_node(Node), ok. init_per_group(_GroupName, Config) -> @@ -59,16 +60,16 @@ end_per_group(_GroupName, Config) -> attribute(Config) when is_list(Config) -> Name = "attribute", - ?line Src = filename:join(?config(data_dir, Config), Name), - ?line Out = ?config(priv_dir,Config), + Src = filename:join(proplists:get_value(data_dir, Config), Name), + Out = proplists:get_value(priv_dir,Config), - ?line {ok,attribute=Mod} = compile:file(Src, [{outdir,Out},report,time]), - ?line Outfile = filename:join(Out, Name++".beam"), - ?line {ok,{Mod,[{locals,Locals}]}} = beam_lib:chunks(Outfile, [locals]), - ?line io:format("locals: ~p\n", [Locals]), + {ok,attribute=Mod} = compile:file(Src, [{outdir,Out},report,time]), + Outfile = filename:join(Out, Name++".beam"), + {ok,{Mod,[{locals,Locals}]}} = beam_lib:chunks(Outfile, [locals]), + io:format("locals: ~p\n", [Locals]), %% The inliner should have removed all local functions. - ?line [] = Locals, + [] = Locals, ok. @@ -88,48 +89,46 @@ attribute(Config) when is_list(Config) -> ?comp(maps_inline_test). try_inline(Mod, Config) -> - Node = ?config(testing_node, Config), - ?line Src = filename:join(?config(data_dir, Config), atom_to_list(Mod)), - ?line Out = ?config(priv_dir,Config), + Node = proplists:get_value(testing_node, Config), + Src = filename:join(proplists:get_value(data_dir, Config), + atom_to_list(Mod)), + Out = proplists:get_value(priv_dir,Config), %% Normal compilation. - ?line io:format("Compiling: ~s\n", [Src]), - ?line {ok,Mod} = compile:file(Src, [{outdir,Out},report,bin_opt_info,clint]), + io:format("Compiling: ~s\n", [Src]), + {ok,Mod} = compile:file(Src, [{outdir,Out},report,bin_opt_info,clint]), - ?line Dog = test_server:timetrap(test_server:minutes(10)), - ?line NormalResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]), - ?line test_server:timetrap_cancel(Dog), + ct:timetrap({minutes,10}), + NormalResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]), %% Inlining. - ?line io:format("Compiling with old inliner: ~s\n", [Src]), - ?line {ok,Mod} = compile:file(Src, [{outdir,Out},report,bin_opt_info, + io:format("Compiling with old inliner: ~s\n", [Src]), + {ok,Mod} = compile:file(Src, [{outdir,Out},report,bin_opt_info, {inline,1000},clint]), %% Run inlined code. - ?line Dog3 = test_server:timetrap(test_server:minutes(10)), - ?line OldInlinedResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]), - ?line test_server:timetrap_cancel(Dog3), + ct:timetrap({minutes,10}), + OldInlinedResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]), %% Compare results. - ?line compare(NormalResult, OldInlinedResult), - ?line NormalResult = OldInlinedResult, + compare(NormalResult, OldInlinedResult), + NormalResult = OldInlinedResult, %% Inlining. - ?line io:format("Compiling with new inliner: ~s\n", [Src]), - ?line {ok,Mod} = compile:file(Src, [{outdir,Out},report, + io:format("Compiling with new inliner: ~s\n", [Src]), + {ok,Mod} = compile:file(Src, [{outdir,Out},report, bin_opt_info,inline,clint]), %% Run inlined code. - ?line Dog4 = test_server:timetrap(test_server:minutes(10)), - ?line InlinedResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]), - ?line test_server:timetrap_cancel(Dog4), + ct:timetrap({minutes,10}), + InlinedResult = rpc:call(Node, ?MODULE, load_and_call, [Out,Mod]), %% Compare results. - ?line compare(NormalResult, InlinedResult), - ?line NormalResult = InlinedResult, + compare(NormalResult, InlinedResult), + NormalResult = InlinedResult, %% Delete Beam file. - ?line ok = file:delete(filename:join(Out, atom_to_list(Mod)++code:objfile_extension())), + ok = file:delete(filename:join(Out, atom_to_list(Mod)++code:objfile_extension())), ok. @@ -141,24 +140,24 @@ compare([{X,Y,RGB1}|T1], [{X,Y,RGB2}|T2]) -> compare(T1, T2); compare([H1|_], [H2|_]) -> io:format("Normal = ~p, Inlined = ~p\n", [H1,H2]), - ?t:fail(); + ct:fail(different); compare([], []) -> ok. start_node(Name, Args) -> case test_server:start_node(Name, slave, [{args,Args}]) of {ok,Node} -> {ok, Node}; - Error -> ?line test_server:fail(Error) + Error -> ct:fail(Error) end. load_and_call(Out, Module) -> - ?line io:format("Loading...\n",[]), - ?line code:purge(Module), - ?line LoadRc = code:load_abs(filename:join(Out, Module)), - ?line {module,Module} = LoadRc, - - ?line io:format("Calling...\n",[]), - ?line {Time,CallResult} = timer:tc(Module, Module, []), - ?line io:format("Time: ~p\n", [Time]), + io:format("Loading...\n",[]), + code:purge(Module), + LoadRc = code:load_abs(filename:join(Out, Module)), + {module,Module} = LoadRc, + + io:format("Calling...\n",[]), + {Time,CallResult} = timer:tc(Module, Module, []), + io:format("Time: ~p\n", [Time]), CallResult. %% Macros used by lists/1 below. @@ -194,69 +193,78 @@ load_and_call(Out, Module) -> %% Note: This module must be compiled with the inline_lists_funcs option. lists(Config) when is_list(Config) -> - ?line List = lists:seq(1, 20), + List = lists:seq(1, 20), %% lists:map/2 - ?line ?TestHighOrder_2(map, (fun(E) -> - R = E band 16#ff, - put(?MODULE, [E|get(?MODULE)]), - R - end), List), + ?TestHighOrder_2(map, + (fun(E) -> + R = E band 16#ff, + put(?MODULE, [E|get(?MODULE)]), + R + end), List), %% lists:flatmap/2 - ?line ?TestHighOrder_2(flatmap, (fun(E) -> - R = lists:duplicate(E, E), - put(?MODULE, [E|get(?MODULE)]), - R - end), List), + ?TestHighOrder_2(flatmap, + (fun(E) -> + R = lists:duplicate(E, E), + put(?MODULE, [E|get(?MODULE)]), + R + end), List), %% lists:foreach/2 - ?line ?TestHighOrder_2(foreach, - (fun(E) -> - put(?MODULE, [E bor 7|get(?MODULE)]) - end), List), + ?TestHighOrder_2(foreach, + (fun(E) -> + put(?MODULE, [E bor 7|get(?MODULE)]) + end), List), %% lists:filter/2 - ?line ?TestHighOrder_2(filter, (fun(E) -> - put(?MODULE, [E|get(?MODULE)]), - (E bsr 1) band 1 =/= 0 - end), List), + ?TestHighOrder_2(filter, + (fun(E) -> + put(?MODULE, [E|get(?MODULE)]), + (E bsr 1) band 1 =/= 0 + end), List), %% lists:any/2 - ?line ?TestHighOrder_2(any, (fun(E) -> - put(?MODULE, [E|get(?MODULE)]), - false %Force it to go through all. - end), List), + ?TestHighOrder_2(any, + (fun(E) -> + put(?MODULE, [E|get(?MODULE)]), + false %Force it to go through all. + end), List), %% lists:all/2 - ?line ?TestHighOrder_2(all, (fun(E) -> - put(?MODULE, [E|get(?MODULE)]), - true %Force it to go through all. - end), List), + ?TestHighOrder_2(all, + (fun(E) -> + put(?MODULE, [E|get(?MODULE)]), + true %Force it to go through all. + end), List), %% lists:foldl/3 - ?line ?TestHighOrder_3(foldl, (fun(E, A) -> - put(?MODULE, [E|get(?MODULE)]), - A bxor E - end), 0, List), + ?TestHighOrder_3(foldl, + (fun(E, A) -> + put(?MODULE, [E|get(?MODULE)]), + A bxor E + end), 0, List), %% lists:foldr/3 - ?line ?TestHighOrder_3(foldr, (fun(E, A) -> - put(?MODULE, [E|get(?MODULE)]), - A bxor (bnot E) - end), 0, List), + ?TestHighOrder_3(foldr, + (fun(E, A) -> + put(?MODULE, [E|get(?MODULE)]), + A bxor (bnot E) + end), 0, List), %% lists:mapfoldl/3 - ?line ?TestHighOrder_3(mapfoldl, (fun(E, A) -> - put(?MODULE, [E|get(?MODULE)]), - {bnot E,A bxor (bnot E)} - end), 0, List), + ?TestHighOrder_3(mapfoldl, + (fun(E, A) -> + put(?MODULE, [E|get(?MODULE)]), + {bnot E,A bxor (bnot E)} + end), 0, List), %% lists:mapfoldr/3 - ?line ?TestHighOrder_3(mapfoldr, (fun(E, A) -> - put(?MODULE, [E|get(?MODULE)]), - {bnot E,A bxor (bnot E)} - end), 0, List), + ?TestHighOrder_3(mapfoldr, + (fun(E, A) -> + put(?MODULE, [E|get(?MODULE)]), + {bnot E,A bxor (bnot E)} + end), 0, List), %% Cleanup. erase(?MODULE), @@ -329,7 +337,7 @@ badarg(Reply, _A) -> Reply. otp_7223(Config) when is_list(Config) -> - ?line {'EXIT', {{case_clause,{1}},_}} = (catch otp_7223_1(1)), + {'EXIT', {{case_clause,{1}},_}} = (catch otp_7223_1(1)), ok. -compile({inline,[{otp_7223_1,1}]}). @@ -342,7 +350,7 @@ otp_7223_2({a}) -> coverage(Config) when is_list(Config) -> Mod = bsdecode, - Src = filename:join(?config(data_dir, Config), Mod), + Src = filename:join(proplists:get_value(data_dir, Config), Mod), {ok,Mod,_} = compile:file(Src, [binary,report,{inline,0},clint]), {ok,Mod,_} = compile:file(Src, [binary,report,{inline,20}, verbose,clint]), diff --git a/lib/compiler/test/inline_SUITE_data/attribute.erl b/lib/compiler/test/inline_SUITE_data/attribute.erl index 961086a888..4bd9d65032 100644 --- a/lib/compiler/test/inline_SUITE_data/attribute.erl +++ b/lib/compiler/test/inline_SUITE_data/attribute.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/inline_SUITE_data/bsdecode.erl b/lib/compiler/test/inline_SUITE_data/bsdecode.erl index ae134ad02e..f6419b33d1 100644 --- a/lib/compiler/test/inline_SUITE_data/bsdecode.erl +++ b/lib/compiler/test/inline_SUITE_data/bsdecode.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/inline_SUITE_data/bsdes.erl b/lib/compiler/test/inline_SUITE_data/bsdes.erl index 8d2d1a8287..43d941f144 100644 --- a/lib/compiler/test/inline_SUITE_data/bsdes.erl +++ b/lib/compiler/test/inline_SUITE_data/bsdes.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/inline_SUITE_data/decode1.erl b/lib/compiler/test/inline_SUITE_data/decode1.erl index 9b4fc071a3..f390a32061 100644 --- a/lib/compiler/test/inline_SUITE_data/decode1.erl +++ b/lib/compiler/test/inline_SUITE_data/decode1.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/inline_SUITE_data/fname.erl b/lib/compiler/test/inline_SUITE_data/fname.erl index 7ad4446bf3..5df8ee6b8c 100644 --- a/lib/compiler/test/inline_SUITE_data/fname.erl +++ b/lib/compiler/test/inline_SUITE_data/fname.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/inline_SUITE_data/itracer.erl b/lib/compiler/test/inline_SUITE_data/itracer.erl index 93f24e9bb1..a6df21f233 100644 --- a/lib/compiler/test/inline_SUITE_data/itracer.erl +++ b/lib/compiler/test/inline_SUITE_data/itracer.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl index d9762e2647..776a1a5950 100644 --- a/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl +++ b/lib/compiler/test/inline_SUITE_data/maps_inline_test.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl index 398398a397..3cb49433ce 100644 --- a/lib/compiler/test/lc_SUITE.erl +++ b/lib/compiler/test/lc_SUITE.erl @@ -1,40 +1,51 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% -module(lc_SUITE). --author('[email protected]'). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, basic/1,deeply_nested/1,no_generator/1, - empty_generator/1,no_export/1]). + empty_generator/1,no_export/1,shadow/1, + effect/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> test_lib:recompile(?MODULE), - [basic, deeply_nested, no_generator, empty_generator, no_export]. + [{group,p}]. groups() -> - []. + [{p,test_lib:parallel(), + [basic, + deeply_nested, + no_generator, + empty_generator, + no_export, + shadow, + effect + ]}]. init_per_suite(Config) -> Config. @@ -50,43 +61,52 @@ end_per_group(_GroupName, Config) -> init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = test_server:timetrap(?t:minutes(1)), - [{watchdog,Dog}|Config]. + Config. end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. basic(Config) when is_list(Config) -> - ?line L0 = lists:seq(1, 10), - ?line L1 = my_map(fun(X) -> {x,X} end, L0), - ?line L1 = [{x,X} || X <- L0], - ?line L0 = my_map(fun({x,X}) -> X end, L1), - ?line [1,2,3,4,5] = [X || X <- L0, X < 6], - ?line [4,5,6] = [X || X <- L0, X > 3, X < 7], - ?line [] = [X || X <- L0, X > 32, X < 7], - ?line [1,3,5,7,9] = [X || X <- L0, odd(X)], - ?line [2,4,6,8,10] = [X || X <- L0, not odd(X)], - ?line [1,3,5,9] = [X || X <- L0, odd(X), X =/= 7], - ?line [2,4,8,10] = [X || X <- L0, not odd(X), X =/= 6], + L0 = lists:seq(1, 10), + L1 = my_map(fun(X) -> {x,X} end, L0), + L1 = [{x,X} || X <- L0], + L0 = my_map(fun({x,X}) -> X end, L1), + [1,2,3,4,5] = [X || X <- L0, X < 6], + [4,5,6] = [X || X <- L0, X > 3, X < 7], + [] = [X || X <- L0, X > 32, X < 7], + [1,3,5,7,9] = [X || X <- L0, odd(X)], + [2,4,6,8,10] = [X || X <- L0, not odd(X)], + [1,3,5,9] = [X || X <- L0, odd(X), X =/= 7], + [2,4,8,10] = [X || X <- L0, not odd(X), X =/= 6], %% Append is specially handled. - ?line [1,3,5,9,2,4,8,10] = [X || X <- L0, odd(X), X =/= 7] ++ + [1,3,5,9,2,4,8,10] = [X || X <- L0, odd(X), X =/= 7] ++ [X || X <- L0, not odd(X), X =/= 6], %% Guards BIFs are evaluated in guard context. Weird, but true. - ?line [{a,b,true},{x,y,true,true}] = [X || X <- tuple_list(), element(3, X)], + [{a,b,true},{x,y,true,true}] = [X || X <- tuple_list(), element(3, X)], %% Filter expressions with andalso/orelse. - ?line "abc123" = alphanum("?abc123.;"), + "abc123" = alphanum("?abc123.;"), + + %% Aliased patterns. + [] = [t || {C=D}={_,_} <- []], + [] = [X || {X,{Y}={X,X}} <- []], + [t] = [t || "a"++"b" = "ab" <- ["ab"]], + + %% Strange filter block. + [] = [{X,Y} || {X} <- [], begin Y = X, Y =:= X end], + [{a,a}] = [{X,Y} || {X} <- [{a}], begin Y = X, Y =:= X end], + + %% Not matching. + [] = [3 || {3=4} <- []], %% Error cases. - ?line [] = [{xx,X} || X <- L0, element(2, X) == no_no_no], - ?line {'EXIT',_} = (catch [X || X <- L1, list_to_atom(X) == dum]), - ?line [] = [X || X <- L1, X+1 < 2], - ?line {'EXIT',_} = (catch [X || X <- L1, odd(X)]), - ?line fc([x], catch [E || E <- id(x)]), + [] = [{xx,X} || X <- L0, element(2, X) == no_no_no], + {'EXIT',_} = (catch [X || X <- L1, list_to_atom(X) == dum]), + [] = [X || X <- L1, X+1 < 2], + {'EXIT',_} = (catch [X || X <- L1, odd(X)]), + fc([x], catch [E || E <- id(x)]), ok. tuple_list() -> @@ -116,12 +136,12 @@ deeply_nested_1() -> X16 <- [4],X17 <- [3],X18 <- [fun() -> X16+X17 end],X19 <- [2],X20 <- [1]]. no_generator(Config) when is_list(Config) -> - ?line Seq = lists:seq(-10, 17), - ?line [no_gen_verify(no_gen(A, B), A, B) || A <- Seq, B <- Seq], + Seq = lists:seq(-10, 17), + [no_gen_verify(no_gen(A, B), A, B) || A <- Seq, B <- Seq], %% Literal expression, for coverage. - ?line [a] = [a || true], - ?line [a,b,c] = [a || true] ++ [b,c], + [a] = [a || true], + [a,b,c] = [a || true] ++ [b,c], ok. no_gen(A, B) -> @@ -162,7 +182,7 @@ no_gen_verify(Res, A, B) -> ShouldBe -> ok; _ -> io:format("A = ~p; B = ~p; Expected = ~p, actual = ~p", [A,B,ShouldBe,Res]), - ?t:fail() + ct:fail(failed) end. no_gen_eval(Fun, Res) -> @@ -174,13 +194,51 @@ no_gen_eval(Fun, Res) -> no_gen_one_more(A, B) -> A + 1 =:= B. empty_generator(Config) when is_list(Config) -> - ?line [] = [X || {X} <- [], (false or (X/0 > 3))], + [] = [X || {X} <- [], (false or (X/0 > 3))], ok. no_export(Config) when is_list(Config) -> [] = [ _X = a || false ] ++ [ _X = a || false ], ok. +%% Test that variables in list comprehensions are +%% correctly shadowed. + +shadow(Config) when is_list(Config) -> + Shadowed = nomatch, + _ = id(Shadowed), %Eliminate warning. + L = [{Shadowed,Shadowed+1} || Shadowed <- lists:seq(7, 9)], + [{7,8},{8,9},{9,10}] = id(L), + [8,9] = id([Shadowed || {_,Shadowed} <- id(L), + Shadowed < 10]), + ok. + +effect(Config) when is_list(Config) -> + [{42,{a,b,c}}] = + do_effect(fun(F, L) -> + [F({V1,V2}) || + #{<<1:500>>:=V1,<<2:301>>:=V2} <- L], + ok + end, id([#{},x,#{<<1:500>>=>42,<<2:301>>=>{a,b,c}}])), + + %% Will trigger the time-trap timeout if not tail-recursive. + case ?MODULE of + lc_SUITE -> + _ = [{'EXIT',{badarg,_}} = + (catch binary_to_atom(<<C/utf8>>, utf8)) || + C <- lists:seq(16#10000, 16#FFFFF)]; + _ -> + ok + end, + + ok. + +do_effect(Lc, L) -> + put(?MODULE, []), + F = fun(V) -> put(?MODULE, [V|get(?MODULE)]) end, + ok = Lc(F, L), + lists:reverse(erase(?MODULE)). + id(I) -> I. fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Args,_}|_]}}) -> ok; diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index 403b7e8405..36e82c1459 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -2,16 +2,17 @@ %% %% Copyright Ericsson AB 2013. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -21,16 +22,32 @@ ]). -export([ - t_build_and_match_literals/1, - t_update_literals/1,t_match_and_update_literals/1, + %% literals + t_build_and_match_literals/1, t_build_and_match_literals_large/1, + t_update_literals/1, t_update_literals_large/1, + t_match_and_update_literals/1, t_match_and_update_literals_large/1, t_update_map_expressions/1, - t_update_assoc/1,t_update_exact/1, - t_guard_bifs/1, t_guard_sequence/1, t_guard_update/1, - t_guard_receive/1, t_guard_fun/1, + t_update_assoc/1, t_update_assoc_large/1, + t_update_exact/1, t_update_exact_large/1, + t_guard_bifs/1, + t_guard_sequence/1, t_guard_sequence_large/1, + t_guard_update/1, t_guard_update_large/1, + t_guard_receive/1, t_guard_receive_large/1, + t_guard_fun/1, t_list_comprehension/1, t_map_sort_literals/1, t_map_size/1, t_build_and_match_aliasing/1, + t_is_map/1, + + %% variables + t_build_and_match_variables/1, + t_update_assoc_variables/1,t_update_exact_variables/1, + t_nested_pattern_expressions/1, + t_guard_update_variables/1, + t_guard_sequence_variables/1, + t_guard_sequence_mixed/1, + t_frequency_table/1, %% warnings t_warn_useless_build/1, @@ -46,21 +63,44 @@ %% errors in 17.0-rc1 t_update_values/1, t_expand_map_update/1, - t_export/1 + t_export/1, + + %% errors in 18 + t_register_corruption/1, + t_bad_update/1 + ]). suite() -> []. -all() -> [ - t_build_and_match_literals, - t_update_literals, t_match_and_update_literals, +all() -> + test_lib:recompile(?MODULE), + [ + %% literals + t_build_and_match_literals, t_build_and_match_literals_large, + t_update_literals, t_update_literals_large, + t_match_and_update_literals, t_match_and_update_literals_large, t_update_map_expressions, - t_update_assoc,t_update_exact, - t_guard_bifs, t_guard_sequence, t_guard_update, - t_guard_receive,t_guard_fun, t_list_comprehension, + t_update_assoc, t_update_assoc_large, + t_update_exact, t_update_exact_large, + t_guard_bifs, + t_guard_sequence, t_guard_sequence_large, + t_guard_update, t_guard_update_large, + t_guard_receive, t_guard_receive_large, + t_guard_fun, t_list_comprehension, t_map_sort_literals, t_map_size, t_build_and_match_aliasing, + t_is_map, + + %% variables + t_build_and_match_variables, + t_update_assoc_variables,t_update_exact_variables, + t_nested_pattern_expressions, + t_guard_update_variables, + t_guard_sequence_variables, + t_guard_sequence_mixed, + t_frequency_table, %% warnings t_warn_useless_build, @@ -76,7 +116,11 @@ all() -> [ %% errors in 17.0-rc1 t_update_values, t_expand_map_update, - t_export + t_export, + + %% errors in 18 + t_register_corruption, + t_bad_update ]. groups() -> []. @@ -119,6 +163,11 @@ t_build_and_match_literals(Config) when is_list(Config) -> %% nil key #{[]:=ok,1:=2} = id(#{[]=>ok,1=>2}), + #{1:=2,[]:=ok,1:=2} = id(#{[]=>ok,1=>2}), + + %% pseudo literals + #{ -3 := yep } = id(#{ -3 => yep }), + #{ <<0:358>> := "three" } = id(#{<<0:358>> =>"three"}), %% error case {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))), @@ -126,10 +175,465 @@ t_build_and_match_literals(Config) when is_list(Config) -> {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))), {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))), - {'EXIT',{badarg,_}} = (catch id(#{<<0:258>> =>"three"})), {'EXIT',{{badmatch,_},_}} = (catch (#{#{"a"=>42} := 3}=id(#{#{"a"=>3}=>42}))), ok. +t_build_and_match_literals_large(Config) when is_list(Config) -> + % normal non-repeating + M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" }), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M0, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M0, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M0, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M0, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M0, + + 60 = map_size(M0), + 60 = maps:size(M0), + + % with repeating + M1 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 10=>na0,20=>nb0,30=>"nc0","40"=>"nd0",<<"50">>=>"ne0",{["00"]}=>"n10", + 11=>na1,21=>nb1,31=>"nc1","41"=>"nd1",<<"51">>=>"ne1",{["01"]}=>"n11", + 12=>na2,22=>nb2,32=>"nc2","42"=>"nd2",<<"52">>=>"ne2",{["02"]}=>"n12", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + + 13=>na3,23=>nb3,33=>"nc3","43"=>"nd3",<<"53">>=>"ne3",{["03"]}=>"n13", + 14=>na4,24=>nb4,34=>"nc4","44"=>"nd4",<<"54">>=>"ne4",{["04"]}=>"n14", + + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" }), + + #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1, + #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1, + #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1, + #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1, + #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1, + + 60 = map_size(M1), + 60 = maps:size(M1), + + % with floats + + M2 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9"}), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2, + + #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2, + #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2, + #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2, + #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2, + #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2, + + #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2, + #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2, + #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2, + #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2, + #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2, + + 90 = map_size(M2), + 90 = maps:size(M2), + + % with bignums + M3 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + 36893488147419103232=>big1, 73786976294838206464=>big2, + 147573952589676412928=>big3, 18446744073709551616=>big4, + 4294967296=>big5, 8589934592=>big6, + 4294967295=>big7, 67108863=>big8 + }), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, + + #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3, + #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3, + #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3, + #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3, + #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3, + + #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, + + #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + + 98 = map_size(M3), + 98 = maps:size(M3), + + %% with maps + + M4 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M4, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M4, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M4, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M4, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M4, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M4, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M4, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M4, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M4, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M4, + + #{ #{ one => small, map => key } := "small map key 1", + #{ second => small, map => key } := "small map key 2", + #{ third => small, map => key } := "small map key 3" } = M4, + + #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M4, + + + #{ 15:=V1,25:=b5,35:=V2,"45":="d5",<<"55">>:=V3,{["05"]}:="15", + #{ one => small, map => key } := "small map key 1", + #{ second => small, map => key } := V4, + #{ third => small, map => key } := "small map key 3", + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := V5 } = M4, + + a5 = V1, + "c5" = V2, + "e5" = V3, + "small map key 2" = V4, + "large map key 1" = V5, + + 95 = map_size(M4), + 95 = maps:size(M4), + + % call for value + + M5 = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", + 11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11", + 12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12", + 13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13", + 14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14", + + 15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15", + 16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16", + 17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17", + 18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18", + 19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19", + + 10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"), + 11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"), + 12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"), + 13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"), + 14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"), + + 15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"), + 16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"), + 17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"), + 18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"), + 19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"), + + #{ one => small, map => key } => id("small map key 1"), + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }), + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M5, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M5, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M5, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M5, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M5, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M5, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M5, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M5, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M5, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M5, + + #{ #{ one => small, map => key } := "small map key 1", + #{ second => small, map => key } := "small map key 2", + #{ third => small, map => key } := "small map key 3" } = M5, + + #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M5, + + 95 = map_size(M5), + 95 = maps:size(M5), + + %% remember + + #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0, + #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0, + #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0, + #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0, + #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0, + + #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1, + #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1, + #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1, + #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1, + #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2, + + #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2, + #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2, + #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2, + #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2, + #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2, + + #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, + + #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3, + #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3, + #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3, + #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3, + #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3, + + #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3, + + #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3, + #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3, + #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3, + #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3, + + ok. + + t_build_and_match_aliasing(Config) when is_list(Config) -> M1 = id(#{a=>1,b=>2,c=>3,d=>4}), #{c:=C1=_=_=C2} = M1, @@ -143,6 +647,19 @@ t_build_and_match_aliasing(Config) when is_list(Config) -> M2 = id(#{"a"=>1,"b"=>2,"c"=>3,"d"=>4}), #{"a":=A2,"a":=A2,"a":=A2,"b":=B2,"b":=B2,"b":=2} = M2, #{"a":=_,"a":=_,"a":=_,"b":=_,"b":=_,"b":=2} = M2, + + #{a:=A1,a:=A1,a:=A1,b:=B1,b:=B1} = #{a:=A1,a:=A1,a:=A1,b:=B1,b:=B1,b:=2} = M1, + #{"a":=A3,"b":=B3} = #{"a":=A3,"a":=A3} = #{"b":=B3,"b":=2} = M2, + + #{"a":=1,"b":=2,"c":=3,"d":=4} = #{"a":=A4,"b":=B4} = #{"a":=A4,"a":=A4} = #{"b":=B4,"d":=4} = M2, + #{"a":=A5,"b":=B5} = #{"a":=A5,"a":=A5} = #{"b":=B5,"d":=4} = #{"a":=1,"b":=2,"c":=3,"d":=4} = M2, + #{"a":=_,"b":=_} = #{"a":=_,"a":=_} = #{"b":=_,"d":=4} = #{"a":=1,"b":=2,"c":=3,"d":=4} = M2, + + M3 = id(#{<<12:300>>=>1,<<13:300>>=>2}), + #{<<12:300>> := V1, <<13:300>> := V2} = #{<<13:300>> := V2, <<12:300>> := V1} = M3, + #{<<12:300>> := 1, <<13:300>> := 2} = #{<<13:300>> := _, <<12:300>> := _} = M3, + #{<<13:300>> := _, <<12:300>> := _} = #{<<12:300>> := 1, <<13:300>> := 2} = M3, + ok. t_map_size(Config) when is_list(Config) -> @@ -161,14 +678,25 @@ t_map_size(Config) when is_list(Config) -> false = map_is_size(M#{ "c" => 2}, 2), %% Error cases. - {'EXIT',{badarg,_}} = (catch map_size([])), - {'EXIT',{badarg,_}} = (catch map_size(<<1,2,3>>)), - {'EXIT',{badarg,_}} = (catch map_size(1)), + {'EXIT',{{badmap,[]},_}} = (catch map_size([])), + {'EXIT',{{badmap,<<1,2,3>>},_}} = (catch map_size(<<1,2,3>>)), + {'EXIT',{{badmap,1},_}} = (catch map_size(1)), ok. map_is_size(M,N) when map_size(M) =:= N -> true; map_is_size(_,_) -> false. +t_is_map(Config) when is_list(Config) -> + true = is_map(#{}), + true = is_map(#{a=>1}), + false = is_map({a,b}), + false = is_map(x), + if is_map(#{}) -> ok end, + if is_map(#{b=>1}) -> ok end, + if not is_map([1,2,3]) -> ok end, + if not is_map(x) -> ok end, + ok. + % test map updates without matching t_update_literals(Config) when is_list(Config) -> Map = #{x=>1,y=>2,z=>3,q=>4}, @@ -177,13 +705,75 @@ t_update_literals(Config) when is_list(Config) -> ]), ok. +t_update_literals_large(Config) when is_list(Config) -> + Map = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", + 11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11", + 12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12", + 13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13", + 14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14", + + 15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15", + 16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16", + 17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17", + 18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18", + 19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19", + + 10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"), + 11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"), + 12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"), + 13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"), + 14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"), + + 15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"), + 16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"), + 17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"), + 18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"), + 19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"), + + #{ one => small, map => key } => id("small map key 1"), + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }), + + #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [ + {"a","1"},{"b","2"},{"c","3"},{"d","4"} + ]), + ok. + + + loop_update_literals_x_q(Map, []) -> Map; loop_update_literals_x_q(Map, [{X,Q}|Vs]) -> loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs). % test map updates with matching t_match_and_update_literals(Config) when is_list(Config) -> - Map = #{x=>0,y=>"untouched",z=>"also untouched",q=>1}, + Map = #{ x=>0,y=>"untouched",z=>"also untouched",q=>1, + #{ "one" => small, map => key } => "small map key 1" }, + #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [ {1,2},{3,4},{5,6},{7,8} ]), @@ -197,8 +787,77 @@ t_match_and_update_literals(Config) when is_list(Config) -> #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number }, ok. +t_match_and_update_literals_large(Config) when is_list(Config) -> + Map = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10", + 11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11", + 12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12", + 13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13", + 14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14", + + 15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15", + 16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16", + 17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17", + 18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18", + 19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19", + + 10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"), + 11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"), + 12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"), + 13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"), + 14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"), + + 15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"), + 16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"), + 17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"), + 18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"), + 19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"), + + x=>0,y=>"untouched",z=>"also untouched",q=>1, + + #{ "one" => small, map => key } => id("small map key 1"), + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }), + + #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [ + {1,2},{3,4},{5,6},{7,8} + ]), + M0 = id(Map#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}), + M1 = id(Map#{}), + M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, + 4 => number, 18446744073709551629 => wat}, + M0 = M2, + + #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number }, + ok. + loop_match_and_update_literals_x_q(Map, []) -> Map; -loop_match_and_update_literals_x_q(#{q:=Q0,x:=X0} = Map, [{X,Q}|Vs]) -> +loop_match_and_update_literals_x_q(#{ q:=Q0, x:=X0, + #{ "one" => small, map => key } := "small map key 1" } = Map, [{X,Q}|Vs]) -> loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs). @@ -224,9 +883,12 @@ t_update_map_expressions(Config) when is_list(Config) -> #{ "a" := b } = F(), - %% Error cases, FIXME: should be 'badmap'? - {'EXIT',{badarg,_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), - {'EXIT',{badarg,_}} = (catch (id([]))#{ a := 42, b => 2 }), + %% Error cases. + {'EXIT',{{badmap,<<>>},_}} = (catch (id(<<>>))#{ a := 42, b => 2 }), + {'EXIT',{{badmap,[]},_}} = (catch (id([]))#{ a := 42, b => 2 }), + {'EXIT',{{badmap,_},_}} = + (catch (fun t_update_map_expressions/1)#{u => 42}), + ok. @@ -241,13 +903,92 @@ t_update_assoc(Config) when is_list(Config) -> #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2, M2 = M0#{3.0:=wrong,3.0=>new}, + % Can't handle directly yet + Bin = <<0:257>>, + #{ Bin := val } = id(M0#{<<0:257>> => val}), %% binary limitation + %% Errors cases. BadMap = id(badmap), - {'EXIT',{badarg,_}} = (catch BadMap#{nonexisting=>val}), - {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting=>val}), - {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> => val}), %% limitation + {'EXIT',{{badmap,BadMap},_}} = (catch BadMap#{nonexisting=>val}), + {'EXIT',{{badmap,<<>>},_}} = (catch <<>>#{nonexisting=>val}), + + %% Evaluation order. + {'EXIT',{blurf,_}} = + (catch BadMap#{whatever=>id(error(blurf))}), + {'EXIT',{blurf,_}} = + (catch BadMap#{id(error(blurf))=>whatever}), ok. +t_update_assoc_large(Config) when is_list(Config) -> + M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + + M1 = M0#{1=>42,2=>100,4=>[a,b,c]}, + #{1:=42,2:=100,10.0:=fa0,4:=[a,b,c],25:=b5} = M1, + #{ 10:=43, 24:=b4, 15:=a5, 35:="c5", 2.0:=100, 13.0:=fa3, 4.0:=[a,b,c]} = + M0#{1.0=>float,10:=43,2.0=>wrong,2.0=>100,4.0=>[a,b,c]}, + + M2 = M0#{13.0=>new}, + #{10:=a0,20:=b0,13.0:=new,"40":="d0",<<"50">>:="e0"} = M2, + M2 = M0#{13.0:=wrong,13.0=>new}, + + %% Errors cases. + BadMap = id({no,map}), + {'EXIT',{{badmap,BadMap},_}} = (catch BadMap#{nonexisting=>M0}), + ok. + + + t_update_exact(Config) when is_list(Config) -> M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), @@ -268,21 +1009,121 @@ t_update_exact(Config) when is_list(Config) -> 1.0 => new_val4 }, %% Errors cases. - {'EXIT',{badarg,_}} = (catch ((id(nil))#{ a := b })), - {'EXIT',{badarg,_}} = (catch M0#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{1.0:=v,1.0=>v2}), - {'EXIT',{badarg,_}} = (catch M0#{42.0:=v,42:=v2}), - {'EXIT',{badarg,_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), - {'EXIT',{badarg,_}} = (catch <<>>#{nonexisting:=val}), - {'EXIT',{badarg,_}} = (catch M0#{<<0:257>> := val}), %% limitation + {'EXIT',{{badmap,nil},_}} = (catch ((id(nil))#{ a := b })), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{{badmap,<<>>},_}} = (catch <<>>#{nonexisting:=val}), + {'EXIT',{{badkey,<<0:257>>},_}} = + (catch M0#{<<0:257>> := val}), %limitation + + %% A workaround for a bug allowed an empty map to be updated. + {'EXIT',{{badkey,a},_}} = (catch (id(#{}))#{a:=1}), + {'EXIT',{{badkey,a},_}} = (catch #{}#{a:=1}), + Empty = #{}, + {'EXIT',{{badkey,a},_}} = (catch Empty#{a:=1}), + + %% Evaluation order. + BadMap = id([no,map]), + {'EXIT',{blurf,_}} = + (catch BadMap#{whatever:=id(error(blurf))}), + {'EXIT',{blurf,_}} = + (catch BadMap#{id(error(blurf)):=whatever}), + {'EXIT',{{badmap,BadMap},_}} = + (catch BadMap#{id(nonexisting):=whatever}), + ok. + +t_update_exact_large(Config) when is_list(Config) -> + M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + + M1 = M0#{10:=42,<<"55">>:=100,10.0:=[a,b,c]}, + #{ 10:=42,<<"55">>:=100,{["05"]}:="15",10.0:=[a,b,c], + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1" } = M1, + + M1 = M0#{10:=wrong,10=>42,<<"55">>=>wrong,<<"55">>:=100,10.0:=[a,b,c]}, + + M2 = M0#{13.0:=new}, + #{10:=a0,20:=b0,13.0:=new} = M2, + M2 = M0#{13.0=>wrong,13.0:=new}, + + %% Errors cases. + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + ok. t_update_values(Config) when is_list(Config) -> V0 = id(1337), M0 = #{ a => 1, val => V0}, V1 = get_val(M0), - M1 = M0#{ val := [V0,V1], "wazzup" => 42 }, + M1 = id(M0#{ val := [V0,V1], "wazzup" => 42 }), [1337, {some_val, 1337}] = get_val(M1), + M2 = id(M1#{ <<42:333>> => 1337 }), + {bin_key,1337} = get_val(M2), N = 110, List = [{[I,1,2,3,I],{1,2,3,"wat",I}}|| I <- lists:seq(1,N)], @@ -308,6 +1149,7 @@ t_export(Config) when is_list(Config) -> check_val(#{val1:=V1, val2:=V2},V1,V2) -> ok. +get_val(#{ <<42:333>> := V }) -> {bin_key, V}; get_val(#{ "wazzup" := _, val := V}) -> V; get_val(#{ val := V }) -> {some_val, V}. @@ -358,6 +1200,75 @@ t_guard_sequence(Config) when is_list(Config) -> {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})), ok. +t_guard_sequence_large(Config) when is_list(Config) -> + M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19", + + 10.0=>fa0,20.0=>fb0,30.0=>"fc0", + 11.0=>fa1,21.0=>fb1,31.0=>"fc1", + 12.0=>fa2,22.0=>fb2,32.0=>"fc2", + 13.0=>fa3,23.0=>fb3,33.0=>"fc3", + 14.0=>fa4,24.0=>fb4,34.0=>"fc4", + + 15.0=>fa5,25.0=>fb5,35.0=>"fc5", + 16.0=>fa6,26.0=>fb6,36.0=>"fc6", + 17.0=>fa7,27.0=>fb7,37.0=>"fc7", + 18.0=>fa8,28.0=>fb8,38.0=>"fc8", + 19.0=>fa9,29.0=>fb9,39.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + {1, "a"} = map_guard_sequence_1(M0#{seq=>1,val=>id("a")}), + {2, "b"} = map_guard_sequence_1(M0#{seq=>2,val=>id("b")}), + {3, "c"} = map_guard_sequence_1(M0#{seq=>3,val=>id("c")}), + {4, "d"} = map_guard_sequence_1(M0#{seq=>4,val=>id("d")}), + {5, "e"} = map_guard_sequence_1(M0#{seq=>5,val=>id("e")}), + + {1,M1} = map_guard_sequence_2(M1 = id(M0#{a=>3})), + {2,M2} = map_guard_sequence_2(M2 = id(M0#{a=>4, b=>4})), + {3,gg,M3} = map_guard_sequence_2(M3 = id(M0#{a=>gg, b=>4})), + {4,sc,sc,M4} = map_guard_sequence_2(M4 = id(M0#{a=>sc, b=>3, c=>sc2})), + {5,kk,kk,M5} = map_guard_sequence_2(M5 = id(M0#{a=>kk, b=>other, c=>sc2})), + + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(M0#{seq=>6,val=>id("e")})), + {'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(M0#{b=>5})), + ok. + map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val}; map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val}; map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val}; @@ -376,6 +1287,66 @@ t_guard_update(Config) when is_list(Config) -> first = map_guard_update(#{}, #{x=>first}), second = map_guard_update(#{y=>old}, #{x=>second,y=>old}), third = map_guard_update(#{x=>old,y=>old}, #{x=>third,y=>old}), + bad_map_guard_update(), + ok. + +t_guard_update_large(Config) when is_list(Config) -> + M0 = id(#{ 70=>a0,80=>b0,90=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10", + 71=>a1,81=>b1,91=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11", + 72=>a2,82=>b2,92=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12", + 73=>a3,83=>b3,93=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13", + 74=>a4,84=>b4,94=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14", + + 75=>a5,85=>b5,95=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15", + 76=>a6,86=>b6,96=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16", + 77=>a7,87=>b7,97=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17", + 78=>a8,88=>b8,98=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18", + 79=>a9,89=>b9,99=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19", + + 70.0=>fa0,80.0=>fb0,90.0=>"fc0", + 71.0=>fa1,81.0=>fb1,91.0=>"fc1", + 72.0=>fa2,82.0=>fb2,92.0=>"fc2", + 73.0=>fa3,83.0=>fb3,93.0=>"fc3", + 74.0=>fa4,84.0=>fb4,94.0=>"fc4", + + 75.0=>fa5,85.0=>fb5,95.0=>"fc5", + 76.0=>fa6,86.0=>fb6,96.0=>"fc6", + 77.0=>fa7,87.0=>fb7,97.0=>"fc7", + 78.0=>fa8,88.0=>fb8,98.0=>"fc8", + 79.0=>fa9,89.0=>fb9,99.0=>"fc9", + + #{ one => small, map => key } => "small map key 1", + #{ second => small, map => key } => "small map key 2", + #{ third => small, map => key } => "small map key 3", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + 16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1", + + #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10", + 11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11", + 12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12", + 13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13", + 14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14", + + 15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15", + k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16", + 17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17", + 18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18", + 19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }), + + + error = map_guard_update(M0#{},M0#{}), + first = map_guard_update(M0#{},M0#{x=>first}), + second = map_guard_update(M0#{y=>old}, M0#{x=>second,y=>old}), ok. map_guard_update(M1, M2) when M1#{x=>first} =:= M2 -> first; @@ -383,6 +1354,29 @@ map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second; map_guard_update(M1, M2) when M1#{x:=third} =:= M2 -> third; map_guard_update(_, _) -> error. +bad_map_guard_update() -> + do_bad_map_guard_update(fun burns/1), + do_bad_map_guard_update(fun turns/1), + ok. + +do_bad_map_guard_update(Fun) -> + do_bad_map_guard_update_1(Fun, #{}), + do_bad_map_guard_update_1(Fun, #{true=>1}), + ok. + +do_bad_map_guard_update_1(Fun, Value) -> + %% Note: The business with the seemingly redundant fun + %% disables inlining, which would otherwise change the + %% EXIT reason. + {'EXIT',{function_clause,_}} = (catch Fun(Value)), + ok. + +burns(Richmond) when not (Richmond#{true := 0}); [Richmond] -> + specification. + +turns(Richmond) when not (Richmond#{true => 0}); [Richmond] -> + specification. + t_guard_receive(Config) when is_list(Config) -> M0 = #{ id => 0 }, Pid = spawn_link(fun() -> guard_receive_loop() end), @@ -408,6 +1402,43 @@ t_guard_receive(Config) when is_list(Config) -> done = call(Pid, done), ok. +-define(t_guard_receive_large_procs, 1500). + +t_guard_receive_large(Config) when is_list(Config) -> + M = lists:foldl(fun(_,#{procs := Ps } = M) -> + M#{ procs := Ps#{ spawn_link(fun() -> grecv_loop() end) => 0 }} + end, #{procs => #{}, done => 0}, lists:seq(1,?t_guard_receive_large_procs)), + lists:foreach(fun(Pid) -> + Pid ! {self(), hello} + end, maps:keys(maps:get(procs,M))), + ok = guard_receive_large_loop(M), + ok. + +guard_receive_large_loop(#{done := ?t_guard_receive_large_procs}) -> + ok; +guard_receive_large_loop(M) -> + receive + #{pid := Pid, msg := hello} -> + case M of + #{done := Count, procs := #{Pid := 150}} -> + Pid ! {self(), done}, + guard_receive_large_loop(M#{done := Count + 1}); + #{procs := #{Pid := Count} = Ps} -> + Pid ! {self(), hello}, + guard_receive_large_loop(M#{procs := Ps#{Pid := Count + 1}}) + end + end. + +grecv_loop() -> + receive + {_, done} -> + ok; + {Pid, hello} -> + Pid ! #{pid=>self(), msg=>hello}, + grecv_loop() + end. + + call(Pid, M) -> Pid ! {self(), M}, receive {Pid, Res} -> Res end. @@ -437,7 +1468,15 @@ guard_receive_loop() -> t_list_comprehension(Config) when is_list(Config) -> - [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]], + [#{k:=1},#{k:=2},#{k:=3}] = id([#{k=>I} || I <- [1,2,3]]), + Ls = id([#{<<2:301>> => I, "wat" => I + 1} || I <- [1,2,3]]), + [#{<<2:301>>:=1,"wat":=2},#{<<2:301>>:=2,"wat":=3},#{<<2:301>>:=3,"wat":=4}] = Ls, + [{1,2},{2,3},{3,4}] = id([{I2,I1} || #{"wat" := I1, <<2:301>> := I2} <- Ls]), + + Ks = lists:seq($a,$z), + Ms = [#{[K1,K2]=>{K1,K2}} || K1 <- Ks, K2 <- Ks], + [#{"aa" := {$a,$a}},#{"ab":={$a,$b}}|_] = Ms, + [#{"zz" := {$z,$z}},#{"zy":={$z,$y}}|_] = lists:reverse(Ms), ok. t_guard_fun(Config) when is_list(Config) -> @@ -467,7 +1506,7 @@ t_guard_fun(Config) when is_list(Config) -> {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} -> ok; {'EXIT', {{case_clause,_},_}} -> {comment,inlined}; Other -> - test_server:fail({no_match, Other}) + ct:fail({no_match, Other}) end. @@ -487,7 +1526,7 @@ t_map_sort_literals(Config) when is_list(Config) -> true = id(#{ c => 1, b => 1, a => 1 }) < id(#{ b => 1, c => 1, d => 1}), true = id(#{ "a" => 1 }) < id(#{ <<"a">> => 1}), false = id(#{ <<"a">> => 1 }) < id(#{ "a" => 1}), - false = id(#{ 1 => 1 }) < id(#{ 1.0 => 1}), + true = id(#{ 1 => 1 }) < id(#{ 1.0 => 1}), false = id(#{ 1.0 => 1 }) < id(#{ 1 => 1}), %% value order @@ -553,7 +1592,7 @@ t_build_and_match_empty_val(Config) when is_list(Config) -> {'EXIT',{function_clause,_}} -> ok; {'EXIT', {{case_clause,_},_}} -> {comment,inlined}; Other -> - test_server:fail({no_match, Other}) + ct:fail({no_match, Other}) end. t_build_and_match_val(Config) when is_list(Config) -> @@ -571,7 +1610,7 @@ t_build_and_match_val(Config) when is_list(Config) -> {'EXIT',{function_clause,_}} -> ok; {'EXIT', {{case_clause,_},_}} -> {comment,inlined}; Other -> - test_server:fail({no_match, Other}) + ct:fail({no_match, Other}) end. t_build_and_match_nil(Config) when is_list(Config) -> @@ -585,6 +1624,7 @@ t_build_and_match_nil(Config) when is_list(Config) -> "treat" => V2, [] => V1 }), #{ [] := V3, [] := V3 } = id(#{ [] => V1, [] => V3 }), + #{ <<1>> := V3, [] := V1 } = id(#{ [] => V1, <<1>> => V3 }), ok. t_build_and_match_structure(Config) when is_list(Config) -> @@ -601,5 +1641,386 @@ t_build_and_match_structure(Config) when is_list(Config) -> end, ok. +%% simple build and match variables +t_build_and_match_variables(Config) when is_list(Config) -> + K0 = id(#{}), + K1 = id(1), V1 = id(a), + K2 = id(2), V2 = id(b), + K3 = id(3), V3 = id("c"), + K4 = id("4"), V4 = id("d"), + K5 = id(<<"5">>), V5 = id(<<"e">>), + K6 = id({"6",7}), V6 = id("f"), + K7 = id(#{ "a" => 3 }), + #{K1:=V1} = id(#{K1=>V1}), + #{K1:=V1,K2:=V2} = id(#{K1=>V1,K2=>V2}), + #{K1:=V1,K2:=V2,K3:=V3} = id(#{K1=>V1,K2=>V2,K3=>V3}), + #{K1:=V1,K2:=V2,K3:=V3,K4:=V4} = id(#{K1=>V1,K2=>V2,K3=>V3,K4=>V4}), + #{K1:=V1,K2:=V2,K3:=V3,K4:=V4,K5:=V5} = id(#{K1=>V1,K2=>V2,K3=>V3,K4=>V4,K5=>V5}), + #{K1:=V1,K2:=V2,K3:=V3,K4:=V4,K5:=V5,K6:=V6} = id(#{K1=>V1,K2=>V2,K3=>V3,K4=>V4,K5=>V5,K6=>V6}), + + #{K5:=X,K5:=X=3,K4:=4} = id(#{K5=>3,K4=>4}), + #{K5:=X,<<"5">>:=X=3,K4:=4} = id(#{K5=>3,K4=>4}), + #{K5:=X,<<"5">>:=X=3,K4:=4} = id(#{<<"5">>=>3,K4=>4}), + + #{ K4:=#{ K3:=#{K1:=V1, K2:=V2}}, K5:=V5} = + id(#{ K5=>V5, K4=>#{ K3=>#{K2 => V2, K1 => V1}}}), + #{ K4 := #{ K5 := Res }, K6 := Res} = id(#{K4=>#{K5 => 99}, K6 => 99}), + + %% has keys + #{a :=_,b :=_,K1:=_,K2:=_,K3:=V3,K4:=ResKey,K4:=ResKey,"4":=ResKey,"4":="ok"} = + id(#{ a=>1, b=>1, K1=>V1, K2=>V2, K3=>V3, K4=>"nope", "4"=>"ok" }), + + %% function + ok = match_function_map_neg_keys(#{ -1 => a, -2 => b, -3 => c }), + + %% map key + #{ K0 := 42 } = id(#{ K0 => 42 }), + #{ K7 := 42 } = id(#{ K7 => 42 }), + + %% nil key + KNIL = id([]), + #{KNIL:=ok,1:=2} = id(#{KNIL=>ok,1=>2}), + + Bin = <<0:258>>, + #{ Bin := "three" } = id(#{<<0:258>> =>"three"}), + + %% error case + {'EXIT',{{badmatch,_},_}} = (catch (#{K5:=3,x:=2} = id(#{K5=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{K5:=2} = id(#{K5=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{K5:=3} = id({a,b,c}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{K5:=3} = id(#{K6=>3}))), + {'EXIT',{{badmatch,_},_}} = (catch (#{K5:=3} = id(K7))), + {'EXIT',{{badmatch,_},_}} = (catch (#{K7:=3} = id(#{K7=>42}))), + ok. + + +match_function_map_neg_keys(#{ -1 := a, -2 := b, -3 := c }) -> ok. + +t_update_assoc_variables(Config) when is_list(Config) -> + K1 = id(1), + K2 = id(2), + K3 = id(3.0), + K4 = id(4), + K5 = id(5), + K6 = id(2.0), + + M0 = #{K1=>a,K2=>b,K3=>c,K4=>d,K5=>e}, + + M1 = M0#{K1=>42,K2=>100,K4=>[a,b,c]}, + #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1, + #{1:=42,2:=b,4:=d,5:=e,2.0:=100,K3:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,K6=>100,4.0=>[a,b,c]}, + + M2 = M0#{K3=>new}, + #{1:=a,2:=b,K3:=new,4:=d,5:=e} = M2, + M2 = M0#{3.0:=wrong,K3=>new}, + + #{ <<0:258>> := val } = id(M0#{<<0:258>> => val}), %% binary limitation + + %% Errors cases. + BadMap = id(badmap), + {'EXIT',{{badmap,BadMap},_}} = (catch BadMap#{nonexisting=>val}), + {'EXIT',{{badmap,<<>>},_}} = (catch <<>>#{nonexisting=>val}), + ok. + +t_update_exact_variables(Config) when is_list(Config) -> + K1 = id(1), + K2 = id(2), + K3 = id(3.0), + K4 = id(4), + + M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}), + + M1 = M0#{K1:=42,K2:=100,K4:=[a,b,c]}, + #{1:=42,2:=100,3.0:=c,K4:=[a,b,c],5:=e} = M1, + M1 = M0#{K1:=wrong,1:=also_wrong,K1=>42,2=>wrong,K2:=100,4:=[a,b,c]}, + + M2 = M0#{K3:=new}, + #{1:=a,K2:=b,3.0:=new,K4:=d,5:=e} = M2, + M2 = M0#{3.0=>wrong,K3:=new}, + true = M2 =/= M0#{3=>right,3.0:=new}, + #{ 3 := right, 3.0 := new } = M0#{3=>right,K3:=new}, + + M3 = id(#{ 1 => val}), + #{1 := update2,1.0 := new_val4} = M3#{ + 1.0 => new_val1, K1 := update, K1=> update3, + K1 := update2, 1.0 := new_val2, 1.0 => new_val3, + 1.0 => new_val4 }, + + #{ "wat" := 3, 2 := a } = id(#{ "wat" => 1, K2 => 2 }#{ K2 := a, "wat" := 3 }), + + %% Errors cases. + {'EXIT',{{badmap,nil},_}} = (catch ((id(nil))#{ a := b })), + {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}), + {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}), + {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}), + {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}), + {'EXIT',{{badmap,<<>>},_}} = (catch <<>>#{nonexisting:=val}), + {'EXIT',{{badkey,<<0:257>>},_}} = + (catch M0#{<<0:257>> := val}), %limitation + ok. + +t_nested_pattern_expressions(Config) when is_list(Config) -> + K1 = id("hello"), + %K2 = id({ok}), + [_,_,#{ <<"hi">> := wat, K1 := 42 }|_] = id([k,k,#{<<"hi">> => wat, K1 => 42}]), + [_,_,#{ -1 := wat, K1 := 42 }|_] = id([k,k,#{-1 => wat, K1 => 42}]), + [_,_,{#{ -1 := #{ {-3,<<0:300>>} := V1 }, K1 := 42 },3}|_] = id([k,k,{#{-1 => #{{-3,<<0:300>>}=>"hi"}, K1 => 42},3}]), + "hi" = V1, + %[k,#{ {-1,K1,[]} := {wat,K1}, K2 := 42 }|_] = id([k,#{{-1,K1,[]} => {wat,K1}, K2 => 42}]), + %[k,#{ [-1,K2,[]] := {wat,K1}, K1 := 42 }|_] = id([k,#{[-1,K2,[]] => {wat,K1}, K1 => 42}]), + + M0 = id(#{ <<33:333>> => 1, <<332:333>> => ok, a => ok, wat => yep, watzor => ok }), + F0 = map_nested_pattern_funs(M0), + F1 = F0(wat), + F2 = F1(watzor), + {yep,ok} = F2(M0), + ok. + +map_nested_pattern_funs(M) -> + K0 = id(a), + fun(K1) -> + case M of + #{ K0 := ok, K1 := yep, <<33:333>> := 1 } -> + fun(K2) -> + case M of + #{ K2 := ok, K1 := yep, <<33:333>> := 1 } -> + fun + (#{ <<332:333>> := ok, K1 := V1, K2 := V2 }) -> + {V1,V2} + end + end + end + end + end. + +t_guard_update_variables(Config) when is_list(Config) -> + error = map_guard_update_variables(n,#{},#{}), + first = map_guard_update_variables(x,#{}, #{x=>first}), + second = map_guard_update_variables(x,#{y=>old}, #{x=>second,y=>old}), + third = map_guard_update_variables(x,#{x=>old,y=>old}, #{x=>third,y=>old}), + fourth = map_guard_update_variables(x,#{x=>old,y=>old}, #{x=>4,y=>new}), + ok. + +map_guard_update_variables(K,M1,M2) when M1#{K=>first} =:= M2 -> first; +map_guard_update_variables(K,M1,M2) when M1#{K=>second} =:= M2 -> second; +map_guard_update_variables(K,M1,M2) when M1#{K:=third} =:= M2 -> third; +map_guard_update_variables(K,M1,M2) when M1#{K:=4,y=>new} =:= M2 -> fourth; +map_guard_update_variables(_,_,_) -> error. + +t_guard_sequence_variables(Config) when is_list(Config) -> + {1,"a"} = map_guard_sequence_var_1(a,#{seq=>1,a=>id("a"),b=>no}), + {2,"b"} = map_guard_sequence_var_1(b,#{seq=>2,b=>id("b"),a=>no}), + {3,"c"} = map_guard_sequence_var_1(a,#{seq=>3,a=>id("c"),b=>no}), + {4,"d"} = map_guard_sequence_var_1(b,#{seq=>4,b=>id("d"),a=>no}), + {4,4} = map_guard_sequence_var_1(seq,#{seq=>4}), + {4,4,y} = map_guard_sequence_var_1(seq,#{seq=>4,b=>id("d"),a=>y}), + {5,"d"} = map_guard_sequence_var_1(b,#{seq=>5,b=>id("d"),a=>y}), + + %% error case + {'EXIT',{{case_clause,_},_}} = (catch map_guard_sequence_var_1("a",#{seq=>4,val=>id("e")})), + ok. + + +map_guard_sequence_var_1(K,M) -> + case M of + #{seq:=1=Seq, K:=Val} -> {Seq,Val}; + #{seq:=2=Seq, K:=Val} -> {Seq,Val}; + #{seq:=3=Seq, K:=Val} -> {Seq,Val}; + #{K:=4=Seq, K:=Val1,a:=Val2} -> {Seq,Val1,Val2}; + #{seq:=4=Seq, K:=Val} -> {Seq,Val}; + #{K:=4=Seq, K:=Val} -> {Seq,Val}; + #{seq:=5=Seq, K:=Val} -> {Seq,Val} + end. + + +t_guard_sequence_mixed(Config) when is_list(Config) -> + M0 = id(#{ a=>1, b=>1, c=>1, d=>1, e=>1, f=>1, g=>1, h=>1 }), + M1 = id(M0#{ d := 3 }), + 1 = map_guard_sequence_mixed(a,d,M1), + M2 = id(M1#{ b := 2, d := 4, h := 2 }), + 2 = map_guard_sequence_mixed(a,d,M2), + M3 = id(M2#{ b := 3, e := 5, g := 3 }), + 3 = map_guard_sequence_mixed(a,e,M3), + M4 = id(M3#{ c := 4, e := 6, h := 1 }), + 4 = map_guard_sequence_mixed(a,e,M4), + M5 = id(M4#{ c := 5, f := 7, g := 2 }), + 5 = map_guard_sequence_mixed(a,f,M5), + M6 = id(M5#{ c := 6, f := 8, h := 3 }), + 6 = map_guard_sequence_mixed(a,f,M6), + + %% error case + {'EXIT',{{case_clause,_},_}} = (catch map_guard_sequence_mixed(a,b,M0)), + ok. + +map_guard_sequence_mixed(K1,K2,M) -> + case M of + #{ K1 := 1, b := 1, K2 := 3, g := 1} -> 1; + #{ K1 := 1, b := 2, K2 := 4, h := 2} -> 2; + #{ K1 := 1, b := 3, K2 := 5, g := 3} -> 3; + #{ K1 := 1, c := 4, K2 := 6, h := 1} -> 4; + #{ K1 := 1, c := 5, K2 := 7, g := 2} -> 5; + #{ K1 := 1, c := 6, K2 := 8, h := 3} -> 6 + end. + +%% register corruption discovered in 18 due to +%% get_map_elements might destroys registers when fail-label is taken. +%% Only seen when patterns have two targets, +%% specifically: we copy one register, and then jump. +%% {test,is_map,{f,5},[{x,1}]}. +%% +%% {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}. +%% %% if 'a' exists but not 'b' {x,1} is overwritten, jump {f,7} +%% +%% {move,{integer,1},{x,0}}. +%% {call_only,3,{f,10}}. +%% +%% {label,7}. +%% {get_map_elements,{f,8},{x,1},{list,[{atom,b},{x,2}]}}. +%% %% {x,1} (src) is now corrupt +%% +%% {move,{x,0},{x,1}}. +%% {move,{integer,2},{x,0}}. +%% {call_only,3,{f,10}}. +%% +%% Only happens in beam_block opt_move pass with two destinations. + +t_register_corruption(Config) when is_list(Config) -> + M = #{a=> <<"value">>, c=>3}, + {3,wanted,<<"value">>} = register_corruption_bar(M,wanted), + {3,wanted,<<"value">>} = register_corruption_foo(wanted,M), + ok. + +register_corruption_foo(A,#{a := V1, b := V2}) -> + register_corruption_dummy_call(1,V1,V2); +register_corruption_foo(A,#{b := V}) -> + register_corruption_dummy_call(2,A,V); +register_corruption_foo(A,#{a := V}) -> + register_corruption_dummy_call(3,A,V). + +register_corruption_bar(M,A) -> + case M of + #{a := V1, b := V2} -> + register_corruption_dummy_call(1,V1,V2); + #{b := V} -> + register_corruption_dummy_call(2,A,V); + #{a := V} -> + register_corruption_dummy_call(3,A,V) + end. + + +register_corruption_dummy_call(A,B,C) -> {A,B,C}. + + +t_frequency_table(Config) when is_list(Config) -> + rand:seed(exsplus, {13,1337,54}), % pseudo random + N = 100000, + Ts = rand_terms(N), + #{ n:=N, tf := Tf } = frequency_table(Ts,#{ n=>0, tf => #{}}), + ok = check_frequency(Ts,Tf), + ok. + + +frequency_table([T|Ts], M) -> + case M of + #{ n := N, tf := #{ T := C } = F } -> + frequency_table(Ts,M#{ n := N + 1, tf := F#{ T := C + 1 }}); + #{ n := N, tf := F } -> + frequency_table(Ts,M#{ n := N + 1, tf := F#{ T => 1 }}) + end; +frequency_table([], M) -> M. + + +check_frequency(Ts,Tf) -> + check_frequency(Ts,Tf,dict:new()). + +check_frequency([T|Ts],Tf,D) -> + case dict:find(T,D) of + error -> check_frequency(Ts,Tf,dict:store(T,1,D)); + {ok,C} -> check_frequency(Ts,Tf,dict:store(T,C+1,D)) + end; +check_frequency([],Tf,D) -> + validate_frequency(dict:to_list(D),Tf). + +validate_frequency([{T,C}|Fs],Tf) -> + case Tf of + #{ T := C } -> validate_frequency(Fs,Tf); + _ -> error + end; +validate_frequency([], _) -> ok. + + +t_bad_update(_Config) -> + {#{0.0:=Id},#{}} = properly(#{}), + 42 = Id(42), + {'EXIT',{{badmap,_},_}} = (catch increase(0)), + ok. + +properly(Item) -> + {Item#{0.0 => fun id/1},Item}. + +increase(Allows) -> + catch fun() -> Allows end#{[] => +Allows, "warranty" => fun id/1}. + + +%% aux + +rand_terms(0) -> []; +rand_terms(N) -> [rand_term()|rand_terms(N-1)]. + +rand_term() -> + case rand:uniform(6) of + 1 -> rand_binary(); + 2 -> rand_number(); + 3 -> rand_atom(); + 4 -> rand_tuple(); + 5 -> rand_list(); + 6 -> rand_map() + end. + +rand_binary() -> + case rand:uniform(3) of + 1 -> <<>>; + 2 -> <<"hi">>; + 3 -> <<"message text larger than 64 bytes. yep, message text larger than 64 bytes.">> + end. + +rand_number() -> + case rand:uniform(3) of + 1 -> rand:uniform(5); + 2 -> float(rand:uniform(5)); + 3 -> 1 bsl (63 + rand:uniform(3)) + end. + +rand_atom() -> + case rand:uniform(3) of + 1 -> hi; + 2 -> some_atom; + 3 -> some_other_atom + end. + + +rand_tuple() -> + case rand:uniform(3) of + 1 -> {ok, rand_term()}; % careful + 2 -> {1, 2, 3}; + 3 -> {<<"yep">>, 1337} + end. + +rand_list() -> + case rand:uniform(3) of + 1 -> "hi"; + 2 -> [1,rand_term()]; % careful + 3 -> [improper|list] + end. + +rand_map() -> + case rand:uniform(3) of + 1 -> #{ hi => 3 }; + 2 -> #{ wat => rand_term(), other => 3 }; % careful + 3 -> #{ hi => 42, other => 42, yet_anoter => 1337 } + end. + + + %% Use this function to avoid compile-time evaluation of an expression. id(I) -> I. diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index 1e778dca24..127679ba69 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -20,11 +21,12 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - pmatch/1,mixed/1,aliases/1,match_in_call/1, - untuplify/1,shortcut_boolean/1,letify_guard/1, - selectify/1,underscore/1,match_map/1,coverage/1]). + pmatch/1,mixed/1,aliases/1,non_matching_aliases/1, + match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1, + selectify/1,underscore/1,match_map/1,map_vars_used/1, + coverage/1,grab_bag/1,literal_binary/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -33,10 +35,12 @@ all() -> [{group,p}]. groups() -> - [{p,test_lib:parallel(), - [pmatch,mixed,aliases,match_in_call,untuplify, + [{p,[parallel], + [pmatch,mixed,aliases,non_matching_aliases, + match_in_call,untuplify, shortcut_boolean,letify_guard,selectify, - underscore,match_map,coverage]}]. + underscore,match_map,map_vars_used,coverage, + grab_bag,literal_binary]}]. init_per_suite(Config) -> @@ -53,10 +57,10 @@ end_per_group(_GroupName, Config) -> pmatch(Config) when is_list(Config) -> - ?line ok = doit(1), - ?line ok = doit(2), - ?line {error,baz} = doit(3), - ?line {error,foobar} = doit(4), + ok = doit(1), + ok = doit(2), + {error,baz} = doit(3), + {error,foobar} = doit(4), ok. %% Thanks to Tobias Lindahl (HiPE). @@ -76,13 +80,13 @@ doit(X) -> end. mixed(Config) when is_list(Config) -> - ?line glufs = mixit(1), - ?line klafs = mixit(2), - ?line fnurra = mixit(3), - ?line usch = mixit(4), - ?line {error,blurf} = mixit(5), - ?line {error,87987987} = mixit(6), - ?line {error,{a,b,c}} = mixit(7), + glufs = mixit(1), + klafs = mixit(2), + fnurra = mixit(3), + usch = mixit(4), + {error,blurf} = mixit(5), + {error,87987987} = mixit(6), + {error,{a,b,c}} = mixit(7), ok. mixit(X) -> @@ -91,9 +95,9 @@ mixit(X) -> 2 -> b; 3 -> 42; 4 -> 77; - 5 -> blurf; - 6 -> 87987987; - 7 -> {a,b,c} + 4+1 -> blurf; + 5+1 -> 87987987; + 6+1 -> {a,b,c} end of a -> glufs; b -> klafs; @@ -104,41 +108,41 @@ mixit(X) -> aliases(Config) when is_list(Config) -> %% Lists/strings. - ?line ok = str_alias("abc"), - ?line ok = str_alias("def"), - ?line ok = str_alias("ghi"), - ?line ok = str_alias("klm"), - ?line ok = str_alias("qrs"), - ?line ok = str_alias("xy"), - ?line ok = str_alias(""), - ?line ok = str_alias([]), - ?line error = str_alias("blurf"), + ok = str_alias("abc"), + ok = str_alias("def"), + ok = str_alias("ghi"), + ok = str_alias("klm"), + ok = str_alias("qrs"), + ok = str_alias("xy"), + ok = str_alias(""), + ok = str_alias([]), + error = str_alias("blurf"), %% Characters/integers. - ?line ok = char_alias($v), - ?line ok = char_alias(118), - ?line ok = char_alias($w), - ?line ok = char_alias(119), - ?line ok = char_alias(42), - ?line ok = char_alias(3.0), - ?line error = char_alias($_), - ?line error = char_alias(0), - - ?line {42,42,42} = three(42), - - ?line {1,42,99,1,42,99} = tuple_alias({1,42,99}), - ?line {-10,20,-10,20,-10,20} = tuple_alias({-10,20}), - ?line 6 = tup_lit_alias({1,2,3}), - ?line 6 = tup_lit_alias_rev({1,2,3}), - - ?line {42,42,42,42} = multiple_aliases_1(42), - ?line {7,7,7} = multiple_aliases_2(7), - ?line {{a,b},{a,b},{a,b}} = multiple_aliases_3({a,b}), + ok = char_alias($v), + ok = char_alias(118), + ok = char_alias($w), + ok = char_alias(119), + ok = char_alias(42), + ok = char_alias(3.0), + error = char_alias($_), + error = char_alias(0), + + {42,42,42} = three(42), + + {1,42,99,1,42,99} = tuple_alias({1,42,99}), + {-10,20,-10,20,-10,20} = tuple_alias({-10,20}), + 6 = tup_lit_alias({1,2,3}), + 6 = tup_lit_alias_rev({1,2,3}), + + {42,42,42,42} = multiple_aliases_1(42), + {7,7,7} = multiple_aliases_2(7), + {{a,b},{a,b},{a,b}} = multiple_aliases_3({a,b}), %% Lists/literals. - ?line {a,b} = list_alias1([a,b]), - ?line {a,b} = list_alias2([a,b]), - ?line {a,b} = list_alias3([a,b]), + {a,b} = list_alias1([a,b]), + {a,b} = list_alias2([a,b]), + {a,b} = list_alias3([a,b]), ok. @@ -243,17 +247,90 @@ list_alias2([X,Y]=[a,b]) -> list_alias3([X,b]=[a,Y]) -> {X,Y}. +non_matching_aliases(_Config) -> + none = mixed_aliases(<<42>>), + none = mixed_aliases([b]), + none = mixed_aliases([d]), + none = mixed_aliases({a,42}), + none = mixed_aliases(42), + + {'EXIT',{{badmatch,42},_}} = (catch nomatch_alias(42)), + {'EXIT',{{badmatch,job},_}} = (catch entirely()), + {'EXIT',{{badmatch,associates},_}} = (catch printer()), + {'EXIT',{{badmatch,borogoves},_}} = (catch tench()), + + put(perch, 0), + {'EXIT',{{badmatch,{spine,42}},_}} = (catch perch(42)), + 1 = erase(perch), + + put(salmon, 0), + {'EXIT',{{badmatch,mimsy},_}} = (catch salmon()), + 1 = erase(salmon), + + put(shark, 0), + {'EXIT',{{badmatch,_},_}} = (catch shark()), + 1 = erase(shark), + + {'EXIT',{{badmatch,_},_}} = (catch radio(research)), + ok. + +mixed_aliases(<<X:8>> = x) -> {a,X}; +mixed_aliases([b] = <<X:8>>) -> {b,X}; +mixed_aliases(<<X:8>> = {a,X}) -> {c,X}; +mixed_aliases([X] = <<X:8>>) -> {d,X}; +mixed_aliases(_) -> none. + +nomatch_alias(I) -> + {ok={A,B}} = id(I), + {A,B}. + +entirely() -> + 0(((Voice = true) = cool) = job), + [receive _ -> Voice end || banking <- printer]. + +printer() -> + {[Indoor] = [] = associates}, + [ireland || Indoor <- Indoor]. + +tench() -> + E = begin + [A] = [] = borogoves, + A + 1 + end, + E + 7 * A. + +perch(X) -> + begin + put(perch, get(perch)+1), + [A] = [] = {spine,X} + end. + +salmon() -> + {put(salmon, get(salmon)+1),#{key:=([A]=[])}=mimsy,exit(fail)}, + A + 10. + +shark() -> + (hello = there) = (catch shark(put(shark, get(shark)+1), a = b)). + +shark(_, _) -> + ok. + +radio(research) -> + (connection = proof) = + (catch erlang:trace_pattern(catch mechanisms + assist, + summary = mechanisms)). + %% OTP-7018. match_in_call(Config) when is_list(Config) -> - ?line mac_a(0), - ?line mac_b(1), - ?line mac_c(42), - ?line mac_d(42), - ?line mac_e({gurka,42}), + mac_a(0), + mac_b(1), + mac_c(42), + mac_d(42), + mac_e({gurka,42}), - ?line [{2,2},{2,2}] = mac_lc([{2,any},{2,2}]), - ?line {'EXIT',_} = (catch mac_lc([{1,1}])), + [{2,2},{2,2}] = mac_lc([{2,any},{2,2}]), + {'EXIT',_} = (catch mac_lc([{1,1}])), ok. @@ -298,8 +375,8 @@ gurka({gurka,X}, X) -> ok. untuplify(Config) when is_list(Config) -> %% We do this to cover sys_core_fold:unalias_pat/1. - ?line {1,2,3,4,alias,{[1,2],{3,4},alias}} = untuplify_1([1,2], {3,4}, alias), - ?line error = untuplify_1([1,2], {3,4}, 42), + {1,2,3,4,alias,{[1,2],{3,4},alias}} = untuplify_1([1,2], {3,4}, alias), + error = untuplify_1([1,2], {3,4}, 42), ok. untuplify_1(A, B, C) -> @@ -314,11 +391,11 @@ untuplify_1(A, B, C) -> %% Coverage of beam_dead:shortcut_boolean_label/4. shortcut_boolean(Config) when is_list(Config) -> - ?line false = shortcut_boolean_1([0]), - ?line true = shortcut_boolean_1({42}), - ?line maybe = shortcut_boolean_1(self()), - ?line {'EXIT',_} = (catch shortcut_boolean_1([a,b])), - ?line {'EXIT',_} = (catch shortcut_boolean_1({a,b})), + false = shortcut_boolean_1([0]), + true = shortcut_boolean_1({42}), + maybe = shortcut_boolean_1(self()), + {'EXIT',_} = (catch shortcut_boolean_1([a,b])), + {'EXIT',_} = (catch shortcut_boolean_1({a,b})), ok. shortcut_boolean_1(X) -> @@ -337,8 +414,8 @@ shortcut_boolean_1(X) -> %% Test sys_core_fold:letify_guard/3. letify_guard(Config) when is_list(Config) -> - ?line {-15,a} = letify_guard(-15, a), - ?line 5 = letify_guard(2, 3), + {-15,a} = letify_guard(-15, a), + 5 = letify_guard(2, 3), ok. letify_guard(A, B) -> @@ -354,18 +431,18 @@ letify_guard(A, B) -> %% instructions in beam_dead and beam_peep. selectify(Config) when is_list(Config) -> - ?line integer = sel_different_types({r,42}), - ?line atom = sel_different_types({r,forty_two}), - ?line none = sel_different_types({r,18}), - ?line {'EXIT',_} = (catch sel_different_types([a,b,c])), - - ?line integer = sel_same_value({r,42}), - ?line error = sel_same_value({r,100}), - ?line error = sel_same_value(a), - - ?line integer42 = sel_same_value2(42), - ?line integer43 = sel_same_value2(43), - ?line error = sel_same_value2(44), + integer = sel_different_types({r,42}), + atom = sel_different_types({r,forty_two}), + none = sel_different_types({r,18}), + {'EXIT',_} = (catch sel_different_types([a,b,c])), + + integer = sel_same_value({r,42}), + error = sel_same_value({r,100}), + error = sel_same_value(a), + + integer42 = sel_same_value2(42), + integer43 = sel_same_value2(43), + error = sel_same_value2(44), ok. sel_different_types({r,_}=T) when element(2, T) =:= forty_two -> @@ -419,10 +496,25 @@ do_match_map_2(Map) -> Tuple end. +map_vars_used(Config) when is_list(Config) -> + {some,value} = do_map_vars_used(a, b, #{{a,b}=>42,v=>{some,value}}), + ok. + +do_map_vars_used(X, Y, Map) -> + case {X,Y} of + T -> + %% core_lib:is_var_used/2 would not consider T used. + #{T:=42,v:=Val} = Map, + Val + end. + coverage(Config) when is_list(Config) -> %% Cover beam_dead. ok = coverage_1(x, a), - ok = coverage_1(x, b). + ok = coverage_1(x, b), + + %% Cover sys_pre_expand. + ok = coverage_3("abc"). coverage_1(B, Tag) -> case Tag of @@ -433,4 +525,64 @@ coverage_1(B, Tag) -> coverage_2(1, a, x) -> ok; coverage_2(2, b, x) -> ok. +coverage_3([$a]++[]++"bc") -> ok. + +grab_bag(_Config) -> + [_|T] = id([a,b,c]), + [b,c] = id(T), + + T1 = fun() -> + [_|_] = x + end, + {'EXIT',_} = (catch T1()), + + T2 = fun(A, B) -> + case {{element(1, A),element(2, B)}, + {element(2, A),element(2, B)}} of + {Same,Same} -> ok; + {{0,1},{up,X}} -> id(X); + {_,{X,_}} -> id(X) + end + end, + ok = T2({a,a,z,z}, {z,a,z}), + 1 = T2({0,up}, {zzz,1}), + y = T2({x,y}, {a,z,z}), + + %% OTP-5244. + L = [{stretch,0,0}, + {bad,[]}, + {bad,atom}, + {bad,0}, + {bad,16#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}, + {bad,16#555555555555555555555555555555555555555555555555555}], + ok = grab_bag_remove_failure(L, unit, 0), + + ok. + +grab_bag_remove_failure([], _Unit, _MaxFailure) -> + ok; +grab_bag_remove_failure([{bad,Bad}|_], _Unit, _MaxFailure) -> + Bad; +grab_bag_remove_failure([{stretch,_,Mi}=Stretch | Specs], Unit, _MaxFailure) -> + {MinMax,NewMaxFailure} = id({min,1}), + case {MinMax,grab_bag_remove_failure(Specs, Unit, NewMaxFailure)} of + {min,{NewMaxFailure,Rest}} -> + {done,[{fixed,Mi} | Rest]}; + {min,_} when Specs =/= [] -> + grab_bag_remove_failure([Stretch|tl(Specs)], Unit, NewMaxFailure); + {min,_} -> + ok + end. + +%% Regression in 19.0, reported by Alexei Sholik +literal_binary(_Config) -> + 3 = literal_binary_match(bar,<<"y">>), + ok. + +literal_binary_match(bar, <<"x">>) -> 1; +literal_binary_match(_, <<"x">>) -> 2; +literal_binary_match(_, <<"y">>) -> 3; +literal_binary_match(_, _) -> fail. + + id(I) -> I. diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl index 44c7161530..f543f0d4de 100644 --- a/lib/compiler/test/misc_SUITE.erl +++ b/lib/compiler/test/misc_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -22,9 +23,10 @@ init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, tobias/1,empty_string/1,md5/1,silly_coverage/1, - confused_literals/1,integer_encoding/1,override_bif/1]). + confused_literals/1,integer_encoding/0,integer_encoding/1, + override_bif/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %% For the override_bif testcase. %% NB, no other testcases in this testsuite can use these without erlang:prefix! @@ -37,22 +39,25 @@ -compile({no_auto_import,[byte_size/1]}). -import(erlang,[byte_size/1]). - +%% Cover the code for callback handling. +-callback must_define_this_one() -> 'ok'. +-callback do_something_strange(atom()) -> 'ok'. +-optional_callbacks([do_something_strange/1]). +-optional_callbacks([ignore_me]). %Invalid; ignored. %% Include an opaque declaration to cover the stripping of %% opaque types from attributes in v3_kernel. -opaque misc_SUITE_test_cases() :: [atom()]. init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = test_server:timetrap(?t:minutes(10)), - [{watchdog,Dog}|Config]. + Config. end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,10}}]. -spec all() -> misc_SUITE_test_cases(). all() -> @@ -60,7 +65,7 @@ all() -> [{group,p}]. groups() -> - [{p,[],%%test_lib:parallel(), + [{p,[], [tobias,empty_string,md5,silly_coverage, confused_literals,integer_encoding,override_bif]}]. @@ -87,32 +92,27 @@ abs(_N) -> binary_part(_,_,_) -> dummy_bp. -% Make sure that auto-imported BIF's are overridden correctly - -override_bif(suite) -> - []; -override_bif(doc) -> - ["Test dat local functions and imports override auto-imported BIFs."]; +%% Test that local functions and imports override auto-imported BIFs. override_bif(Config) when is_list(Config) -> - ?line dummy_abs = abs(1), - ?line dummy_bp = binary_part(<<"hello">>,1,1), - ?line dummy = binary_part(<<"hello">>,{1,1}), - ?line 1 = erlang:abs(1), - ?line <<"e">> = erlang:binary_part(<<"hello">>,1,1), - ?line <<"e">> = erlang:binary_part(<<"hello">>,{1,1}), + dummy_abs = abs(1), + dummy_bp = binary_part(<<"hello">>,1,1), + dummy = binary_part(<<"hello">>,{1,1}), + 1 = erlang:abs(1), + <<"e">> = erlang:binary_part(<<"hello">>,1,1), + <<"e">> = erlang:binary_part(<<"hello">>,{1,1}), F = fun(X) when byte_size(X) =:= 4 -> four; (X) -> byte_size(X) end, - ?line four = F(<<1,2,3,4>>), - ?line 5 = F(<<1,2,3,4,5>>), + four = F(<<1,2,3,4>>), + 5 = F(<<1,2,3,4,5>>), ok. %% A bug reported by Tobias Lindahl for a development version of R11B. tobias(Config) when is_list(Config) -> - ?line 1 = tobias_1([1,2,3]), + 1 = tobias_1([1,2,3]), ok. tobias_1([H|_T]) -> @@ -133,7 +133,7 @@ tobias_2(_, _) -> -record(r, {s = ""}). empty_string(Config) when is_list(Config) -> - ?line #r{s="x"} = empty_string_1(#r{}), + #r{s="x"} = empty_string_1(#r{}), ok. empty_string_1(T) -> @@ -148,15 +148,15 @@ md5(Config) when is_list(Config) -> end. md5() -> - ?line Dir = filename:dirname(code:which(?MODULE)), - ?line Beams = filelib:wildcard(filename:join(Dir, "*.beam")), - ?line io:format("Found ~w beam files", [length(Beams)]), - ?line lists:foreach(fun md5_1/1, Beams). + Dir = filename:dirname(code:which(?MODULE)), + Beams = filelib:wildcard(filename:join(Dir, "*.beam")), + io:format("Found ~w beam files", [length(Beams)]), + lists:foreach(fun md5_1/1, Beams). md5_1(Beam) -> - ?line {ok,{Mod,[Vsn]}} = beam_lib:version(Beam), - ?line {ok,Code} = file:read_file(Beam), - ?line {Mod,<<Vsn:128>>} = {Mod,code:module_md5(Code)}. + {ok,{Mod,[Vsn]}} = beam_lib:version(Beam), + {ok,Code} = file:read_file(Beam), + {Mod,<<Vsn:128>>} = {Mod,code:module_md5(Code)}. %% Cover some code that handles internal errors. @@ -165,9 +165,9 @@ silly_coverage(Config) when is_list(Config) -> BadCoreErlang = {c_module,[], name,[],[], [{{c_var,[],{foo,2}},seriously_bad_body}]}, - ?line expect_error(fun() -> sys_core_fold:module(BadCoreErlang, []) end), - ?line expect_error(fun() -> sys_core_dsetel:module(BadCoreErlang, []) end), - ?line expect_error(fun() -> v3_kernel:module(BadCoreErlang, []) end), + expect_error(fun() -> sys_core_fold:module(BadCoreErlang, []) end), + expect_error(fun() -> sys_core_dsetel:module(BadCoreErlang, []) end), + expect_error(fun() -> v3_kernel:module(BadCoreErlang, []) end), %% v3_life BadKernel = {k_mdef,[],?MODULE, @@ -177,11 +177,11 @@ silly_coverage(Config) when is_list(Config) -> {k,[],[],[]}, f,0,[], seriously_bad_body}]}, - ?line expect_error(fun() -> v3_life:module(BadKernel, []) end), + expect_error(fun() -> v3_life:module(BadKernel, []) end), %% v3_codegen CodegenInput = {?MODULE,[{foo,0}],[],[{function,foo,0,[a|b],a,b,[]}]}, - ?line expect_error(fun() -> v3_codegen:module(CodegenInput, []) end), + expect_error(fun() -> v3_codegen:module(CodegenInput, []) end), %% beam_a BeamAInput = {?MODULE,[{foo,0}],[], @@ -191,13 +191,25 @@ silly_coverage(Config) when is_list(Config) -> {label,2}|non_proper_list]}],99}, expect_error(fun() -> beam_a:module(BeamAInput, []) end), + %% beam_reorder + BlockInput = {?MODULE,[{foo,0}],[], + [{function,foo,0,2, + [{label,1}, + {func_info,{atom,?MODULE},{atom,foo},0}, + {label,2}|non_proper_list]}],99}, + expect_error(fun() -> beam_reorder:module(BlockInput, []) end), + %% beam_block BlockInput = {?MODULE,[{foo,0}],[], [{function,foo,0,2, [{label,1}, {func_info,{atom,?MODULE},{atom,foo},0}, {label,2}|non_proper_list]}],99}, - ?line expect_error(fun() -> beam_block:module(BlockInput, []) end), + expect_error(fun() -> beam_block:module(BlockInput, []) end), + + %% beam_bs + BsInput = BlockInput, + expect_error(fun() -> beam_bs:module(BsInput, []) end), %% beam_type TypeInput = {?MODULE,[{foo,0}],[], @@ -223,16 +235,17 @@ silly_coverage(Config) when is_list(Config) -> [{label,1}, {func_info,{atom,?MODULE},{atom,foo},0}, {label,2}|non_proper_list]}],99}, - ?line expect_error(fun() -> beam_bool:module(BoolInput, []) end), + expect_error(fun() -> beam_bool:module(BoolInput, []) end), - %% beam_dead + %% beam_dead. This is tricky. Our function must look OK to + %% beam_utils:clean_labels/1, but must crash beam_dead. DeadInput = {?MODULE,[{foo,0}],[], [{function,foo,0,2, [{label,1}, {func_info,{atom,?MODULE},{atom,foo},0}, {label,2}, - {jump,bad}]}],99}, - ?line expect_error(fun() -> beam_block:module(DeadInput, []) end), + {test,is_eq_exact,{f,1},[bad,operands]}]}],99}, + expect_error(fun() -> beam_dead:module(DeadInput, []) end), %% beam_clean CleanInput = {?MODULE,[{foo,0}],[], @@ -241,15 +254,18 @@ silly_coverage(Config) when is_list(Config) -> {func_info,{atom,?MODULE},{atom,foo},0}, {label,2}, {jump,{f,42}}]}],99}, - ?line expect_error(fun() -> beam_clean:module(CleanInput, []) end), + expect_error(fun() -> beam_clean:module(CleanInput, []) end), - %% beam_peep + %% beam_peep. This is tricky. Use a select instruction with + %% an odd number of elements in the list to crash + %% prune_redundant_values/2 but not beam_clean:clean_labels/1. PeepInput = {?MODULE,[{foo,0}],[], [{function,foo,0,2, [{label,1}, {func_info,{atom,?MODULE},{atom,foo},0}, - {label,2}|non_proper_list]}],99}, - ?line expect_error(fun() -> beam_peep:module(PeepInput, []) end), + {label,2},{select,op,r,{f,2},[{f,2}]}]}], + 2}, + expect_error(fun() -> beam_peep:module(PeepInput, []) end), %% beam_bsm. This is tricky. Our function must be sane enough to not crash %% btb_index/1, but must crash the main optimization pass. @@ -260,7 +276,7 @@ silly_coverage(Config) when is_list(Config) -> {label,2}, {test,bs_get_binary2,{f,99},0,[{x,0},{atom,all},1,[]],{x,0}}, {block,[a|b]}]}],0}, - ?line expect_error(fun() -> beam_bsm:module(BsmInput, []) end), + expect_error(fun() -> beam_bsm:module(BsmInput, []) end), %% beam_receive. ReceiveInput = {?MODULE,[{foo,0}],[], @@ -270,7 +286,7 @@ silly_coverage(Config) when is_list(Config) -> {label,2}, {call_ext,0,{extfunc,erlang,make_ref,0}}, {block,[a|b]}]}],0}, - ?line expect_error(fun() -> beam_receive:module(ReceiveInput, []) end), + expect_error(fun() -> beam_receive:module(ReceiveInput, []) end), BeamZInput = {?MODULE,[{foo,0}],[], [{function,foo,0,2, @@ -279,33 +295,44 @@ silly_coverage(Config) when is_list(Config) -> {label,2}|non_proper_list]}],99}, expect_error(fun() -> beam_z:module(BeamZInput, []) end), + %% beam_validator. + BeamValInput = {?MODULE,[{foo,0}],[], + [{function,foo,0,2, + [{label,1}, + {func_info,{atom,?MODULE},{atom,foo},0}, + {label,2}|non_proper_list]}],99}, + expect_error(fun() -> beam_validator:module(BeamValInput, []) end), + ok. expect_error(Fun) -> try Fun() of Any -> io:format("~p", [Any]), - ?t:fail(call_was_supposed_to_fail) + ct:fail(call_was_supposed_to_fail) catch Class:Reason -> Stk = erlang:get_stacktrace(), io:format("~p:~p\n~p\n", [Class,Reason,Stk]), case {Class,Reason} of {error,undef} -> - ?t:fail(not_supposed_to_fail_with_undef); + ct:fail(not_supposed_to_fail_with_undef); {_,_} -> ok end end. confused_literals(Config) when is_list(Config) -> - ?line {0,infinity} = confused_literals_1(int), - ?line {0.0,infinity} = confused_literals_1(float), + {0,infinity} = confused_literals_1(int), + {0.0,infinity} = confused_literals_1(float), ok. confused_literals_1(int) -> {0,infinity}; confused_literals_1(float) -> {0.0,infinity}. +integer_encoding() -> + [{timetrap,{minutes,4}}]. + integer_encoding(Config) when is_list(Config) -> case ?MODULE of misc_SUITE -> integer_encoding_1(Config); @@ -313,61 +340,65 @@ integer_encoding(Config) when is_list(Config) -> end. integer_encoding_1(Config) -> - Dog = test_server:timetrap(?t:minutes(4)), - ?line PrivDir = ?config(priv_dir, Config), - ?line SrcFile = filename:join(PrivDir, "misc_SUITE_integer_encoding.erl"), - ?line DataFile = filename:join(PrivDir, "integer_encoding.data"), + PrivDir = proplists:get_value(priv_dir, Config), + SrcFile = filename:join(PrivDir, "misc_SUITE_integer_encoding.erl"), + DataFile = filename:join(PrivDir, "integer_encoding.data"), Mod = misc_SUITE_integer_encoding, %% Create files. - ?line {ok,Src} = file:open(SrcFile, [write]), - ?line {ok,Data} = file:open(DataFile, [write]), + {ok,Src} = file:open(SrcFile, [write]), + {ok,Data} = file:open(DataFile, [write]), io:format(Src, "-module(~s).\n", [Mod]), io:put_chars(Src, "-export([t/1]).\n"), io:put_chars(Src, "t(Last) ->[\n"), io:put_chars(Data, "[\n"), - ?line do_integer_encoding(-(id(1) bsl 10000), Src, Data), - ?line do_integer_encoding(id(1) bsl 10000, Src, Data), - ?line do_integer_encoding(2048, 0, Src, Data), - + do_integer_encoding(-(id(1) bsl 10000), Src, Data), + do_integer_encoding(id(1) bsl 10000, Src, Data), + do_integer_encoding(1024, 0, Src, Data), + _ = [begin + B = 1 bsl I, + do_integer_encoding(-B-1, Src, Data), + do_integer_encoding(-B, Src, Data), + do_integer_encoding(-B+1, Src, Data), + do_integer_encoding(B-1, Src, Data), + do_integer_encoding(B, Src, Data), + do_integer_encoding(B+1, Src, Data) + end || I <- lists:seq(1, 128)], io:put_chars(Src, "Last].\n\n"), - ?line ok = file:close(Src), + ok = file:close(Src), io:put_chars(Data, "0].\n\n"), - ?line ok = file:close(Data), + ok = file:close(Data), %% Compile and load Erlang module. - ?line SrcRoot = filename:rootname(SrcFile), - ?line {ok,Mod,Binary} = compile:file(SrcRoot, [binary,report]), - ?line {module,Mod} = code:load_binary(Mod, SrcRoot, Binary), + SrcRoot = filename:rootname(SrcFile), + {ok,Mod,Binary} = compile:file(SrcRoot, [binary,report]), + {module,Mod} = code:load_binary(Mod, SrcRoot, Binary), %% Compare lists. - ?line List = Mod:t(0), - ?line {ok,[List]} = file:consult(DataFile), + List = Mod:t(0), + {ok,[List]} = file:consult(DataFile), OneBsl10000 = id(1) bsl 10000, - ?line [-(1 bsl 10000),OneBsl10000|_] = List, + [-(1 bsl 10000),OneBsl10000|_] = List, %% Cleanup. - ?line file:delete(SrcFile), - ?line file:delete(DataFile), - ?t:timetrap_cancel(Dog), + file:delete(SrcFile), + file:delete(DataFile), ok. do_integer_encoding(0, _, _, _) -> ok; do_integer_encoding(N, I0, Src, Data) -> - I1 = (I0 bsl 5) bor (random:uniform(32) - 1), + I1 = (I0 bsl 5) bor (rand:uniform(32) - 1), do_integer_encoding(I1, Src, Data), - I2 = -(I1 bxor (random:uniform(32) - 1)), + I2 = -(I1 bxor (rand:uniform(32) - 1)), do_integer_encoding(I2, Src, Data), do_integer_encoding(N-1, I1, Src, Data). do_integer_encoding(I, Src, Data) -> Str = integer_to_list(I), - io:put_chars(Src, Str), - io:put_chars(Src, ", \n"), - io:put_chars(Data, Str), - io:put_chars(Data, ", \n"). - + io:put_chars(Src, [Str,",\n"]), + io:put_chars(Data, [Str,",\n"]). + id(I) -> I. diff --git a/lib/compiler/test/num_bif_SUITE.erl b/lib/compiler/test/num_bif_SUITE.erl index 3479cf5425..7eac90bac3 100644 --- a/lib/compiler/test/num_bif_SUITE.erl +++ b/lib/compiler/test/num_bif_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% -module(num_bif_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %% Tests optimization of the BIFs: %% abs/1 @@ -64,68 +65,68 @@ end_per_group(_GroupName, Config) -> t_abs(Config) when is_list(Config) -> %% Floats. - ?line 5.5 = abs(5.5), - ?line 0.0 = abs(0.0), - ?line 100.0 = abs(-100.0), + 5.5 = abs(5.5), + 0.0 = abs(0.0), + 100.0 = abs(-100.0), %% Integers. - ?line 5 = abs(5), - ?line 0 = abs(0), - ?line 100 = abs(-100), + 5 = abs(5), + 0 = abs(0), + 100 = abs(-100), %% The largest smallnum. OTP-3190. - ?line X = (1 bsl 27) - 1, - ?line X = abs(X), - ?line X = abs(X-1)+1, - ?line X = abs(X+1)-1, - ?line X = abs(-X), - ?line X = abs(-X-1)-1, - ?line X = abs(-X+1)+1, + X = (1 bsl 27) - 1, + X = abs(X), + X = abs(X-1)+1, + X = abs(X+1)-1, + X = abs(-X), + X = abs(-X-1)-1, + X = abs(-X+1)+1, %% Bignums. BigNum = 13984792374983749, - ?line BigNum = abs(BigNum), - ?line BigNum = abs(-BigNum), + BigNum = abs(BigNum), + BigNum = abs(-BigNum), ok. t_float(Config) when is_list(Config) -> - ?line 0.0 = float(0), - ?line 2.5 = float(2.5), - ?line 0.0 = float(0.0), - ?line -100.55 = float(-100.55), - ?line 42.0 = float(42), - ?line -100.0 = float(-100), + 0.0 = float(0), + 2.5 = float(2.5), + 0.0 = float(0.0), + -100.55 = float(-100.55), + 42.0 = float(42), + -100.0 = float(-100), %% Bignums. - ?line 4294967305.0 = float(4294967305), - ?line -4294967305.0 = float(-4294967305), + 4294967305.0 = float(4294967305), + -4294967305.0 = float(-4294967305), %% Extremly big bignums. - ?line Big = list_to_integer(lists:duplicate(2000, $1)), - ?line {'EXIT', {badarg, _}} = (catch float(Big)), + Big = list_to_integer(lists:duplicate(2000, $1)), + {'EXIT', {badarg, _}} = (catch float(Big)), %% Invalid types and lists. - ?line {'EXIT', {badarg, _}} = (catch list_to_integer(atom)), - ?line {'EXIT', {badarg, _}} = (catch list_to_integer(123)), - ?line {'EXIT', {badarg, _}} = (catch list_to_integer([$1, [$2]])), - ?line {'EXIT', {badarg, _}} = (catch list_to_integer("1.2")), - ?line {'EXIT', {badarg, _}} = (catch list_to_integer("a")), - ?line {'EXIT', {badarg, _}} = (catch list_to_integer("")), + {'EXIT', {badarg, _}} = (catch list_to_integer(atom)), + {'EXIT', {badarg, _}} = (catch list_to_integer(123)), + {'EXIT', {badarg, _}} = (catch list_to_integer([$1, [$2]])), + {'EXIT', {badarg, _}} = (catch list_to_integer("1.2")), + {'EXIT', {badarg, _}} = (catch list_to_integer("a")), + {'EXIT', {badarg, _}} = (catch list_to_integer("")), ok. %% Tests float_to_list/1. t_float_to_list(Config) when is_list(Config) -> - ?line test_ftl("0.0e+0", 0.0), - ?line test_ftl("2.5e+1", 25.0), - ?line test_ftl("2.5e+0", 2.5), - ?line test_ftl("2.5e-1", 0.25), - ?line test_ftl("-3.5e+17", -350.0e15), + test_ftl("0.0e+0", 0.0), + test_ftl("2.5e+1", 25.0), + test_ftl("2.5e+0", 2.5), + test_ftl("2.5e-1", 0.25), + test_ftl("-3.5e+17", -350.0e15), ok. test_ftl(Expect, Float) -> - %% No ?line on the next line -- we want the line number from t_float_to_list. + %% No on the next line -- we want the line number from t_float_to_list. Expect = remove_zeros(lists:reverse(float_to_list(Float)), []). %% Removes any non-significant zeros in a floating point number. @@ -147,36 +148,36 @@ remove_zeros([], Result) -> %% Tests integer_to_list/1. t_integer_to_list(Config) when is_list(Config) -> - ?line "0" = integer_to_list(0), - ?line "42" = integer_to_list(42), - ?line "-42" = integer_to_list(-42), - ?line "-42" = integer_to_list(-42), - ?line "32768" = integer_to_list(32768), - ?line "268435455" = integer_to_list(268435455), - ?line "-268435455" = integer_to_list(-268435455), - ?line "123456932798748738738" = integer_to_list(123456932798748738738), - ?line Big_List = lists:duplicate(2000, $1), - ?line Big = list_to_integer(Big_List), - ?line Big_List = integer_to_list(Big), + "0" = integer_to_list(0), + "42" = integer_to_list(42), + "-42" = integer_to_list(-42), + "-42" = integer_to_list(-42), + "32768" = integer_to_list(32768), + "268435455" = integer_to_list(268435455), + "-268435455" = integer_to_list(-268435455), + "123456932798748738738" = integer_to_list(123456932798748738738), + Big_List = lists:duplicate(2000, $1), + Big = list_to_integer(Big_List), + Big_List = integer_to_list(Big), ok. %% Tests list_to_float/1. t_list_to_float_safe(Config) when is_list(Config) -> - ?line 0.0 = list_to_float("0.0"), - ?line 0.0 = list_to_float("-0.0"), - ?line 0.5 = list_to_float("0.5"), - ?line -0.5 = list_to_float("-0.5"), - ?line 100.0 = list_to_float("1.0e2"), - ?line 127.5 = list_to_float("127.5"), - ?line -199.5 = list_to_float("-199.5"), - - ?line {'EXIT', {badarg, _}} = (catch list_to_float("0")), - ?line {'EXIT', {badarg, _}} = (catch list_to_float("0..0")), - ?line {'EXIT', {badarg, _}} = (catch list_to_float("0e12")), - ?line {'EXIT', {badarg, _}} = (catch list_to_float("--0.0")), -%% ?line {'EXIT', {badarg, _}} = (catch list_to_float("0.0e+99999999")), + 0.0 = list_to_float("0.0"), + 0.0 = list_to_float("-0.0"), + 0.5 = list_to_float("0.5"), + -0.5 = list_to_float("-0.5"), + 100.0 = list_to_float("1.0e2"), + 127.5 = list_to_float("127.5"), + -199.5 = list_to_float("-199.5"), + + {'EXIT', {badarg, _}} = (catch list_to_float("0")), + {'EXIT', {badarg, _}} = (catch list_to_float("0..0")), + {'EXIT', {badarg, _}} = (catch list_to_float("0e12")), + {'EXIT', {badarg, _}} = (catch list_to_float("--0.0")), +%% {'EXIT', {badarg, _}} = (catch list_to_float("0.0e+99999999")), ok. @@ -184,101 +185,101 @@ t_list_to_float_safe(Config) when is_list(Config) -> %% (Known to crash the Unix version of Erlang 4.4.1) t_list_to_float_risky(Config) when is_list(Config) -> - ?line Many_Ones = lists:duplicate(25000, $1), - ?line _ = list_to_float("2."++Many_Ones), - ?line {'EXIT', {badarg, _}} = (catch list_to_float("2"++Many_Ones)), + Many_Ones = lists:duplicate(25000, $1), + _ = list_to_float("2."++Many_Ones), + {'EXIT', {badarg, _}} = (catch list_to_float("2"++Many_Ones)), ok. %% Tests list_to_integer/1. t_list_to_integer(Config) when is_list(Config) -> - ?line 0 = list_to_integer("0"), - ?line 0 = list_to_integer("00"), - ?line 0 = list_to_integer("-0"), - ?line 1 = list_to_integer("1"), - ?line -1 = list_to_integer("-1"), - ?line 42 = list_to_integer("42"), - ?line -12 = list_to_integer("-12"), - ?line 32768 = list_to_integer("32768"), - ?line 268435455 = list_to_integer("268435455"), - ?line -268435455 = list_to_integer("-268435455"), + 0 = list_to_integer("0"), + 0 = list_to_integer("00"), + 0 = list_to_integer("-0"), + 1 = list_to_integer("1"), + -1 = list_to_integer("-1"), + 42 = list_to_integer("42"), + -12 = list_to_integer("-12"), + 32768 = list_to_integer("32768"), + 268435455 = list_to_integer("268435455"), + -268435455 = list_to_integer("-268435455"), %% Bignums. - ?line 123456932798748738738 = list_to_integer("123456932798748738738"), - ?line _ = list_to_integer(lists:duplicate(2000, $1)), + 123456932798748738738 = list_to_integer("123456932798748738738"), + _ = list_to_integer(lists:duplicate(2000, $1)), ok. %% Tests round/1. t_round(Config) when is_list(Config) -> - ?line 0 = round(0.0), - ?line 0 = round(0.4), - ?line 1 = round(0.5), - ?line 0 = round(-0.4), - ?line -1 = round(-0.5), - ?line 255 = round(255.3), - ?line 256 = round(255.6), - ?line -1033 = round(-1033.3), - ?line -1034 = round(-1033.6), + 0 = round(0.0), + 0 = round(0.4), + 1 = round(0.5), + 0 = round(-0.4), + -1 = round(-0.5), + 255 = round(255.3), + 256 = round(255.6), + -1033 = round(-1033.3), + -1034 = round(-1033.6), % OTP-3722: - ?line X = (1 bsl 27) - 1, - ?line MX = -X, - ?line MXm1 = -X-1, - ?line MXp1 = -X+1, - ?line F = X + 0.0, - ?line X = round(F), - ?line X = round(F+1)-1, - ?line X = round(F-1)+1, - ?line MX = round(-F), - ?line MXm1 = round(-F-1), - ?line MXp1 = round(-F+1), - - ?line X = round(F+0.1), - ?line X = round(F+1+0.1)-1, - ?line X = round(F-1+0.1)+1, - ?line MX = round(-F+0.1), - ?line MXm1 = round(-F-1+0.1), - ?line MXp1 = round(-F+1+0.1), - - ?line X = round(F-0.1), - ?line X = round(F+1-0.1)-1, - ?line X = round(F-1-0.1)+1, - ?line MX = round(-F-0.1), - ?line MXm1 = round(-F-1-0.1), - ?line MXp1 = round(-F+1-0.1), - - ?line 0.5 = abs(round(F+0.5)-(F+0.5)), - ?line 0.5 = abs(round(F-0.5)-(F-0.5)), - ?line 0.5 = abs(round(-F-0.5)-(-F-0.5)), - ?line 0.5 = abs(round(-F+0.5)-(-F+0.5)), + X = (1 bsl 27) - 1, + MX = -X, + MXm1 = -X-1, + MXp1 = -X+1, + F = X + 0.0, + X = round(F), + X = round(F+1)-1, + X = round(F-1)+1, + MX = round(-F), + MXm1 = round(-F-1), + MXp1 = round(-F+1), + + X = round(F+0.1), + X = round(F+1+0.1)-1, + X = round(F-1+0.1)+1, + MX = round(-F+0.1), + MXm1 = round(-F-1+0.1), + MXp1 = round(-F+1+0.1), + + X = round(F-0.1), + X = round(F+1-0.1)-1, + X = round(F-1-0.1)+1, + MX = round(-F-0.1), + MXm1 = round(-F-1-0.1), + MXp1 = round(-F+1-0.1), + + 0.5 = abs(round(F+0.5)-(F+0.5)), + 0.5 = abs(round(F-0.5)-(F-0.5)), + 0.5 = abs(round(-F-0.5)-(-F-0.5)), + 0.5 = abs(round(-F+0.5)-(-F+0.5)), %% Bignums. - ?line 4294967296 = round(4294967296.1), - ?line 4294967297 = round(4294967296.9), - ?line -4294967296 = -round(4294967296.1), - ?line -4294967297 = -round(4294967296.9), + 4294967296 = round(4294967296.1), + 4294967297 = round(4294967296.9), + -4294967296 = -round(4294967296.1), + -4294967297 = -round(4294967296.9), ok. t_trunc(Config) when is_list(Config) -> - ?line 0 = trunc(0.0), - ?line 5 = trunc(5.3333), - ?line -10 = trunc(-10.978987), + 0 = trunc(0.0), + 5 = trunc(5.3333), + -10 = trunc(-10.978987), % The largest smallnum, converted to float (OTP-3722): - ?line X = (1 bsl 27) - 1, - ?line F = X + 0.0, + X = (1 bsl 27) - 1, + F = X + 0.0, io:format("X = ~p/~w/~w, F = ~p/~w/~w, trunc(F) = ~p/~w/~w~n", [X, X, binary_to_list(term_to_binary(X)), F, F, binary_to_list(term_to_binary(F)), trunc(F), trunc(F), binary_to_list(term_to_binary(trunc(F)))]), - ?line X = trunc(F), - ?line X = trunc(F+1)-1, - ?line X = trunc(F-1)+1, - ?line X = -trunc(-F), - ?line X = -trunc(-F-1)-1, - ?line X = -trunc(-F+1)+1, + X = trunc(F), + X = trunc(F+1)-1, + X = trunc(F-1)+1, + X = -trunc(-F), + X = -trunc(-F-1)-1, + X = -trunc(-F+1)+1, %% Bignums. - ?line 4294967305 = trunc(4294967305.7), - ?line -4294967305 = trunc(-4294967305.7), + 4294967305 = trunc(4294967305.7), + -4294967305 = trunc(-4294967305.7), ok. diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl index 00a6e900d4..8304672558 100644 --- a/lib/compiler/test/receive_SUITE.erl +++ b/lib/compiler/test/receive_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -26,18 +27,17 @@ export/1,recv/1,coverage/1,otp_7980/1,ref_opt/1, wait/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). init_per_testcase(_Case, Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(2)), - [{watchdog, Dog}|Config]. + Config. -end_per_testcase(_Case, Config) -> - Dog=?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> test_lib:recompile(?MODULE), @@ -63,21 +63,21 @@ end_per_group(_GroupName, Config) -> -record(state, {ena = true}). recv(Config) when is_list(Config) -> - ?line Pid = spawn_link(fun() -> loop(#state{}) end), + Pid = spawn_link(fun() -> loop(#state{}) end), Self = self(), - ?line Pid ! {Self,test}, + Pid ! {Self,test}, receive {ok,test} -> ok; {error,Other} -> io:format("Got unpexected ~p", [Other]), - ?line ?t:fail() + ct:fail(unexpected) after 10000 -> - ?line ?t:fail(no_answer) + ct:fail(no_answer) end, receive X -> io:format("Unexpected extra message: ~p", [X]), - ?line ?t:fail() + ct:fail(unexpected) after 10 -> ok end, @@ -115,11 +115,17 @@ coverage(Config) when is_list(Config) -> self() ! 17, self() ! 19, - ?line 59 = tuple_to_values(infinity, x), - ?line 61 = tuple_to_values(999999, x), - ?line 0 = tuple_to_values(1, x), + 59 = tuple_to_values(infinity, x), + 61 = tuple_to_values(999999, x), + 0 = tuple_to_values(1, x), + + {'EXIT',{{badmap,[]},_}} = (catch monitor_plus_badmap(self())), + ok. +monitor_plus_badmap(Pid) -> + monitor(process, Pid) + []#{}. + receive_all() -> receive Any -> @@ -187,12 +193,13 @@ ref_opt(Config) when is_list(Config) -> end. ref_opt_1(Config) -> - ?line DataDir = ?config(data_dir, Config), - ?line PrivDir = ?config(priv_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.{erl,S}"])), - ?line test_lib:p_run(fun(Src) -> - do_ref_opt(Src, PrivDir) - end, Sources), + test_lib:p_run(fun(Src) -> + do_ref_opt(Src, PrivDir) + end, Sources), + cover_recv_instructions(), ok. do_ref_opt(Source, PrivDir) -> @@ -202,9 +209,9 @@ do_ref_opt(Source, PrivDir) -> {outdir,PrivDir}] ++ [from_asm || Ext =:= ".S" ]), Base = filename:rootname(filename:basename(Source), Ext), - code:purge(list_to_atom(Base)), - BeamFile = filename:join(PrivDir, Base), - code:load_abs(BeamFile), + code:purge(list_to_atom(Base)), + BeamFile = filename:join(PrivDir, Base), + code:load_abs(BeamFile), ok = Mod:Mod(), {beam_file,Mod,_,_,_,Code} = beam_disasm:file(BeamFile), case Base of @@ -232,11 +239,32 @@ collect_recv_opt_instrs(Code) -> end] || {function,_,_,_,Is} <- Code], lists:append(L). +cover_recv_instructions() -> + %% We want to cover the handling of recv_mark and recv_set in beam_utils. + %% Since those instructions are introduced in a late optimization pass, + %% beam_utils:live_opt() will not see them unless the compilation is + %% started from a .S file. The compile_SUITE:asm/1 test case will + %% compile all test suite files to .S and then run them through the + %% compiler again. + %% + %% Here will we will ensure that this modules contains recv_mark + %% and recv_set instructions. + Pid = spawn_link(fun() -> + receive {Parent,Ref} -> + Parent ! Ref + end + end), + Ref = make_ref(), + Pid ! {self(),Ref}, + receive + Ref -> ok + end. + export(Config) when is_list(Config) -> Ref = make_ref(), - ?line self() ! {result,Ref,42}, - ?line 42 = export_1(Ref), - ?line {error,timeout} = export_1(Ref), + self() ! {result,Ref,42}, + 42 = export_1(Ref), + {error,timeout} = export_1(Ref), ok. export_1(Reference) -> diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl index f736e14bf6..5546765f26 100644 --- a/lib/compiler/test/record_SUITE.erl +++ b/lib/compiler/test/record_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2013. All Rights Reserved. +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -20,25 +21,24 @@ -module(record_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, errors/1,record_test_2/1,record_test_3/1,record_access_in_guards/1, guard_opt/1,eval_once/1,foobar/1,missing_test_heap/1, - nested_access/1,coverage/1]). + nested_access/1,coverage/1,grab_bag/1]). init_per_testcase(_Case, Config) -> - ?line Dog = test_server:timetrap(test_server:minutes(2)), - [{watchdog,Dog}|Config]. + Config. -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> test_lib:recompile(?MODULE), @@ -48,7 +48,7 @@ groups() -> [{p,test_lib:parallel(), [errors,record_test_2,record_test_3, record_access_in_guards,guard_opt,eval_once,foobar, - missing_test_heap,nested_access,coverage]}]. + missing_test_heap,nested_access,coverage,grab_bag]}]. init_per_suite(Config) -> @@ -70,18 +70,18 @@ end_per_group(_GroupName, Config) -> errors(Config) when is_list(Config) -> Foo = #foo{a=1,b=2,c=3,d=4}, - ?line #foo{a=19,b=42,c=3,d=4} = update_foo(Foo, 19, 42), - - ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19)), - ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35)), - ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17)), - ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17, 42)), - - ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19)), - ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35)), - ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17)), - ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17, 42)), - ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, + #foo{a=19,b=42,c=3,d=4} = update_foo(Foo, 19, 42), + + {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19)), + {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35)), + {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17)), + {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17, 42)), + + {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19)), + {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35)), + {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17)), + {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17, 42)), + {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17, 42, -2)), ok. @@ -117,72 +117,72 @@ update_foo_barf(#foo{}=R, A, _B, C, D, E) -> R#barf{a=A,b=A,c=C,d=D,e=E}. --define(TrueGuard(Expr), if Expr -> ok; true -> ?t:fail() end). --define(FalseGuard(Expr), if Expr -> ?t:fail(); true -> ok end). +-define(TrueGuard(Expr), if Expr -> ok; true -> ct:fail(failed) end). +-define(FalseGuard(Expr), if Expr -> ct:fail(failed); true -> ok end). record_test_2(Config) when is_list(Config) -> - ?line true = is_record(#foo{}, foo), - ?line false = is_record(#foo{}, barf), - ?line false = is_record({foo}, foo), + true = is_record(#foo{}, foo), + false = is_record(#foo{}, barf), + false = is_record({foo}, foo), - ?line true = erlang:is_record(#foo{}, foo), - ?line false = erlang:is_record(#foo{}, barf), - ?line false = erlang:is_record({foo}, foo), + true = erlang:is_record(#foo{}, foo), + false = erlang:is_record(#foo{}, barf), + false = erlang:is_record({foo}, foo), - ?line false = is_record([], foo), - ?line false = is_record(Config, foo), + false = is_record([], foo), + false = is_record(Config, foo), - ?line ?TrueGuard(is_record(#foo{}, foo)), - ?line ?FalseGuard(is_record(#foo{}, barf)), - ?line ?FalseGuard(is_record({foo}, foo)), + ?TrueGuard(is_record(#foo{}, foo)), + ?FalseGuard(is_record(#foo{}, barf)), + ?FalseGuard(is_record({foo}, foo)), - ?line ?TrueGuard(erlang:is_record(#foo{}, foo)), - ?line ?FalseGuard(erlang:is_record(#foo{}, barf)), - ?line ?FalseGuard(erlang:is_record({foo}, foo)), + ?TrueGuard(erlang:is_record(#foo{}, foo)), + ?FalseGuard(erlang:is_record(#foo{}, barf)), + ?FalseGuard(erlang:is_record({foo}, foo)), - ?line ?FalseGuard(is_record([], foo)), - ?line ?FalseGuard(is_record(Config, foo)), + ?FalseGuard(is_record([], foo)), + ?FalseGuard(is_record(Config, foo)), %% 'not is_record/2' to test guard optimization. - ?line ?FalseGuard(not is_record(#foo{}, foo)), - ?line ?TrueGuard(not is_record(#foo{}, barf)), - ?line ?TrueGuard(not is_record({foo}, foo)), + ?FalseGuard(not is_record(#foo{}, foo)), + ?TrueGuard(not is_record(#foo{}, barf)), + ?TrueGuard(not is_record({foo}, foo)), - ?line ?FalseGuard(not erlang:is_record(#foo{}, foo)), - ?line ?TrueGuard(not erlang:is_record(#foo{}, barf)), - ?line ?TrueGuard(not erlang:is_record({foo}, foo)), + ?FalseGuard(not erlang:is_record(#foo{}, foo)), + ?TrueGuard(not erlang:is_record(#foo{}, barf)), + ?TrueGuard(not erlang:is_record({foo}, foo)), Foo = id(#foo{}), - ?line ?FalseGuard(not erlang:is_record(Foo, foo)), - ?line ?TrueGuard(not erlang:is_record(Foo, barf)), + ?FalseGuard(not erlang:is_record(Foo, foo)), + ?TrueGuard(not erlang:is_record(Foo, barf)), - ?line ?TrueGuard(not is_record(Config, foo)), + ?TrueGuard(not is_record(Config, foo)), - ?line ?TrueGuard(not is_record(a, foo)), - ?line ?TrueGuard(not is_record([], foo)), + ?TrueGuard(not is_record(a, foo)), + ?TrueGuard(not is_record([], foo)), %% Pass non-literal first argument. - ?line true = is_record(id(#foo{}), foo), - ?line false = is_record(id(#foo{}), barf), - ?line false = is_record(id({foo}), foo), + true = is_record(id(#foo{}), foo), + false = is_record(id(#foo{}), barf), + false = is_record(id({foo}), foo), - ?line true = erlang:is_record(id(#foo{}), foo), - ?line false = erlang:is_record(id(#foo{}), barf), - ?line false = erlang:is_record(id({foo}), foo), + true = erlang:is_record(id(#foo{}), foo), + false = erlang:is_record(id(#foo{}), barf), + false = erlang:is_record(id({foo}), foo), NoRec1 = id(blurf), NoRec2 = id([]), - ?line ?TrueGuard(not is_record(NoRec1, foo)), - ?line ?TrueGuard(not is_record(NoRec2, foo)), + ?TrueGuard(not is_record(NoRec1, foo)), + ?TrueGuard(not is_record(NoRec2, foo)), %% The optimizer attempts to move expressions to guards, %% but it must not move an is_record/2 call that is not %% allowed in a guard in the first place. - ?line ok = case is_record(id({a}), id(a)) of + ok = case is_record(id({a}), id(a)) of true -> ok; false -> error end, @@ -190,184 +190,192 @@ record_test_2(Config) when is_list(Config) -> %% Force the use of guard bifs by using the 'xor' operation. False = id(false), - ?line ?TrueGuard(is_record(#foo{}, foo) xor False), - ?line ?FalseGuard(is_record(#foo{}, barf) xor False), - ?line ?FalseGuard(is_record({foo}, foo) xor False ), + ?TrueGuard(is_record(#foo{}, foo) xor False), + ?FalseGuard(is_record(#foo{}, barf) xor False), + ?FalseGuard(is_record({foo}, foo) xor False ), - ?line ?TrueGuard(is_record(Foo, foo) xor False), - ?line ?FalseGuard(is_record(Foo, barf) xor False), + ?TrueGuard(is_record(Foo, foo) xor False), + ?FalseGuard(is_record(Foo, barf) xor False), %% Implicit guards by using a list comprehension. List = id([1,#foo{a=2},3,#bar{d=4},5,#foo{a=6},7]), - ?line [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo)], - ?line [#bar{d=4}] = [X || X <- List, is_record(X, bar)], - ?line [1,#foo{a=2},3,5,#foo{a=6},7] = + [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo)], + [#bar{d=4}] = [X || X <- List, is_record(X, bar)], + [1,#foo{a=2},3,5,#foo{a=6},7] = [X || X <- List, not is_record(X, bar)], - ?line [1,3,5,7] = + [1,3,5,7] = [X || X <- List, ((not is_record(X, bar)) and (not is_record(X, foo)))], - ?line [#foo{a=2},#bar{d=4},#foo{a=6}] = + [#foo{a=2},#bar{d=4},#foo{a=6}] = [X || X <- List, ((is_record(X, bar)) or (is_record(X, foo)))], - ?line [1,3,#bar{d=4}] = + [1,3,#bar{d=4}] = [X || X <- List, ((is_record(X, bar)) or (X < 5))], - ?line MyList = [#foo{a=3},x,[],{a,b}], - ?line [#foo{a=3}] = [X || X <- MyList, is_record(X, foo)], - ?line [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo)], - ?line [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo) end], - ?line [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo) end], - ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo) or - not is_binary(X)], - ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or - not is_binary(X)], - ?line [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)], - ?line [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or - is_reference(X)], - ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, - begin is_record(X, foo) or - not is_binary(X) end], - ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, - begin not is_record(X, foo) or - not is_binary(X) end], - ?line [#foo{a=3}] = [X || X <- MyList, - begin is_record(X, foo) or is_reference(X) end], - ?line [x,[],{a,b}] = [X || X <- MyList, - begin not is_record(X, foo) or - is_reference(X) end], + MyList = [#foo{a=3},x,[],{a,b}], + [#foo{a=3}] = [X || X <- MyList, is_record(X, foo)], + [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo)], + [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo) end], + [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo) end], + [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo) or + not is_binary(X)], + [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or + not is_binary(X)], + [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)], + [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or + is_reference(X)], + [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, + begin is_record(X, foo) or + not is_binary(X) end], + [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, + begin not is_record(X, foo) or + not is_binary(X) end], + [#foo{a=3}] = [X || X <- MyList, + begin is_record(X, foo) or is_reference(X) end], + [x,[],{a,b}] = [X || X <- MyList, + begin not is_record(X, foo) or + is_reference(X) end], %% Call is_record/2 with illegal arguments. - ?line [] = [X || X <- [], is_record(t, id(X))], - ?line {'EXIT',{badarg,_}} = (catch [X || X <- [1], is_record(t, id(X))]), + [] = [X || X <- [], is_record(t, id(X))], + {'EXIT',{badarg,_}} = (catch [X || X <- [1], is_record(t, id(X))]), %% Update several fields with a string literal. - ?line #barf{} = Barf0 = id(#barf{}), - ?line Barf = update_barf(Barf0), - ?line #barf{a="abc",b=1} = id(Barf), + #barf{} = Barf0 = id(#barf{}), + Barf = update_barf(Barf0), + #barf{a="abc",b=1} = id(Barf), + + %% Test optimization of is_record/3. + false = case id({a,b}) of + {_,_}=Tuple -> is_record(Tuple, foo) + end, + false = case id(true) of + true=Bool -> is_record(Bool, foo) + end, ok. record_test_3(Config) when is_list(Config) -> - ?line true = is_record(#foo{}, foo, 5), - ?line false = is_record(#foo{}, barf, 5), - ?line false = is_record(#foo{}, barf, 6), - ?line false = is_record({foo}, foo, 5), + true = is_record(#foo{}, foo, 5), + false = is_record(#foo{}, barf, 5), + false = is_record(#foo{}, barf, 6), + false = is_record({foo}, foo, 5), - ?line true = erlang:is_record(#foo{}, foo, 5), - ?line false = erlang:is_record(#foo{}, barf, 5), - ?line false = erlang:is_record({foo}, foo, 5), + true = erlang:is_record(#foo{}, foo, 5), + false = erlang:is_record(#foo{}, barf, 5), + false = erlang:is_record({foo}, foo, 5), - ?line false = is_record([], foo), - ?line false = is_record(Config, foo), + false = is_record([], foo), + false = is_record(Config, foo), - ?line ?TrueGuard(is_record(#foo{}, foo, 5)), - ?line ?FalseGuard(is_record(#foo{}, barf, 5)), - ?line ?FalseGuard(is_record(#foo{}, barf, 6)), - ?line ?FalseGuard(is_record({foo}, foo, 5)), + ?TrueGuard(is_record(#foo{}, foo, 5)), + ?FalseGuard(is_record(#foo{}, barf, 5)), + ?FalseGuard(is_record(#foo{}, barf, 6)), + ?FalseGuard(is_record({foo}, foo, 5)), - ?line ?TrueGuard(erlang:is_record(#foo{}, foo, 5)), - ?line ?FalseGuard(erlang:is_record(#foo{}, barf, 5)), - ?line ?FalseGuard(erlang:is_record(#foo{}, barf, 6)), - ?line ?FalseGuard(erlang:is_record({foo}, foo, 5)), + ?TrueGuard(erlang:is_record(#foo{}, foo, 5)), + ?FalseGuard(erlang:is_record(#foo{}, barf, 5)), + ?FalseGuard(erlang:is_record(#foo{}, barf, 6)), + ?FalseGuard(erlang:is_record({foo}, foo, 5)), - ?line ?FalseGuard(is_record([], foo, 5)), - ?line ?FalseGuard(is_record(Config, foo, 5)), + ?FalseGuard(is_record([], foo, 5)), + ?FalseGuard(is_record(Config, foo, 5)), %% 'not is_record/2' to test guard optimization. - ?line ?FalseGuard(not is_record(#foo{}, foo, 5)), - ?line ?TrueGuard(not is_record(#foo{}, barf, 6)), - ?line ?TrueGuard(not is_record({foo}, foo, 5)), + ?FalseGuard(not is_record(#foo{}, foo, 5)), + ?TrueGuard(not is_record(#foo{}, barf, 6)), + ?TrueGuard(not is_record({foo}, foo, 5)), - ?line ?FalseGuard(not erlang:is_record(#foo{}, foo, 5)), - ?line ?TrueGuard(not erlang:is_record(#foo{}, barf, 5)), - ?line ?TrueGuard(not erlang:is_record({foo}, foo, 5)), + ?FalseGuard(not erlang:is_record(#foo{}, foo, 5)), + ?TrueGuard(not erlang:is_record(#foo{}, barf, 5)), + ?TrueGuard(not erlang:is_record({foo}, foo, 5)), Foo = id(#foo{}), - ?line ?FalseGuard(not erlang:is_record(Foo, foo, 5)), - ?line ?TrueGuard(not erlang:is_record(Foo, barf, 6)), + ?FalseGuard(not erlang:is_record(Foo, foo, 5)), + ?TrueGuard(not erlang:is_record(Foo, barf, 6)), - ?line ?TrueGuard(not is_record(Config, foo, 5)), + ?TrueGuard(not is_record(Config, foo, 5)), - ?line ?TrueGuard(not is_record(a, foo, 5)), - ?line ?TrueGuard(not is_record([], foo, 5)), + ?TrueGuard(not is_record(a, foo, 5)), + ?TrueGuard(not is_record([], foo, 5)), %% Pass non-literal first argument. - ?line true = is_record(id(#foo{}), foo, 5), - ?line false = is_record(id(#foo{}), barf, 6), - ?line false = is_record(id({foo}), foo, 5), + true = is_record(id(#foo{}), foo, 5), + false = is_record(id(#foo{}), barf, 6), + false = is_record(id({foo}), foo, 5), - ?line true = erlang:is_record(id(#foo{}), foo, 5), - ?line false = erlang:is_record(id(#foo{}), barf, 6), - ?line false = erlang:is_record(id({foo}), foo, 5), + true = erlang:is_record(id(#foo{}), foo, 5), + false = erlang:is_record(id(#foo{}), barf, 6), + false = erlang:is_record(id({foo}), foo, 5), NoRec1 = id(blurf), NoRec2 = id([]), - ?line ?TrueGuard(not is_record(NoRec1, foo, 5)), - ?line ?TrueGuard(not is_record(NoRec2, foo, 5)), + ?TrueGuard(not is_record(NoRec1, foo, 5)), + ?TrueGuard(not is_record(NoRec2, foo, 5)), %% Force the use of guard bifs by using the 'xor' operation. False = id(false), - ?line ?TrueGuard(is_record(#foo{}, foo, 5) xor False), - ?line ?FalseGuard(is_record(#foo{}, barf, 6) xor False), - ?line ?FalseGuard(is_record({foo}, foo, 5) xor False ), + ?TrueGuard(is_record(#foo{}, foo, 5) xor False), + ?FalseGuard(is_record(#foo{}, barf, 6) xor False), + ?FalseGuard(is_record({foo}, foo, 5) xor False ), - ?line ?TrueGuard(is_record(Foo, foo, 5) xor False), - ?line ?FalseGuard(is_record(Foo, barf, 6) xor False), + ?TrueGuard(is_record(Foo, foo, 5) xor False), + ?FalseGuard(is_record(Foo, barf, 6) xor False), %% Implicit guards by using a list comprehension. List = id([1,#foo{a=2},3,#bar{d=4},5,#foo{a=6},7]), - ?line [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo, 5)], - ?line [#bar{d=4}] = [X || X <- List, is_record(X, bar, 5)], - ?line [1,#foo{a=2},3,5,#foo{a=6},7] = + [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo, 5)], + [#bar{d=4}] = [X || X <- List, is_record(X, bar, 5)], + [1,#foo{a=2},3,5,#foo{a=6},7] = [X || X <- List, not is_record(X, bar, 5)], - ?line [1,3,5,7] = + [1,3,5,7] = [X || X <- List, ((not is_record(X, bar, 5)) and (not is_record(X, foo, 5)))], - ?line [#foo{a=2},#bar{d=4},#foo{a=6}] = + [#foo{a=2},#bar{d=4},#foo{a=6}] = [X || X <- List, ((is_record(X, bar, 5)) or (is_record(X, foo, 5)))], - ?line [1,3,#bar{d=4}] = + [1,3,#bar{d=4}] = [X || X <- List, ((is_record(X, bar, 5)) or (X < 5))], - ?line MyList = [#foo{a=3},x,[],{a,b}], - ?line [#foo{a=3}] = [X || X <- MyList, is_record(X, foo, 5)], - ?line [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo, 5)], - ?line [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo, 5) end], - ?line [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo, 5) end], - ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo, 5) or - not is_binary(X)], - ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo, 5) or - not is_binary(X)], - ?line [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)], - ?line [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or - is_reference(X)], - ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, - begin is_record(X, foo, 5) or - not is_binary(X) end], - ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, - begin not is_record(X, foo, 5) or - not is_binary(X) end], - ?line [#foo{a=3}] = [X || X <- MyList, - begin is_record(X, foo, 5) or is_reference(X) end], - ?line [x,[],{a,b}] = [X || X <- MyList, - begin not is_record(X, foo, 5) or - is_reference(X) end], + MyList = [#foo{a=3},x,[],{a,b}], + [#foo{a=3}] = [X || X <- MyList, is_record(X, foo, 5)], + [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo, 5)], + [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo, 5) end], + [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo, 5) end], + [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo, 5) or + not is_binary(X)], + [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo, 5) or + not is_binary(X)], + [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)], + [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or + is_reference(X)], + [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, + begin is_record(X, foo, 5) or + not is_binary(X) end], + [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, + begin not is_record(X, foo, 5) or + not is_binary(X) end], + [#foo{a=3}] = [X || X <- MyList, + begin is_record(X, foo, 5) or is_reference(X) end], + [x,[],{a,b}] = [X || X <- MyList, + begin not is_record(X, foo, 5) or + is_reference(X) end], %% Update several fields with a string literal. - ?line #barf{} = Barf0 = id(#barf{}), - ?line Barf = update_barf(Barf0), - ?line #barf{a="abc",b=1} = id(Barf), + #barf{} = Barf0 = id(#barf{}), + Barf = update_barf(Barf0), + #barf{a="abc",b=1} = id(Barf), %% Non-literal arguments. - ?line true = is_record(id(#barf{}), id(barf), id(6)), - ?line false = is_record(id(#barf{}), id(barf), id(42)), - ?line false = is_record(id(#barf{}), id(foo), id(6)), + true = is_record(id(#barf{}), id(barf), id(6)), + false = is_record(id(#barf{}), id(barf), id(42)), + false = is_record(id(#barf{}), id(foo), id(6)), Rec = id(#barf{}), Good = id(barf), @@ -380,15 +388,15 @@ record_test_3(Config) when is_list(Config) -> ok. record_access_in_guards(Config) when is_list(Config) -> - ?line Priv = ?config(priv_dir, Config), - ?line file:set_cwd(test_lib:get_data_dir(Config)), - ?line Opts0 = [{outdir,Priv},report_errors|test_lib:opt_opts(?MODULE)], + Priv = proplists:get_value(priv_dir, Config), + file:set_cwd(test_lib:get_data_dir(Config)), + Opts0 = [{outdir,Priv},report_errors|test_lib:opt_opts(?MODULE)], M = record_access_in_guards, Opts = [strict_record_tests|Opts0], - ?line io:format("Options: ~p\n", [Opts]), - ?line {ok,M} = c:c(M, Opts), - ?line ok = M:t(), + io:format("Options: ~p\n", [Opts]), + {ok,M} = c:c(M, Opts), + ok = M:t(), ok. @@ -478,19 +486,19 @@ update_barf(R) -> R#barf{a="abc",b=1}. eval_once(Config) when is_list(Config) -> - ?line once(fun(GetRec) -> + once(fun(GetRec) -> true = erlang:is_record(GetRec(), foo) end, #foo{}), - ?line once(fun(GetRec) -> + once(fun(GetRec) -> (GetRec())#foo{a=1} end, #foo{}), - ?line once(fun(GetRec) -> + once(fun(GetRec) -> (GetRec())#foo{a=1,b=2} end, #foo{}), - ?line once(fun(GetRec) -> + once(fun(GetRec) -> (GetRec())#foo{a=1,b=2,c=3} end, #foo{}), - ?line once(fun(GetRec) -> + once(fun(GetRec) -> (GetRec())#foo{a=1,b=2,c=3,d=4} end, #foo{}), ok. @@ -506,7 +514,7 @@ once(Test, Record) -> 1 -> ok; N -> io:format("Evaluated ~w times\n", [N]), - ?t:fail() + ct:fail(more_than_once) end, Result. @@ -562,21 +570,21 @@ nested_access(Config) when is_list(Config) -> N0 = #nrec0{}, N1 = #nrec1{}, N2 = #nrec2{}, - ?line <<"nested0">> = N0#nrec0.name, - ?line <<"nested1">> = N1#nrec1.name, - ?line <<"nested2">> = N2#nrec2.name, - ?line <<"nested0">> = N1#nrec1.nrec0#nrec0.name, - ?line <<"nested0">> = N2#nrec2.nrec1#nrec1.nrec0#nrec0.name, - ?line <<"nested1">> = N2#nrec2.nrec1#nrec1.name, - ?line <<"nested0">> = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0.name, + <<"nested0">> = N0#nrec0.name, + <<"nested1">> = N1#nrec1.name, + <<"nested2">> = N2#nrec2.name, + <<"nested0">> = N1#nrec1.nrec0#nrec0.name, + <<"nested0">> = N2#nrec2.nrec1#nrec1.nrec0#nrec0.name, + <<"nested1">> = N2#nrec2.nrec1#nrec1.name, + <<"nested0">> = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0.name, N1a = N2#nrec2.nrec1#nrec1{name = <<"nested1a">>}, - ?line <<"nested1a">> = N1a#nrec1.name, + <<"nested1a">> = N1a#nrec1.name, N2a = N2#nrec2.nrec1#nrec1.nrec0#nrec0{name = <<"nested0a">>}, N2b = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0{name = <<"nested0a">>}, - ?line <<"nested0a">> = N2a#nrec0.name, - ?line N2a = N2b, + <<"nested0a">> = N2a#nrec0.name, + N2a = N2b, ok. -record(rr, {a,b,c}). @@ -593,4 +601,60 @@ coverage(Config) when is_list(Config) -> #rr{a=1,b=2,c=42} = id(R), %Test for correctness. ok. + +-record(default_fun, {a = fun(X) -> X*X end}). + +%% compiler treats records with 1 and 2 fields differently... +-record(gb_nil, {}). +-record(gb_foo, {hello=1}). +-record(gb_bar, {hello=2,there=3}). + +%% Taken from compilation_SUITE. +grab_bag(_Config) -> + T1 = fun() -> + X = #foo{}, + Y = #foo{}, + {X#foo.a == Y#foo.a,X#foo.b} + end, + {true,undefined} = T1(), + + T2 = fun(X, Y) -> + first_arg(X#foo.a =/= Y#foo.a, X#foo.b =/= X#foo.b) + end, + true = T2(#foo{a=x,b=z}, #foo{a=y,b=z}), + + T3 = fun() -> + #default_fun{a=Fun} = id(#default_fun{}), + 9 = Fun(3) + end, + T3(), + + %% Stupid code, but the compiler used to crash. + T4 = fun() -> + F0 = fun() -> + R1 = #gb_nil{}, + R2 = R1#gb_nil{}, + R1 = R2 + end, + F1 = fun() -> + R1 = #gb_foo{}, + R2 = R1#gb_foo{}, + R1 = R2 + end, + + F2 = fun() -> + R1 = #gb_bar{}, + R2 = R1#gb_bar{}, + R1 = R2 + end, + F0(), + F1(), + F2() + end, + T4(), + + ok. + +first_arg(First, _) -> First. + id(I) -> I. diff --git a/lib/compiler/test/record_SUITE_data/record_access_in_guards.erl b/lib/compiler/test/record_SUITE_data/record_access_in_guards.erl index c2b3ec2f34..9b72432246 100644 --- a/lib/compiler/test/record_SUITE_data/record_access_in_guards.erl +++ b/lib/compiler/test/record_SUITE_data/record_access_in_guards.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% diff --git a/lib/compiler/test/regressions_SUITE.erl b/lib/compiler/test/regressions_SUITE.erl new file mode 100644 index 0000000000..7d2c2ac974 --- /dev/null +++ b/lib/compiler/test/regressions_SUITE.erl @@ -0,0 +1,95 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015-2016. 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% + +%% Test specific code snippets that has crashed the compiler in the past. +-module(regressions_SUITE). +-include_lib("common_test/include/ct.hrl"). + +-export([all/0,groups/0,init_per_testcase/2,end_per_testcase/2,suite/0]). +-export([maps/1]). + +groups() -> + [{p,test_lib:parallel(), + [maps]}]. + +init_per_testcase(_Case, Config) -> + Config. + +end_per_testcase(_Case, _Config) -> + ok. + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. + +all() -> + test_lib:recompile(?MODULE), + [{group,p}]. + +%%% test cases + +maps(Config) when is_list(Config) -> + Ts = [{beam_bool_get_elements, + <<"century(#{ron := operator}, _century) -> + if 0.0; _century, _century, _century -> _century end. + ">>}], + ok = run(Config, Ts), + ok. + +%% aux + +run(Config, Tests) -> + F = fun({N,P}) -> + io:format("Compiling test for: ~w~n", [N]), + case catch run_test(Config, P) of + {'EXIT', Reason} -> + io:format("~nTest ~p failed.~nReason: ~p~n", + [N, Reason]), + fail(); + _ -> ok + end + end, + lists:foreach(F, Tests). + + +run_test(Conf, Test0) -> + Module = "regressions_"++test_lib:uniq(), + Filename = Module ++ ".erl", + DataDir = proplists:get_value(priv_dir, Conf), + Test = ["-module(", Module, "). ", Test0], + File = filename:join(DataDir, Filename), + Def = [binary,export_all,return], + Opts = [ Opt ++ Def || + Opt <- [ [no_postopt], + [no_copt], + [no_postopt,no_copt], + [inline], + [inline,no_postopt], + [] + ]], + ok = file:write_file(File, Test), + lists:foreach(fun(Opt) -> + io:format(" - compiling with ~p~n", [Opt]), + {ok,_M,_Bin,_} = compile:file(File,Opt) + end, Opts), + file:delete(File), + ok. + +fail() -> + ct:fail(failed). diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl index a8befbecd9..d5b79e2357 100644 --- a/lib/compiler/test/test_lib.erl +++ b/lib/compiler/test/test_lib.erl @@ -1,27 +1,30 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2013. All Rights Reserved. +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% -module(test_lib). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -compile({no_auto_import,[binary_part/2]}). --export([recompile/1,parallel/0,uniq/0,opt_opts/1,get_data_dir/1, - smoke_disasm/1,p_run/2,binary_part/2]). +-export([id/1,recompile/1,parallel/0,uniq/0,opt_opts/1,get_data_dir/1, + is_cloned_mod/1,smoke_disasm/1,p_run/2,binary_part/2]). + +id(I) -> I. recompile(Mod) when is_atom(Mod) -> case whereis(cover_server) of @@ -44,24 +47,26 @@ smoke_disasm(File) when is_list(File) -> Res = beam_disasm:file(File), {beam_file,_Mod} = {element(1, Res),element(2, Res)}. +%% If we are running cover, we don't want to run test cases that +%% invokes the compiler in parallel, as doing so would probably +%% be slower than running them sequentially. + parallel() -> - case ?t:is_cover() orelse erlang:system_info(schedulers) =:= 1 of + case test_server:is_cover() orelse erlang:system_info(schedulers) =:= 1 of true -> []; false -> [parallel] end. uniq() -> - U0 = erlang:ref_to_list(make_ref()), - U1 = re:replace(U0, "^#Ref", ""), - U = re:replace(U1, "[^[A-Za-z0-9_]+", "_", [global]), - re:replace(U, "_*$", "", [{return,list}]). + U = erlang:unique_integer([positive]), + "_" ++ integer_to_list(U). %% Retrieve the "interesting" compiler options (options for optimization %% and compatibility) for the given module. opt_opts(Mod) -> Comp = Mod:module_info(compile), - {value,{options,Opts}} = lists:keysearch(options, 1, Comp), + {options,Opts} = lists:keyfind(options, 1, Comp), lists:filter(fun(no_copt) -> true; (no_postopt) -> true; (no_float_opt) -> true; @@ -80,24 +85,41 @@ opt_opts(Mod) -> %% This function retrieves the path to the original data directory. get_data_dir(Config) -> - Data0 = ?config(data_dir, Config), + Data0 = proplists:get_value(data_dir, Config), Opts = [{return,list}], Data1 = re:replace(Data0, "_no_opt_SUITE", "_SUITE", Opts), Data = re:replace(Data1, "_post_opt_SUITE", "_SUITE", Opts), re:replace(Data, "_inline_SUITE", "_SUITE", Opts). +is_cloned_mod(Mod) -> + is_cloned_mod_1(atom_to_list(Mod)). + +%% Test whether Mod is a cloned module. + +is_cloned_mod_1("no_opt_SUITE") -> true; +is_cloned_mod_1("post_opt_SUITE") -> true; +is_cloned_mod_1("inline_SUITE") -> true; +is_cloned_mod_1([_|T]) -> is_cloned_mod_1(T); +is_cloned_mod_1([]) -> false. + %% p_run(fun(Data) -> ok|error, List) -> ok %% Will fail the test case if there were any errors. p_run(Test, List) -> - N = case ?t:is_cover() of + S = erlang:system_info(schedulers), + N = case test_server:is_cover() of false -> - erlang:system_info(schedulers); + S + 1; true -> - %% Cover is running. Using more than one process - %% will probably only slow down compilation. - 1 + %% Cover is running. Using too many processes + %% could slow us down. Measurements on my computer + %% showed that using 4 parallel processes was + %% slightly faster than using 3. Using more than + %% 4 would not buy us much and could actually be + %% slower. + min(S, 4) end, + io:format("p_run: ~p parallel processes\n", [N]), p_run_loop(Test, List, N, [], 0, 0). p_run_loop(_, [], _, [], Errors, Ws) -> @@ -108,7 +130,8 @@ p_run_loop(_, [], _, [], Errors, Ws) -> 1 -> {comment,"1 warning"}; N -> {comment,integer_to_list(N)++" warnings"} end; - N -> ?t:fail({N,errors}) + N -> + ct:fail({N,errors}) end; p_run_loop(Test, [H|T], N, Refs, Errors, Ws) when length(Refs) < N -> {_,Ref} = erlang:spawn_monitor(fun() -> exit(Test(H)) end), diff --git a/lib/compiler/test/trycatch_SUITE.erl b/lib/compiler/test/trycatch_SUITE.erl index 4530d08c77..a591d6cc93 100644 --- a/lib/compiler/test/trycatch_SUITE.erl +++ b/lib/compiler/test/trycatch_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2012. All Rights Reserved. +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -24,9 +25,10 @@ catch_oops/1,after_oops/1,eclectic/1,rethrow/1, nested_of/1,nested_catch/1,nested_after/1, nested_horrid/1,last_call_optimization/1,bool/1, - plain_catch_coverage/1,andalso_orelse/1,get_in_try/1]). + plain_catch_coverage/1,andalso_orelse/1,get_in_try/1, + hockey/1,handle_info/1,catch_in_catch/1,grab_bag/1]). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -35,11 +37,12 @@ all() -> [{group,p}]. groups() -> - [{p,test_lib:parallel(), + [{p,[parallel], [basic,lean_throw,try_of,try_after,catch_oops, after_oops,eclectic,rethrow,nested_of,nested_catch, nested_after,nested_horrid,last_call_optimization, - bool,plain_catch_coverage,andalso_orelse,get_in_try]}]. + bool,plain_catch_coverage,andalso_orelse,get_in_try, + hockey,handle_info,catch_in_catch,grab_bag]}]. init_per_suite(Config) -> @@ -57,32 +60,32 @@ end_per_group(_GroupName, Config) -> basic(Conf) when is_list(Conf) -> - ?line 2 = + 2 = try my_div(4, 2) catch Class:Reason -> {Class,Reason} end, - ?line error = + error = try my_div(1, 0) catch error:badarith -> error end, - ?line error = + error = try 1.0 / zero() catch error:badarith -> error end, - ?line ok = + ok = try my_add(53, atom) catch error:badarith -> ok end, - ?line exit_nisse = + exit_nisse = try exit(nisse) catch exit:nisse -> exit_nisse end, - ?line ok = + ok = try throw(kalle) catch kalle -> ok @@ -91,27 +94,27 @@ basic(Conf) when is_list(Conf) -> %% Try some stuff where the compiler will optimize away the try. V = id({a,variable}), - ?line V = try V catch nisse -> error end, - ?line 42 = try 42 catch nisse -> error end, - ?line [V] = try [V] catch nisse -> error end, - ?line {ok,V} = try {ok,V} catch nisse -> error end, + V = try V catch nisse -> error end, + 42 = try 42 catch nisse -> error end, + [V] = try [V] catch nisse -> error end, + {ok,V} = try {ok,V} catch nisse -> error end, %% Same idea, but use an after too. - ?line V = try V catch nisse -> error after after_call() end, - ?line after_clean(), - ?line 42 = try 42 after after_call() end, - ?line after_clean(), - ?line [V] = try [V] catch nisse -> error after after_call() end, - ?line after_clean(), - ?line {ok,V} = try {ok,V} after after_call() end, + V = try V catch nisse -> error after after_call() end, + after_clean(), + 42 = try 42 after after_call() end, + after_clean(), + [V] = try [V] catch nisse -> error after after_call() end, + after_clean(), + {ok,V} = try {ok,V} after after_call() end, %% Try/of - ?line ok = try V of - {a,variable} -> ok - catch nisse -> erro - end, - + ok = try V of + {a,variable} -> ok + catch nisse -> erro + end, + ok. after_call() -> @@ -122,24 +125,24 @@ after_clean() -> lean_throw(Conf) when is_list(Conf) -> - ?line {throw,kalle} = + {throw,kalle} = try throw(kalle) catch Kalle -> {throw,Kalle} end, - ?line {exit,kalle} = + {exit,kalle} = try exit(kalle) catch Throw1 -> {throw,Throw1}; exit:Reason1 -> {exit,Reason1} end, - ?line {exit,kalle} = + {exit,kalle} = try exit(kalle) catch exit:Reason2 -> {exit,Reason2}; Throw2 -> {throw,Throw2} end, - ?line {exit,kalle} = + {exit,kalle} = try try exit(kalle) catch Throw3 -> {throw,Throw3} @@ -152,25 +155,25 @@ lean_throw(Conf) when is_list(Conf) -> try_of(Conf) when is_list(Conf) -> - ?line {ok,{some,content}} = + {ok,{some,content}} = try_of_1({value,{good,{some,content}}}), - ?line {error,[other,content]} = + {error,[other,content]} = try_of_1({value,{bad,[other,content]}}), - ?line {caught,{exit,{ex,it,[reason]}}} = + {caught,{exit,{ex,it,[reason]}}} = try_of_1({exit,{ex,it,[reason]}}), - ?line {caught,{throw,[term,{in,a,{tuple}}]}} = + {caught,{throw,[term,{in,a,{tuple}}]}} = try_of_1({throw,[term,{in,a,{tuple}}]}), - ?line {caught,{error,[bad,arg]}} = + {caught,{error,[bad,arg]}} = try_of_1({error,[bad,arg]}), - ?line {caught,{error,badarith}} = + {caught,{error,badarith}} = try_of_1({'div',{1,0}}), - ?line {caught,{error,badarith}} = + {caught,{error,badarith}} = try_of_1({'add',{a,0}}), - ?line {caught,{error,badarg}} = + {caught,{error,badarg}} = try_of_1({'abs',x}), - ?line {caught,{error,function_clause}} = + {caught,{error,function_clause}} = try_of_1(illegal), - ?line {error,{try_clause,{some,other_garbage}}} = + {error,{try_clause,{some,other_garbage}}} = try try_of_1({value,{some,other_garbage}}) catch error:Reason -> {error,Reason} end, @@ -188,29 +191,29 @@ try_of_1(X) -> try_after(Conf) when is_list(Conf) -> - ?line {{ok,[some,value],undefined},finalized} = + {{ok,[some,value],undefined},finalized} = try_after_1({value,{ok,[some,value]}},finalized), - ?line {{error,badarith,undefined},finalized} = + {{error,badarith,undefined},finalized} = try_after_1({'div',{1,0}},finalized), - ?line {{error,badarith,undefined},finalized} = + {{error,badarith,undefined},finalized} = try_after_1({'add',{1,a}},finalized), - ?line {{error,badarg,undefined},finalized} = + {{error,badarg,undefined},finalized} = try_after_1({'abs',a},finalized), - ?line {{error,[the,{reason}],undefined},finalized} = + {{error,[the,{reason}],undefined},finalized} = try_after_1({error,[the,{reason}]},finalized), - ?line {{throw,{thrown,[reason]},undefined},finalized} = + {{throw,{thrown,[reason]},undefined},finalized} = try_after_1({throw,{thrown,[reason]}},finalized), - ?line {{exit,{exited,{reason}},undefined},finalized} = + {{exit,{exited,{reason}},undefined},finalized} = try_after_1({exit,{exited,{reason}}},finalized), - ?line {{error,function_clause,undefined},finalized} = + {{error,function_clause,undefined},finalized} = try_after_1(function_clause,finalized), - ?line ok = + ok = try try_after_1({'add',{1,1}}, finalized) catch error:{try_clause,2} -> ok end, - ?line finalized = erase(try_after), - ?line ok = + finalized = erase(try_after), + ok = try try foo({exit,[reaso,{n}]}) after put(try_after, finalized) end @@ -239,7 +242,7 @@ try_after_1(X, Y) -> after_bind(Conf) when is_list(Conf) -> V = [make_ref(),self()|value], - ?line {value,{value,V}} = + {value,{value,V}} = after_bind_1({value,V}, V, {value,V}), ok. @@ -266,12 +269,12 @@ after_bind_1(X, V, Y) -> catch_oops(Conf) when is_list(Conf) -> V = {v,[a,l|u],{e},self()}, - ?line {value,V} = catch_oops_1({value,V}), - ?line {value,1} = catch_oops_1({'div',{1,1}}), - ?line {error,badarith} = catch_oops_1({'div',{1,0}}), - ?line {error,function_clause} = catch_oops_1(function_clause), - ?line {throw,V} = catch_oops_1({throw,V}), - ?line {exit,V} = catch_oops_1({exit,V}), + {value,V} = catch_oops_1({value,V}), + {value,1} = catch_oops_1({'div',{1,1}}), + {error,badarith} = catch_oops_1({'div',{1,0}}), + {error,function_clause} = catch_oops_1(function_clause), + {throw,V} = catch_oops_1({throw,V}), + {exit,V} = catch_oops_1({exit,V}), ok. catch_oops_1(X) -> @@ -290,10 +293,10 @@ catch_oops_1(X) -> after_oops(Conf) when is_list(Conf) -> V = {self(),make_ref()}, - ?line {{value,V},V} = after_oops_1({value,V}, {value,V}), - ?line {{exit,V},V} = after_oops_1({exit,V}, {value,V}), - ?line {{error,V},undefined} = after_oops_1({value,V}, {error,V}), - ?line {{error,function_clause},undefined} = + {{value,V},V} = after_oops_1({value,V}, {value,V}), + {{exit,V},V} = after_oops_1({exit,V}, {value,V}), + {{error,V},undefined} = after_oops_1({value,V}, {error,V}), + {{error,function_clause},undefined} = after_oops_1({exit,V}, function_clause), ok. @@ -314,39 +317,39 @@ after_oops_1(X, Y) -> eclectic(Conf) when is_list(Conf) -> V = {make_ref(),3.1415926535,[[]|{}]}, - ?line {{value,{value,V},V},V} = + {{value,{value,V},V},V} = eclectic_1({foo,{value,{value,V}}}, undefined, {value,V}), - ?line {{'EXIT',{V,[{?MODULE,foo,1,_}|_]}},V} = + {{'EXIT',{V,[{?MODULE,foo,1,_}|_]}},V} = eclectic_1({catch_foo,{error,V}}, undefined, {value,V}), - ?line {{error,{exit,V},{'EXIT',V}},V} = + {{error,{exit,V},{'EXIT',V}},V} = eclectic_1({foo,{error,{exit,V}}}, error, {value,V}), - ?line {{value,{value,V},V}, + {{value,{value,V},V}, {'EXIT',{badarith,[{?MODULE,my_add,2,_}|_]}}} = eclectic_1({foo,{value,{value,V}}}, undefined, {'add',{0,a}}), - ?line {{'EXIT',V},V} = + {{'EXIT',V},V} = eclectic_1({catch_foo,{exit,V}}, undefined, {throw,V}), - ?line {{error,{'div',{1,0}},{'EXIT',{badarith,[{?MODULE,my_div,2,_}|_]}}}, + {{error,{'div',{1,0}},{'EXIT',{badarith,[{?MODULE,my_div,2,_}|_]}}}, {'EXIT',V}} = eclectic_1({foo,{error,{'div',{1,0}}}}, error, {exit,V}), - ?line {{{error,V},{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}}, + {{{error,V},{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}}, {'EXIT',V}} = eclectic_1({catch_foo,{throw,{error,V}}}, undefined, {exit,V}), %% - ?line {{value,{value,{value,V},V}},V} = + {{value,{value,{value,V},V}},V} = eclectic_2({value,{value,V}}, undefined, {value,V}), - ?line {{value,{throw,{value,V},V}},V} = + {{value,{throw,{value,V},V}},V} = eclectic_2({throw,{value,V}}, throw, {value,V}), - ?line {{caught,{'EXIT',V}},undefined} = + {{caught,{'EXIT',V}},undefined} = eclectic_2({value,{value,V}}, undefined, {exit,V}), - ?line {{caught,{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}},undefined} = + {{caught,{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}},undefined} = eclectic_2({error,{value,V}}, throw, {error,V}), - ?line {{caught,{'EXIT',{badarg,[{erlang,abs,[V],_}|_]}}},V} = + {{caught,{'EXIT',{badarg,[{erlang,abs,[V],_}|_]}}},V} = eclectic_2({value,{'abs',V}}, undefined, {value,V}), - ?line {{caught,{'EXIT',{badarith,[{?MODULE,my_add,2,_}|_]}}},V} = + {{caught,{'EXIT',{badarith,[{?MODULE,my_add,2,_}|_]}}},V} = eclectic_2({exit,{'add',{0,a}}}, exit, {value,V}), - ?line {{caught,{'EXIT',V}},undefined} = + {{caught,{'EXIT',V}},undefined} = eclectic_2({value,{error,V}}, undefined, {exit,V}), - ?line {{caught,{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}},undefined} = + {{caught,{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}},undefined} = eclectic_2({throw,{'div',{1,0}}}, throw, {error,V}), ok. @@ -374,44 +377,44 @@ eclectic_2(X, C, Y) -> Catch = case catch - {Done, - try foo(X) of - V -> {value,V,foo(V)} - catch - C:D -> {C,D,foo(D)} - after - put(eclectic, foo(Y)) - end} of - {Done,Z} -> {value,Z}; - Z -> {caught,Z} - end, + {Done, + try foo(X) of + V -> {value,V,foo(V)} + catch + C:D -> {C,D,foo(D)} + after + put(eclectic, foo(Y)) + end} of + {Done,Z} -> {value,Z}; + Z -> {caught,Z} + end, {Catch,erase(eclectic)}. rethrow(Conf) when is_list(Conf) -> V = {a,[b,{c,self()},make_ref]}, - ?line {value2,value1} = + {value2,value1} = rethrow_1({value,V}, V), - ?line {caught2,{error,V}} = + {caught2,{error,V}} = rethrow_2({error,V}, undefined), - ?line {caught2,{exit,V}} = + {caught2,{exit,V}} = rethrow_1({exit,V}, error), - ?line {caught2,{throw,V}} = + {caught2,{throw,V}} = rethrow_1({throw,V}, undefined), - ?line {caught2,{throw,V}} = + {caught2,{throw,V}} = rethrow_2({throw,V}, undefined), - ?line {caught2,{error,badarith}} = + {caught2,{error,badarith}} = rethrow_1({'add',{0,a}}, throw), - ?line {caught2,{error,function_clause}} = + {caught2,{error,function_clause}} = rethrow_2(function_clause, undefined), - ?line {caught2,{error,{try_clause,V}}} = + {caught2,{error,{try_clause,V}}} = rethrow_1({value,V}, exit), - ?line {value2,{caught1,V}} = + {value2,{caught1,V}} = rethrow_1({error,V}, error), - ?line {value2,{caught1,V}} = + {value2,{caught1,V}} = rethrow_1({exit,V}, exit), - ?line {value2,caught1} = + {value2,caught1} = rethrow_2({throw,V}, V), ok. @@ -441,91 +444,91 @@ rethrow_2(X, C1) -> nested_of(Conf) when is_list(Conf) -> V = {[self()|make_ref()],1.4142136}, - ?line {{value,{value1,{V,x2}}}, - {V,x3}, - {V,x4}, - finalized} = + {{value,{value1,{V,x2}}}, + {V,x3}, + {V,x4}, + finalized} = nested_of_1({{value,{V,x1}},void,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{throw,{V,x2}}}, - {V,x3}, - {V,x4}, - finalized} = + {{caught,{throw,{V,x2}}}, + {V,x3}, + {V,x4}, + finalized} = nested_of_1({{value,{V,x1}},void,{V,x1}}, {throw,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{error,badarith}}, - undefined, - {V,x4}, - finalized} = + {{caught,{error,badarith}}, + undefined, + {V,x4}, + finalized} = nested_of_1({{value,{V,x1}},void,{V,x1}}, {throw,{V,x2}}, {'div',{1,0}}, {value,{V,x4}}), - ?line {{caught,{error,badarith}}, - undefined, - undefined, - finalized} = + {{caught,{error,badarith}}, + undefined, + undefined, + finalized} = nested_of_1({{value,{V,x1}},void,{V,x1}}, {throw,{V,x2}}, {'div',{1,0}}, {'add',{0,b}}), %% - ?line {{caught,{error,{try_clause,{V,x1}}}}, - {V,x3}, - {V,x4}, - finalized} = + {{caught,{error,{try_clause,{V,x1}}}}, + {V,x3}, + {V,x4}, + finalized} = nested_of_1({{value,{V,x1}},void,try_clause}, void, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{exit,{V,x3}}}, - undefined, - {V,x4}, - finalized} = + {{caught,{exit,{V,x3}}}, + undefined, + {V,x4}, + finalized} = nested_of_1({{value,{V,x1}},void,try_clause}, void, {exit,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{throw,{V,x4}}}, - undefined, - undefined, - finalized} = + {{caught,{throw,{V,x4}}}, + undefined, + undefined, + finalized} = nested_of_1({{value,{V,x1}},void,try_clause}, void, {exit,{V,x3}}, {throw,{V,x4}}), %% - ?line {{value,{caught1,{V,x2}}}, - {V,x3}, - {V,x4}, - finalized} = + {{value,{caught1,{V,x2}}}, + {V,x3}, + {V,x4}, + finalized} = nested_of_1({{error,{V,x1}},error,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{error,badarith}}, - {V,x3}, - {V,x4}, - finalized} = + {{caught,{error,badarith}}, + {V,x3}, + {V,x4}, + finalized} = nested_of_1({{error,{V,x1}},error,{V,x1}}, {'add',{1,c}}, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{error,badarith}}, - undefined, - {V,x4}, - finalized} = + {{caught,{error,badarith}}, + undefined, + {V,x4}, + finalized} = nested_of_1({{error,{V,x1}},error,{V,x1}}, {'add',{1,c}}, {'div',{17,0}}, {value,{V,x4}}), - ?line {{caught,{error,badarg}}, - undefined, - undefined, - finalized} = + {{caught,{error,badarg}}, + undefined, + undefined, + finalized} = nested_of_1({{error,{V,x1}},error,{V,x1}}, {'add',{1,c}}, {'div',{17,0}}, {'abs',V}), %% - ?line {{caught,{error,badarith}}, - {V,x3}, - {V,x4}, - finalized} = + {{caught,{error,badarith}}, + {V,x3}, + {V,x4}, + finalized} = nested_of_1({{'add',{2,c}},rethrow,void}, void, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{error,badarg}}, - undefined, - {V,x4}, - finalized} = + {{caught,{error,badarg}}, + undefined, + {V,x4}, + finalized} = nested_of_1({{'add',{2,c}},rethrow,void}, void, {'abs',V}, {value,{V,x4}}), - ?line {{caught,{error,function_clause}}, - undefined, - undefined, - finalized} = + {{caught,{error,function_clause}}, + undefined, + undefined, + finalized} = nested_of_1({{'add',{2,c}},rethrow,void}, void, {'abs',V}, function_clause), ok. @@ -566,93 +569,93 @@ nested_of_1({X1,C1,V1}, nested_catch(Conf) when is_list(Conf) -> V = {[make_ref(),1.4142136,self()]}, - ?line {{value,{value1,{V,x2}}}, - {V,x3}, - {V,x4}, - finalized} = + {{value,{value1,{V,x2}}}, + {V,x3}, + {V,x4}, + finalized} = nested_catch_1({{value,{V,x1}},void,{V,x1}}, - {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{throw,{V,x2}}}, - {V,x3}, - {V,x4}, - finalized} = + {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), + {{caught,{throw,{V,x2}}}, + {V,x3}, + {V,x4}, + finalized} = nested_catch_1({{value,{V,x1}},void,{V,x1}}, - {throw,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{error,badarith}}, - undefined, - {V,x4}, - finalized} = + {throw,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), + {{caught,{error,badarith}}, + undefined, + {V,x4}, + finalized} = nested_catch_1({{value,{V,x1}},void,{V,x1}}, - {throw,{V,x2}}, {'div',{1,0}}, {value,{V,x4}}), - ?line {{caught,{error,badarith}}, - undefined, - undefined, - finalized} = + {throw,{V,x2}}, {'div',{1,0}}, {value,{V,x4}}), + {{caught,{error,badarith}}, + undefined, + undefined, + finalized} = nested_catch_1({{value,{V,x1}},void,{V,x1}}, - {throw,{V,x2}}, {'div',{1,0}}, {'add',{0,b}}), + {throw,{V,x2}}, {'div',{1,0}}, {'add',{0,b}}), %% - ?line {{caught,{error,{try_clause,{V,x1}}}}, - {V,x3}, - {V,x4}, - finalized} = + {{caught,{error,{try_clause,{V,x1}}}}, + {V,x3}, + {V,x4}, + finalized} = nested_catch_1({{value,{V,x1}},void,try_clause}, - void, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{exit,{V,x3}}}, - undefined, - {V,x4}, - finalized} = + void, {value,{V,x3}}, {value,{V,x4}}), + {{caught,{exit,{V,x3}}}, + undefined, + {V,x4}, + finalized} = nested_catch_1({{value,{V,x1}},void,try_clause}, - void, {exit,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{throw,{V,x4}}}, - undefined, - undefined, - finalized} = + void, {exit,{V,x3}}, {value,{V,x4}}), + {{caught,{throw,{V,x4}}}, + undefined, + undefined, + finalized} = nested_catch_1({{value,{V,x1}},void,try_clause}, - void, {exit,{V,x3}}, {throw,{V,x4}}), + void, {exit,{V,x3}}, {throw,{V,x4}}), %% - ?line {{value,{caught1,{V,x2}}}, - {V,x3}, - {V,x4}, - finalized} = + {{value,{caught1,{V,x2}}}, + {V,x3}, + {V,x4}, + finalized} = nested_catch_1({{error,{V,x1}},error,{V,x1}}, - {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{error,badarith}}, - {V,x3}, - {V,x4}, - finalized} = + {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}), + {{caught,{error,badarith}}, + {V,x3}, + {V,x4}, + finalized} = nested_catch_1({{error,{V,x1}},error,{V,x1}}, - {'add',{1,c}}, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{error,badarith}}, - undefined, - {V,x4}, - finalized} = + {'add',{1,c}}, {value,{V,x3}}, {value,{V,x4}}), + {{caught,{error,badarith}}, + undefined, + {V,x4}, + finalized} = nested_catch_1({{error,{V,x1}},error,{V,x1}}, - {'add',{1,c}}, {'div',{17,0}}, {value,{V,x4}}), - ?line {{caught,{error,badarg}}, - undefined, - undefined, - finalized} = + {'add',{1,c}}, {'div',{17,0}}, {value,{V,x4}}), + {{caught,{error,badarg}}, + undefined, + undefined, + finalized} = nested_catch_1({{error,{V,x1}},error,{V,x1}}, - {'add',{1,c}}, {'div',{17,0}}, {'abs',V}), + {'add',{1,c}}, {'div',{17,0}}, {'abs',V}), %% - ?line {{caught,{error,badarith}}, - {V,x3}, - {V,x4}, - finalized} = + {{caught,{error,badarith}}, + {V,x3}, + {V,x4}, + finalized} = nested_catch_1({{'add',{2,c}},rethrow,void}, - void, {value,{V,x3}}, {value,{V,x4}}), - ?line {{caught,{error,badarg}}, - undefined, - {V,x4}, - finalized} = + void, {value,{V,x3}}, {value,{V,x4}}), + {{caught,{error,badarg}}, + undefined, + {V,x4}, + finalized} = nested_catch_1({{'add',{2,c}},rethrow,void}, - void, {'abs',V}, {value,{V,x4}}), - ?line {{caught,{error,function_clause}}, - undefined, - undefined, - finalized} = + void, {'abs',V}, {value,{V,x4}}), + {{caught,{error,function_clause}}, + undefined, + undefined, + finalized} = nested_catch_1({{'add',{2,c}},rethrow,void}, - void, {'abs',V}, function_clause), + void, {'abs',V}, function_clause), ok. nested_catch_1({X1,C1,V1}, @@ -691,64 +694,64 @@ nested_catch_1({X1,C1,V1}, nested_after(Conf) when is_list(Conf) -> V = [{make_ref(),1.4142136,self()}], - ?line {value, + {value, {V,x3}, {value1,{V,x2}}, finalized} = nested_after_1({{value,{V,x1}},void,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}), - ?line {{caught,{error,{V,x2}}}, + {{caught,{error,{V,x2}}}, {V,x3}, undefined, finalized} = nested_after_1({{value,{V,x1}},void,{V,x1}}, {error,{V,x2}}, {value,{V,x3}}), - ?line {{caught,{exit,{V,x3}}}, + {{caught,{exit,{V,x3}}}, undefined, undefined, finalized} = nested_after_1({{value,{V,x1}},void,{V,x1}}, {error,{V,x2}}, {exit,{V,x3}}), %% - ?line {{caught,{error,{try_clause,{V,x1}}}}, + {{caught,{error,{try_clause,{V,x1}}}}, {V,x3}, undefined, finalized} = nested_after_1({{value,{V,x1}},void,try_clause}, void, {value,{V,x3}}), - ?line {{caught,{error,badarith}}, + {{caught,{error,badarith}}, undefined, undefined, finalized} = nested_after_1({{value,{V,x1}},void,try_clause}, void, {'div',{17,0}}), %% - ?line {value, + {value, {V,x3}, {caught1,{V,x2}}, finalized} = nested_after_1({{throw,{V,x1}},throw,{V,x1}}, {value,{V,x2}}, {value,{V,x3}}), - ?line {{caught,{error,badarith}}, + {{caught,{error,badarith}}, {V,x3}, undefined, finalized} = nested_after_1({{throw,{V,x1}},throw,{V,x1}}, {'add',{a,b}}, {value,{V,x3}}), - ?line {{caught,{error,badarg}}, + {{caught,{error,badarg}}, undefined, undefined, finalized} = nested_after_1({{throw,{V,x1}},throw,{V,x1}}, {'add',{a,b}}, {'abs',V}), %% - ?line {{caught,{throw,{V,x1}}}, + {{caught,{throw,{V,x1}}}, {V,x3}, undefined, finalized} = nested_after_1({{throw,{V,x1}},rethrow,void}, void, {value,{V,x3}}), - ?line {{caught,{error,badarith}}, + {{caught,{error,badarith}}, undefined, undefined, finalized} = @@ -790,7 +793,6 @@ nested_after_1({X1,C1,V1}, nested_horrid(Config) when is_list(Config) -> - _V = {make_ref(),nested_horrid,4.711}, {[true,true],{[true,1.0],1.0}} = nested_horrid_1({true,void,void}, 1.0), ok. @@ -841,12 +843,12 @@ my_abs(X) -> abs(X). last_call_optimization(Config) when is_list(Config) -> - ?line error = in_tail(dum), - ?line StkSize0 = in_tail(0), - ?line StkSize = in_tail(50000), + error = in_tail(dum), + StkSize0 = in_tail(0), + StkSize = in_tail(50000), io:format("StkSize0 = ~p", [StkSize0]), io:format("StkSize = ~p", [StkSize]), - ?line StkSize = StkSize0, + StkSize = StkSize0, ok. in_tail(E) -> @@ -889,20 +891,20 @@ do_bool(A0, B) -> plain_catch_coverage(Config) when is_list(Config) -> %% Cover some code in beam_block:alloc_may_pass/1. - ?line {a,[42]} = do_plain_catch_list(42). + {a,[42]} = do_plain_catch_list(42). do_plain_catch_list(X) -> B = [X], catch id({a,B}). andalso_orelse(Config) when is_list(Config) -> - ?line {2,{a,42}} = andalso_orelse_1(true, {a,42}), - ?line {b,{b}} = andalso_orelse_1(false, {b}), - ?line {catched,no_tuple} = andalso_orelse_1(false, no_tuple), + {2,{a,42}} = andalso_orelse_1(true, {a,42}), + {b,{b}} = andalso_orelse_1(false, {b}), + {catched,no_tuple} = andalso_orelse_1(false, no_tuple), - ?line ok = andalso_orelse_2({type,[a]}), - ?line also_ok = andalso_orelse_2({type,[]}), - ?line also_ok = andalso_orelse_2({type,{a}}), + ok = andalso_orelse_2({type,[a]}), + also_ok = andalso_orelse_2({type,[]}), + also_ok = andalso_orelse_2({type,{a}}), ok. andalso_orelse_1(A, B) -> @@ -917,8 +919,6 @@ andalso_orelse_1(A, B) -> catched end,B}. -id(I) -> I. - andalso_orelse_2({Type,Keyval}) -> try if is_atom(Type) andalso length(Keyval) > 0 -> ok; @@ -944,3 +944,100 @@ get_valid_line([_|T]=Path, Annotations) -> _:not_found -> get_valid_line(T, Annotations) end. + +hockey(_) -> + {'EXIT',{{badmatch,_},[_|_]}} = (catch hockey()), + ok. + +hockey() -> + %% beam_jump used to generate a call into the try block. + %% beam_validator disapproved. + receive _ -> (b = fun() -> ok end) + + hockey, +x after 0 -> ok end, try (a = fun() -> ok end) + hockey, + + y catch _ -> ok end. + + +-record(state, {foo}). + +handle_info(_Config) -> + do_handle_info({foo}, #state{}), + ok. + +do_handle_info({_}, State) -> + handle_info_ok(), + State#state{foo = bar}, + case ok of + _ -> + case catch handle_info_ok() of + ok -> + {stop, State} + end + end; +do_handle_info(_, State) -> + (catch begin + handle_info_ok(), + State#state{foo = bar} + end), + case ok of + _ -> + case catch handle_info_ok() of + ok -> + {stop, State} + end + end. + +handle_info_ok() -> ok. + +'catch_in_catch'(_Config) -> + process_flag(trap_exit, true), + Pid = spawn_link(fun() -> + catch_in_catch_init(x), + exit(good_exit) + end), + receive + {'EXIT',Pid,good_exit} -> + ok; + Other -> + io:format("Unexpected: ~p\n", [Other]), + error + after 32000 -> + io:format("No message received\n"), + error + end. + +'catch_in_catch_init'(Param) -> + process_flag(trap_exit, true), + %% The catches were improperly nested, causing a "No catch found" crash. + (catch begin + id(Param), + (catch exit(bar)) + end + ), + ignore. + +grab_bag(_Config) -> + %% Thanks to Martin Bjorklund. + _ = fun() -> ok end, + try + fun() -> ok end + after + fun({A, B}) -> A + B end + end, + + %% Thanks to Tim Rath. + A = {6}, + try + io:fwrite("") + after + fun () -> + fun () -> {_} = A end + end + end, + + %% Unnecessary catch. + 22 = (catch 22), + + ok. + + +id(I) -> I. diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index 0637041873..f884e6e7d6 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -1,18 +1,19 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2013. All Rights Reserved. +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% 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 %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%% 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% %% @@ -26,9 +27,9 @@ -define(privdir, "warnings_SUITE_priv"). -define(t, test_server). -else. --include_lib("test_server/include/test_server.hrl"). --define(datadir, ?config(data_dir, Conf)). --define(privdir, ?config(priv_dir, Conf)). +-include_lib("common_test/include/ct.hrl"). +-define(datadir, proplists:get_value(data_dir, Conf)). +-define(privdir, proplists:get_value(priv_dir, Conf)). -endif. -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, @@ -38,22 +39,20 @@ -export([pattern/1,pattern2/1,pattern3/1,pattern4/1, guard/1,bad_arith/1,bool_cases/1,bad_apply/1, files/1,effect/1,bin_opt_info/1,bin_construction/1, - comprehensions/1,maps/1,redundant_boolean_clauses/1, - latin1_fallback/1]). - -% Default timetrap timeout (set in init_per_testcase). --define(default_timeout, ?t:minutes(2)). + comprehensions/1,maps/1,maps_bin_opt_info/1, + redundant_boolean_clauses/1, + latin1_fallback/1,underscore/1,no_warnings/1, + bit_syntax/1]). init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(?default_timeout), - [{watchdog, Dog} | Config]. + Config. -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,2}}]. all() -> test_lib:recompile(?MODULE), @@ -64,7 +63,9 @@ groups() -> [pattern,pattern2,pattern3,pattern4,guard, bad_arith,bool_cases,bad_apply,files,effect, bin_opt_info,bin_construction,comprehensions,maps, - redundant_boolean_clauses,latin1_fallback]}]. + maps_bin_opt_info, + redundant_boolean_clauses,latin1_fallback, + underscore,no_warnings,bit_syntax]}]. init_per_suite(Config) -> Config. @@ -99,7 +100,7 @@ pattern(Config) when is_list(Config) -> [{2,v3_core,nomatch}, {6,v3_core,nomatch}, {11,v3_core,nomatch} ] }}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), ok. pattern2(Config) when is_list(Config) -> @@ -123,7 +124,7 @@ pattern2(Config) when is_list(Config) -> {4,sys_core_fold,no_clause_match}, {5,sys_core_fold,nomatch_clause_type}, {6,sys_core_fold,nomatch_clause_type}]}}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), %% Disable Core Erlang optimizations. v3_kernel should produce %% a warning for the clause that didn't match. @@ -132,7 +133,7 @@ pattern2(Config) when is_list(Config) -> [nowarn_unused_vars,no_copt], {warnings, [{2,v3_kernel,{nomatch_shadow,1}}]}}], - ?line [] = run(Config, Ts2), + [] = run(Config, Ts2), ok. pattern3(Config) when is_list(Config) -> @@ -148,7 +149,7 @@ pattern3(Config) when is_list(Config) -> [nowarn_unused_vars], {warnings, [{4,v3_kernel,{nomatch_shadow,2}}]}}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), ok. @@ -209,7 +210,7 @@ pattern4(Config) when is_list(Config) -> {23,sys_core_fold,no_clause_match}, {33,sys_core_fold,no_clause_match} ]}}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), ok. @@ -252,7 +253,7 @@ guard(Config) when is_list(Config) -> {11,sys_core_fold,nomatch_guard}, {11,sys_core_fold,{eval_failure,badarg}} ]}}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), ok. @@ -280,11 +281,12 @@ bad_arith(Config) when is_list(Config) -> {3,sys_core_fold,{eval_failure,badarith}}, {9,sys_core_fold,nomatch_guard}, {9,sys_core_fold,{eval_failure,badarith}}, + {9,sys_core_fold,{no_effect,{erlang,is_integer,1}}}, {10,sys_core_fold,nomatch_guard}, {10,sys_core_fold,{eval_failure,badarith}}, {15,sys_core_fold,{eval_failure,badarith}} ] }}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), ok. bool_cases(Config) when is_list(Config) -> @@ -316,7 +318,7 @@ bool_cases(Config) when is_list(Config) -> [{6,sys_core_fold,nomatch_shadow}, {13,sys_core_fold,nomatch_shadow}, {18,sys_core_fold,nomatch_clause_type} ]} }], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), ok. bad_apply(Config) when is_list(Config) -> @@ -335,11 +337,11 @@ bad_apply(Config) when is_list(Config) -> {4,v3_kernel,bad_call}, {5,v3_kernel,bad_call}, {6,v3_kernel,bad_call}]}}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), %% Also verify that the generated code generates the correct error. - ?line try erlang:42() of - _ -> ?line ?t:fail() + try erlang:42() of + _ -> ct:fail(should_fail) catch error:badarg -> ok end, @@ -363,12 +365,12 @@ files(Config) when is_list(Config) -> [{"file1",[{17,sys_core_fold,{eval_failure,badarith}}]}, {"file2",[{10,sys_core_fold,{eval_failure,badarith}}]}]}}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), ok. %% Test warnings for term construction and BIF calls in effect context. effect(Config) when is_list(Config) -> - Ts = [{lc, + Ts = [{effect, <<" t(X) -> case X of @@ -474,6 +476,19 @@ effect(Config) when is_list(Config) -> m9(Bs) -> [{B,ok} = {B,foo:bar(B)} || B <- Bs], ok. + + m10(ConfigTableSize) -> + case ConfigTableSize of + apa -> + CurrentConfig = {id(camel_phase3),id(sms)}, + case CurrentConfig of + {apa, bepa} -> ok; + _ -> ok + end + end, + ok. + + id(I) -> I. ">>, [], {warnings,[{5,sys_core_fold,{no_effect,{erlang,is_integer,1}}}, @@ -496,7 +511,7 @@ effect(Config) when is_list(Config) -> {28,sys_core_fold,useless_building}, {36,sys_core_fold,{no_effect,{erlang,'=:=',2}}}, {38,sys_core_fold,{no_effect,{erlang,get_cookie,0}}}]}}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), ok. bin_opt_info(Config) when is_list(Config) -> @@ -519,14 +534,14 @@ bin_opt_info(Config) when is_list(Config) -> {5,beam_bsm,{no_bin_opt,{{t1,1},no_suitable_bs_start_match}}}, {9,beam_bsm,{no_bin_opt, {binary_used_in,{extfunc,erlang,split_binary,2}}}} ]}}], - ?line [] = run(Config, Ts1), + [] = run(Config, Ts1), %% For coverage: don't give the bin_opt_info option. Ts2 = [{bsm2, Code, [], []}], - ?line [] = run(Config, Ts2), + [] = run(Config, Ts2), ok. bin_construction(Config) when is_list(Config) -> @@ -543,7 +558,7 @@ bin_construction(Config) when is_list(Config) -> [], {warnings,[{4,sys_core_fold,embedded_binary_size}, {8,sys_core_fold,{embedded_unit,8,28}}]}}], - ?line [] = run(Config, Ts), + [] = run(Config, Ts), ok. @@ -578,11 +593,11 @@ maps(Config) when is_list(Config) -> <<" t() -> M = {a,[]}, - {'EXIT',{badarg,_}} = (catch(M#{ a => 1})), + {'EXIT',{badarg,_}} = (catch(M#{ a => 1 })), ok. ">>, [], - {warnings,[{4,v3_kernel,bad_map}]}}, + {warnings,[{4,sys_core_fold,{eval_failure,badmap}}]}}, {bad_map_src2, <<" t() -> @@ -592,7 +607,7 @@ maps(Config) when is_list(Config) -> id(I) -> I. ">>, [inline], - {warnings,[{4,v3_kernel,bad_map}]}}, + []}, {bad_map_src3, <<" t() -> @@ -600,8 +615,8 @@ maps(Config) when is_list(Config) -> ok. ">>, [], - {warnings,[{3,v3_core,bad_map}]}}, - {bad_map_literal_key, + {warnings,[{3,v3_core,badmap}]}}, + {ok_map_literal_key, <<" t() -> V = id(1), @@ -614,10 +629,23 @@ maps(Config) when is_list(Config) -> id(I) -> I. ">>, [], - {warnings,[{6,v3_core,nomatch}]}}], + []}], run(Config, Ts), ok. +maps_bin_opt_info(Config) when is_list(Config) -> + Ts = [{map_bsm, + <<" + t1(<<0:8,7:8,T/binary>>,#{val := I}=M) -> + t1(T, M#{val := I+1}); + t1(<<_:8>>,M) -> + M. + ">>, + [bin_opt_info], + {warnings,[{2,beam_bsm,bin_opt}]}}], + [] = run(Config, Ts), + ok. + redundant_boolean_clauses(Config) when is_list(Config) -> Ts = [{redundant_boolean_clauses, <<" @@ -678,6 +706,124 @@ latin1_fallback(Conf) when is_list(Conf) -> ok. +underscore(Config) when is_list(Config) -> + S0 = <<"f(A) -> + _VAR1 = <<A>>, + _VAR2 = {ok,A}, + _VAR3 = [A], + ok. + g(A) -> + _VAR1 = A/0, + _VAR2 = date(), + ok. + h() -> + _VAR1 = fun() -> ok end, + ok. + i(A) -> + _VAR1 = #{A=>42}, + ok. + ">>, + Ts0 = [{underscore0, + S0, + [], + {warnings,[{2,sys_core_fold,useless_building}, + {3,sys_core_fold,useless_building}, + {4,sys_core_fold,useless_building}, + {7,sys_core_fold,result_ignored}, + {8,sys_core_fold,{no_effect,{erlang,date,0}}}, + {11,sys_core_fold,useless_building}, + {14,sys_core_fold,useless_building} + ]}}], + [] = run(Config, Ts0), + + %% Replace all "_VAR<digit>" variables with a plain underscore. + %% Now there should be no warnings. + S1 = re:replace(S0, "_VAR\\d+", "_", [global]), + io:format("~s\n", [S1]), + Ts1 = [{underscore1,S1,[],[]}], + [] = run(Config, Ts1), + + ok. + +no_warnings(Config) when is_list(Config) -> + Ts = [{no_warnings, + <<"-record(r, {s=ordsets:new(),a,b}). + + a() -> + R = #r{}, %No warning expected. + {R#r.a,R#r.b}. + + b(X) -> + T = true, + Var = [X], %No warning expected. + case T of + false -> Var; + true -> [] + end. + + c() -> + R0 = {r,\"abc\",undefined,os:timestamp()}, %No warning. + case R0 of + {r,V1,_V2,V3} -> {r,V1,\"def\",V3} + end. + + d(In0, Bool) -> + {In1,Int} = case id(Bool) of + false -> {In0,0} + end, + [In1,Int]. + + id(I) -> I. + ">>, + [], + []}], + run(Config, Ts), + ok. + +bit_syntax(Config) -> + Ts = [{?FUNCTION_NAME, + <<"a(<<-1>>) -> ok; + a(<<1023>>) -> ok; + a(<<777/signed>>) -> ok; + a(<<a/binary>>) -> ok; + a(<<a/integer>>) -> ok; + a(<<a/float>>) -> ok; + a(<<a/utf8>>) -> ok; + a(<<a/utf16>>) -> ok; + a(<<a/utf32>>) -> ok; + a(<<a/utf32>>) -> ok. + b(Bin) -> Sz = bad, <<42:Sz>> = Bin. + c(Sz, Bin) -> + case Bin of + <<-42:Sz/unsigned>> -> ok; + <<42:Sz/float>> -> ok; + <<42:Sz/binary>> -> ok + end. + ">>, + [], + {warnings,[{1,sys_core_fold,no_clause_match}, + {1,sys_core_fold,{nomatch_bit_syntax_unsigned,-1}}, + {2,sys_core_fold,{nomatch_bit_syntax_truncated, + unsigned,1023,8}}, + {3,sys_core_fold,{nomatch_bit_syntax_truncated, + signed,777,8}}, + {4,sys_core_fold,{nomatch_bit_syntax_type,a,binary}}, + {5,sys_core_fold,{nomatch_bit_syntax_type,a,integer}}, + {6,sys_core_fold,{nomatch_bit_syntax_type,a,float}}, + {7,sys_core_fold,{nomatch_bit_syntax_type,a,utf8}}, + {8,sys_core_fold,{nomatch_bit_syntax_type,a,utf16}}, + {9,sys_core_fold,{nomatch_bit_syntax_type,a,utf32}}, + {10,sys_core_fold,{nomatch_bit_syntax_type,a,utf32}}, + {11,sys_core_fold,no_clause_match}, + {11,sys_core_fold,{nomatch_bit_syntax_size,bad}}, + {14,sys_core_fold,{nomatch_bit_syntax_unsigned,-42}}, + {16,sys_core_fold,{nomatch_bit_syntax_type,42,binary}} + ]} + }], + run(Config, Ts), + ok. + + %%% %%% End of test cases. %%% @@ -688,7 +834,7 @@ run(Config, Tests) -> E -> BadL; Bad -> - ?t:format("~nTest ~p failed. Expected~n ~p~n" + io:format("~nTest ~p failed. Expected~n ~p~n" "but got~n ~p~n", [N, E, Bad]), fail() end @@ -699,35 +845,34 @@ run(Config, Tests) -> %% Compiles a test module and returns the list of errors and warnings. run_test(Conf, Test0, Warnings) -> - Mod = "warnings_"++test_lib:uniq(), - Filename = Mod ++ ".erl", - ?line DataDir = ?privdir, - Test = ["-module(", Mod, "). ", Test0], - ?line File = filename:join(DataDir, Filename), - ?line Opts = [binary,export_all,return|Warnings], - ?line ok = file:write_file(File, Test), + Module = "warnings_"++test_lib:uniq(), + Filename = Module ++ ".erl", + DataDir = ?privdir, + Test = ["-module(", Module, "). ", Test0], + File = filename:join(DataDir, Filename), + Opts = [binary,export_all,return|Warnings], + ok = file:write_file(File, Test), %% Compile once just to print all warnings. - ?line compile:file(File, [binary,export_all,report|Warnings]), + compile:file(File, [binary,export_all,report|Warnings]), %% Test result of compilation. - ?line Res = case compile:file(File, Opts) of - {ok, _M, Bin, []} when is_binary(Bin) -> - []; - {ok, _M, Bin, Ws0} when is_binary(Bin) -> - %% We are not interested in warnings from - %% erl_lint here. - WsL = [{F,[W || {_,Mod,_}=W <- Ws, - Mod =/= erl_lint]} || - {F,Ws} <- Ws0], - case WsL of - [{_File,Ws}] -> {warnings, Ws}; - _ -> list_to_tuple([warnings, WsL]) - end - end, + Res = case compile:file(File, Opts) of + {ok, _M, Bin, []} when is_binary(Bin) -> + []; + {ok, _M, Bin, Ws0} when is_binary(Bin) -> + %% We are not interested in warnings from + %% erl_lint here. + WsL = [{F,[W || {_,Mod,_}=W <- Ws, + Mod =/= erl_lint]} || + {F,Ws} <- Ws0], + case WsL of + [{_File,Ws}] -> {warnings, Ws}; + _ -> list_to_tuple([warnings, WsL]) + end + end, file:delete(File), Res. fail() -> - io:format("failed~n"), - ?t:fail(). + ct:fail(failed). diff --git a/lib/compiler/test/z_SUITE.erl b/lib/compiler/test/z_SUITE.erl new file mode 100644 index 0000000000..d864184f4c --- /dev/null +++ b/lib/compiler/test/z_SUITE.erl @@ -0,0 +1,63 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. 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% +%% +-module(z_SUITE). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + loaded/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + test_lib:recompile(?MODULE), + [loaded]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +loaded(_Config) -> + 0 = do_loaded(code:all_loaded(), 0), + ok. + +do_loaded([{M,_}|Ms], E0) -> + E = try + _ = M:module_info(), + _ = M:module_info(functions), + E0 + catch + C:Error -> + Stk = erlang:get_stacktrace(), + io:format("~p:~p\n~p\n", [C,Error,Stk]), + E0 + 1 + end, + do_loaded(Ms, E); +do_loaded([], E) -> E. diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 05e682c893..334784657e 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 5.0.4 +COMPILER_VSN = 7.0.1 |