pax_global_header00006660000000000000000000000064146447327760014536gustar00rootroot0000000000000052 comment=aaf2f364c6d9fad452416c0385ccd296541d5661 harec-0.24.2/000077500000000000000000000000001464473277600127055ustar00rootroot00000000000000harec-0.24.2/.builds/000077500000000000000000000000001464473277600142455ustar00rootroot00000000000000harec-0.24.2/.builds/alpine.yml000066400000000000000000000012541464473277600162420ustar00rootroot00000000000000image: alpine/latest sources: - https://git.sr.ht/~sircmpwn/hare - https://git.sr.ht/~sircmpwn/harec - git://c9x.me/qbe.git tasks: - signoff: | cd harec if [ "$BUILD_REASON" = "patchset" ] then if ! git log --format='%b' origin/master^^.. | grep 'Signed-off-by' >/dev/null then echo "Patch missing Signed-off-by" exit 1 fi fi - qbe: | cd qbe make -j2 PREFIX=/usr sudo make install PREFIX=/usr - build: | cd harec cp configs/linux.mk config.mk make -j2 sudo make install - tests: | cd harec make check - stdlib-tests: | cd hare cp configs/linux.mk config.mk make -j2 check harec-0.24.2/.builds/freebsd.yml000066400000000000000000000004671464473277600164110ustar00rootroot00000000000000image: freebsd/latest sources: - https://git.sr.ht/~sircmpwn/harec - git://c9x.me/qbe.git packages: - binutils tasks: - qbe: | cd qbe make CC=cc PREFIX=/usr sudo make install PREFIX=/usr - build: | cd harec cp configs/freebsd.mk config.mk make -j2 - tests: | cd harec make check harec-0.24.2/.builds/netbsd.yml000066400000000000000000000004541464473277600162520ustar00rootroot00000000000000image: netbsd/9.x sources: - https://git.sr.ht/~sircmpwn/harec - git://c9x.me/qbe.git packages: - binutils tasks: - qbe: | cd qbe make PREFIX=/usr sudo make install PREFIX=/usr - build: | cd harec cp configs/netbsd.mk config.mk make -j2 - tests: | cd harec make check harec-0.24.2/.builds/openbsd.yml000066400000000000000000000004611464473277600164230ustar00rootroot00000000000000image: openbsd/latest sources: - https://git.sr.ht/~sircmpwn/harec - git://c9x.me/qbe.git packages: - binutils tasks: - qbe: | cd qbe make PREFIX=/usr doas make install PREFIX=/usr - build: | cd harec cp configs/openbsd.mk config.mk make -j2 - tests: | cd harec make check harec-0.24.2/.gitignore000066400000000000000000000001121464473277600146670ustar00rootroot00000000000000.bin/ .cache/ src/*.o tests/* !tests/*.ha !tests/*.c !tests/run config.mk harec-0.24.2/.mailmap000066400000000000000000000000351464473277600143240ustar00rootroot00000000000000Ember Sawady harec-0.24.2/COPYING000066400000000000000000001045151464473277600137460ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . harec-0.24.2/MAINTAINERS000066400000000000000000000041151464473277600144030ustar00rootroot00000000000000Guidelines for subsystem maintainers ------------------------------------ Maintainers have write access to hare-announce, make use of it to announce notable or breaking API changes. Any changes which affect modules outside of your jurisdiction should be subject to some general review before being pushed upstream. Descriptions of section entries and preferred order --------------------------------------------------- M: *Mail* patches to: FullName R: Designated *Reviewer*: FullName These reviewers should be CCed on patches. L: *Mailing list* that is relevant to this area W: *Web-page* with status/info C: URI for *chat* protocol, server and channel where developers usually hang out, for example irc://server/channel. P: Subsystem Profile document for more details submitting patches to the given subsystem. This is either an in-tree file, or a URI. T: *SCM* tree location. F: *Files* and directories wildcard patterns. A trailing slash includes all files and subdirectory files. F: drivers/net/ all files in and below drivers/net F: drivers/net/* all files in drivers/net, but not below F: */net/* all files in "any top level directory"/net One pattern per line. Multiple F: lines acceptable. X: *Excluded* files and directories that are NOT maintained, same rules as F:. Files exclusions are tested before file matches. Can be useful for excluding a specific subdirectory, for instance: F: net/ X: net/ipv6/ matches all files in and below net excluding net/ipv6/ Maintainers List ---------------- .. note:: When reading this list, please look for the most precise areas first. When adding to this list, please keep the entries in alphabetical order. OPENBSD SUPPORT M: Lennart Jablonka T: *+openbsd* THE REST M: Drew DeVault M: Ember Sawady M: Sebastian M: Bor Grošelj Simić L: ~sircmpwn/hare-dev@lists.sr.ht T: git https://git.sr.ht/~sircmpwn/harec C: irc://irc.libera.chat/#hare-dev F: * F: */ harec-0.24.2/Makefile000066400000000000000000000041651464473277600143530ustar00rootroot00000000000000.POSIX: all: include config.mk include makefiles/$(PLATFORM).mk include makefiles/tests.mk all: $(BINOUT)/harec C_DEFINES = \ -DVERSION='"'"$(VERSION)"'"' \ -DDEFAULT_TARGET='"$(DEFAULT_TARGET)"' headers = \ include/ast.h \ include/check.h \ include/emit.h \ include/eval.h \ include/expr.h \ include/gen.h \ include/identifier.h \ include/lex.h \ include/mod.h \ include/parse.h \ include/qbe.h \ include/scope.h \ include/type_store.h \ include/typedef.h \ include/types.h \ include/utf8.h \ include/util.h harec_objects = \ src/check.o \ src/emit.o \ src/eval.o \ src/expr.o \ src/gen.o \ src/genutil.o \ src/identifier.o \ src/lex.o \ src/main.o \ src/mod.o \ src/parse.o \ src/qbe.o \ src/qinstr.o \ src/qtype.o \ src/scope.o \ src/type_store.o \ src/typedef.o \ src/types.o \ src/utf8.o \ src/util.o $(BINOUT)/harec: $(harec_objects) @mkdir -p -- $(BINOUT) @printf 'CCLD\t%s\n' '$@' @$(CC) $(LDFLAGS) -o $@ $(harec_objects) $(LIBS) .SUFFIXES: .SUFFIXES: .ha .ssa .td .c .o .s .scd .1 .5 src/check.o: $(headers) src/emit.o: $(headers) src/eval.o: $(headers) src/expr.o: $(headers) src/gen.o: $(headers) src/genutil.o: $(headers) src/identifier.o: $(headers) src/lex.o: $(headers) src/main.o: $(headers) src/mod.o: $(headers) src/parse.o: $(headers) src/qbe.o: $(headers) src/qinstr.o: $(headers) src/qtype.o: $(headers) src/scope.o: $(headers) src/type_store.o: $(headers) src/typedef.o: $(headers) src/types.o: $(headers) src/utf8.o: $(headers) src/util.o: $(headers) .c.o: @printf 'CC\t%s\n' '$@' @$(CC) -c $(CFLAGS) $(C_DEFINES) -o $@ $< .s.o: @printf 'AS\t%s\n' '$@' @$(AS) $(ASFLAGS) -o $@ $< .ssa.s: @printf 'QBE\t%s\n' '$@' @$(QBE) $(QBEFLAGS) -o $@ $< .ssa.td: @cmp -s $@ $@.tmp 2>/dev/null || cp $@.tmp $@ .ha.ssa: @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $< clean: @rm -rf -- $(HARECACHE) $(BINOUT) $(harec_objects) $(tests) check: $(BINOUT)/harec $(tests) @$(TDENV) ./tests/run install: $(BINOUT)/harec install -Dm755 $(BINOUT)/harec $(DESTDIR)$(BINDIR)/harec uninstall: rm -- '$(DESTDIR)$(BINDIR)/harec' .PHONY: clean check install uninstall harec-0.24.2/README.md000066400000000000000000000023211464473277600141620ustar00rootroot00000000000000# harec This is a [Hare](https://harelang.org) compiler written in C11 for POSIX-compatible systems. ## Build status
Linux (x86_64)
Build status for Linux
FreeBSD (x86_64)
Build status for FreeBSD
NetBSD (x86_64)
Build status for NetBSD
## Building ``` cp configs/$platform.mk config.mk make ``` Optionally, build and run the test suite as well: ``` make check ``` ## Runtime harec includes a minimal runtime under `rt` which is suitable for running the test suite, but not recommended for production use. See `docs/runtime.txt` for details on how to provide your own runtime implementation, or use the [Hare standard library](https://git.sr.ht/~sircmpwn/hare). harec-0.24.2/configs/000077500000000000000000000000001464473277600143355ustar00rootroot00000000000000harec-0.24.2/configs/freebsd.mk000066400000000000000000000011061464473277600162760ustar00rootroot00000000000000# install locations PREFIX = /usr/local BINDIR = $(PREFIX)/bin # variables used during build PLATFORM = freebsd ARCH = x86_64 HARECFLAGS = QBEFLAGS = ASFLAGS = LDLINKFLAGS = --gc-sections -z noexecstack CFLAGS = -g -std=c11 -D_XOPEN_SOURCE=700 -Iinclude \ -Wall -Wextra -Werror -pedantic -Wno-unused-parameter LDFLAGS = LIBS = -lm # commands used by the build script CC = cc AS = as LD = ld QBE = qbe # build locations HARECACHE = .cache BINOUT = .bin # variables that will be embedded in the binary with -D definitions DEFAULT_TARGET = $(ARCH) VERSION = $$(./scripts/version) harec-0.24.2/configs/linux.mk000066400000000000000000000011041464473277600160210ustar00rootroot00000000000000# install locations PREFIX = /usr/local BINDIR = $(PREFIX)/bin # variables used during build PLATFORM = linux ARCH = x86_64 HARECFLAGS = QBEFLAGS = ASFLAGS = LDLINKFLAGS = --gc-sections -z noexecstack CFLAGS = -g -std=c11 -D_XOPEN_SOURCE=700 -Iinclude \ -Wall -Wextra -Werror -pedantic -Wno-unused-parameter LDFLAGS = LIBS = -lm # commands used by the build script CC = cc AS = as LD = ld QBE = qbe # build locations HARECACHE = .cache BINOUT = .bin # variables that will be embedded in the binary with -D definitions DEFAULT_TARGET = $(ARCH) VERSION = $$(./scripts/version) harec-0.24.2/configs/netbsd.mk000066400000000000000000000011051464473277600161420ustar00rootroot00000000000000# install locations PREFIX = /usr/local BINDIR = $(PREFIX)/bin # variables used during build PLATFORM = netbsd ARCH = x86_64 HARECFLAGS = QBEFLAGS = ASFLAGS = LDLINKFLAGS = --gc-sections -z noexecstack CFLAGS = -g -std=c11 -D_XOPEN_SOURCE=700 -Iinclude \ -Wall -Wextra -Werror -pedantic -Wno-unused-parameter LDFLAGS = LIBS = -lm # commands used by the build script CC = cc AS = as LD = ld QBE = qbe # build locations HARECACHE = .cache BINOUT = .bin # variables that will be embedded in the binary with -D definitions DEFAULT_TARGET = $(ARCH) VERSION = $$(./scripts/version) harec-0.24.2/configs/openbsd.mk000066400000000000000000000012231464473277600163160ustar00rootroot00000000000000# install locations PREFIX = /usr/local BINDIR = $(PREFIX)/bin # variables used during build PLATFORM = openbsd ARCH = x86_64 HARECFLAGS = -N "" -m .main QBEFLAGS = ASFLAGS = LDLINKFLAGS = -z nobtcfi CFLAGS = -g -std=c11 -D_XOPEN_SOURCE=700 -Iinclude \ -Wall -Wextra -Werror -pedantic -Wno-unused-parameter LDFLAGS = LIBS = -lm # commands used by the build script CC = cc # OpenBSD: gas is in the binutils package. as from the base system is too old. AS = gas LD = cc QBE = qbe # build locations HARECACHE = .cache BINOUT = .bin # variables that will be embedded in the binary with -D definitions DEFAULT_TARGET = $(ARCH) VERSION = $$(./scripts/version) harec-0.24.2/docs/000077500000000000000000000000001464473277600136355ustar00rootroot00000000000000harec-0.24.2/docs/declaration_solver.txt000066400000000000000000000112361464473277600202600ustar00rootroot00000000000000First thing harec does after parsing the inputs is making sure all the global declarations are valid. A constraint unique to this compilation step is that unlike with sequential bindings in a compound expression, there is no explicit order in which the declarations should be traversed to validate them, the correct traversal order is given by the contents of declarations themselves. To make things even more complex, interdependent declarations may reside in different subunits, so there is quite a bit of scope juggling involved to achieve correct scope shadowing. Declarations come in five flavours - functions, constants, type aliases, global variables and enum fields. First, the constants defined upon harec invocation are checked and put into a dedicated scope. Then the imports for each subunit are loaded. This step requires the command line defines to already be in place. After the imports of a subunit are loaded, all of its declarations except enums are marked incomplete and put into the unit scope. Duplicate declarations are caught and reported at this step. Enum types are treated separately from enum values in this algorithm. Enum types never have dependencies and can be completed on the spot. Enum values are put into a special scope that is created for each enum type and marked incomplete. At this point the dedicated scope for defines is reparented on the unit scope, shadowing the declarations from the source files. Next, aliases of enum types are detected and taken care of. Because an enum alias only depends on the underlying enum type, its entire dependency tree is known immediately when it is detected, so it can be completed right away. For values of enum aliases we need to cover three possible configurations: (a) An alias to an enum whose values are already completed at this stage. This happens when the underlying enum was imported from another module. This is the easiest case to handle, we can just copy it's values to the alias immediately. (b) An enum alias whose underlying enum is defined in the same unit this case is handled by the general resolution algorithm described below. With everything but the declarations in place, the core part of the algorithm is started: For each incomplete declaration: (1) Save previous enum and subunit context, and load subunit context for current declaration. (2) If this declaration is marked as in-progress, error out because the declaration part of a dependency cycle, otherwise mark this declaration as in progress. (3) Disambiguate between different declaration flavours: - for functions, constants and globals and enum fields: Check and evaluate as if all the dependencies are already resolved. For enum fields, the relevant enum context has to be loaded and implicit values need to be taken care of. If an incomplete dependency is encountered along the way, first resolve that dependency recursively and then continue with resolution. When the check is done, insert the declaration into the unit scope. Declaration is now considered complete. - for type aliases: Types have two distinct properties that both have to be computed before a type is complete, their dimensions and their representation. The approach taken can be summarized as follows: (a) Compute secondary type's dimensions by traversing just enough of its constituent types to be able to do so. If an incomplete type alias is encountered, first compute that type's dimensions recursively and then continue. (b) Insert the complete alias into the type store and into the unit scope. (c) Compute secondary type's representation by traversing all of its constituent types and repeating this entire process on those that are still incomplete. A valid type alias never references itself during (a), because such a type would not be storable in memory. Types may reference themselves during (c). Such types are called self-referential types. Self-references during (c) require no special treatment, because from step (b) onwards the type we are currently declaring can be treated as a regular complete type. (4) Remove the in progress mark (5) Restore the saved subunit and enum context At this point all the incomplete declarations in the unit scope are shadowed by their complete counterparts. From here on, no special considerations regarding incomplete declarations apply and check can proceed accordingly. harec-0.24.2/docs/env.txt000066400000000000000000000015551464473277600151740ustar00rootroot00000000000000harec uses environment variables to find typedef files for modules referenced in the current unit (including transitive dependencies). The user is responsible for building dependencies first and ensuring that they appear in the environment. The variable HARE_TD_$MOD should contain the path where harec can find the typedef file for the module $MOD. For example, if HARE_TD_encoding::utf8 contains the value "/tmp/encoding_utf8.td", harec would expect /tmp/encoding_utf8.td to contain encoding::utf8's typedefs. If a module is referenced without the associated environment variable being present, harec will error out. In addition, harec also recognizes the following environment variables: - NO_COLOR: Disables color output when set to a non-empty string. - HAREC_COLOR: Disables color output when set to 0, enables it when set to any other value. This overrides NO_COLOR. harec-0.24.2/docs/exit_codes.txt000066400000000000000000000005041464473277600165230ustar00rootroot00000000000000When harec's exit code is non-zero, it indicates the stage at which compilation failed. This is used pretty much exclusively in tests. 0 - success 1 - misc user-caused error (e.g. invalid arguments or unset typedef variables) 2 - lexing error 3 - parsing error 4 - checking error 255 - misc abnormal error (e.g. I/O error) harec-0.24.2/docs/runtime.txt000066400000000000000000000056321464473277600160670ustar00rootroot00000000000000harec expects the runtime to provide some features under the "rt" namespace. @symbol("rt.abort") fn _abort(path: *str, line: u64, col: u64, msg: str) void; Print a diagnostic message and terminate the program. fn abort_fixed(path: *str, line: u64, col: u64, reason: u64) void; Print a diagnostic message from a list of pre-determined abort reasons, and terminate the program. The list of reasons are: 0: Slice or array access out-of-bounds 1: Type assertion failed 2: Out of memory 3: Static append exceeds slice capacity 4: Unreachable code 5: Slice allocation capacity smaller than initializer 6: Generic assertion failure without a message 7: Error assertion failed fn memcpy(dest: *opaque, src: *opaque, n: size) void; Copy "n" bytes from "src" to "dest". The memory areas shall not overlap. fn memmove(dest: *opaque, src: *opaque, n: size) void; Copy "n" bytes from "src" to "dest". The memory areas may overlap. fn memset(dest: *opaque, val: u8, n: size) void; Set "n" bytes, starting from the address at "dest", to "val". fn strcmp(a: str, b: str) bool; Compare strings "a" and "b", returning true of they are equal. "ensure" and "unensure" are called when slices are expanded or deleted. The "length" field of the slice will have been updated, and ensure should allocate or re-allocate the data field to have sufficient space, and update the capacity field accordingly. "unensure" is called when the space is no longer needed, and should shrink the allocation as appropriate. type slice = struct { data: nullable *opaque, length: size, capacity: size, }; fn ensure(s: *slice, membsz: size) void; fn unensure(s: *slice, membsz: size) void; "malloc" and "free" are required to support the "alloc" and "free" built-ins. fn malloc(n: size) nullable *opaque; @symbol("rt.free") fn free_(_p: nullable *opaque) void; The runtime is also expected to provide startup code. A list of function pointers of type `fn() void` is provided in the __init_array_start and __fini_array_start globals, which are respectively terminated by __init_array_end and __fini_array_end. The following Hare code will make these globals available to the current unit: const @symbol("__init_array_start") init_start: [*]*fn() void; const @symbol("__init_array_end") init_end: [*]*fn() void; const @symbol("__fini_array_start") fini_start: [*]*fn() void; const @symbol("__fini_array_end") fini_end: [*]*fn() void; When building with +test (harec -T), @test functions will be emitted, and an ELF section, .test_array, will be populated similarly to init_array. The startup code can enumerate the available tests like so: type test = struct { name: str, func: *fn() void, }; const @symbol("__test_array_start") test_start: [*]test; const @symbol("__test_array_end") test_end: [*]test; In order to use these symbols, a custom linker script must be used. A sample is provided in rt/hare.sc which is compatible with GNU's ld and LLVM's lld. harec-0.24.2/include/000077500000000000000000000000001464473277600143305ustar00rootroot00000000000000harec-0.24.2/include/ast.h000066400000000000000000000212571464473277600152770ustar00rootroot00000000000000#ifndef HARE_AST_H #define HARE_AST_H #include #include #include "expr.h" #include "identifier.h" #include "lex.h" #include "types.h" struct ast_type; enum ast_import_mode { IMPORT_NORMAL, // use foo::bar; IMPORT_ALIAS, // use foo = bar::baz; IMPORT_MEMBERS, // use foo::{bar, baz}; IMPORT_WILDCARD, // use foo::bar::*; }; struct ast_import_members { struct location loc; char *name; struct ast_import_members *next; }; struct ast_imports { enum ast_import_mode mode; struct identifier ident; union { char *alias; struct ast_import_members *members; }; struct ast_imports *next; }; struct ast_array_type { struct ast_expression *length; // NULL for unbounded arrays struct ast_type *members; bool contextual; }; struct ast_slice_type { struct ast_type *members; }; struct ast_enum_field { struct location loc; char *name; struct ast_expression *value; struct ast_enum_field *next; }; struct ast_enum_type { enum type_storage storage; struct ast_enum_field *values; }; struct ast_function_parameters { struct location loc; char *name; struct ast_type *type; struct ast_expression *default_value; struct ast_function_parameters *next; }; struct ast_function_type { struct ast_type *result; struct ast_function_parameters *params; enum variadism variadism; }; struct ast_pointer_type { struct ast_type *referent; unsigned int flags; }; struct ast_tagged_union_type { struct ast_type *type; struct ast_tagged_union_type *next; }; struct ast_tuple_type { struct ast_type *type; struct ast_tuple_type *next; }; struct ast_struct_union_field { struct ast_struct_union_field *next; struct ast_expression *offset; char *name; struct ast_type *type; }; struct ast_struct_union_type { struct ast_struct_union_field fields; bool packed; }; struct ast_type { struct location loc; enum type_storage storage; unsigned int flags; union { struct ast_array_type array; struct ast_function_type func; struct ast_pointer_type pointer; struct ast_slice_type slice; struct ast_struct_union_type struct_union; struct ast_tagged_union_type tagged; struct ast_tuple_type tuple; struct { struct identifier alias; union { struct ast_enum_type _enum; bool unwrap; }; }; }; }; struct ast_types { const struct ast_type *type; struct ast_types *next; }; struct ast_expression_list { struct ast_expression *expr; struct ast_expression_list *next; }; struct ast_expression_access { enum access_type type; union { struct identifier ident; struct { struct ast_expression *array; struct ast_expression *index; }; struct { struct ast_expression *_struct; char *field; }; struct { struct ast_expression *tuple; struct ast_expression *value; }; }; }; struct ast_expression_alloc { enum alloc_kind kind; struct ast_expression *init; struct ast_expression *cap; }; struct ast_expression_append { struct ast_expression *object; struct ast_expression *value; struct ast_expression *length; bool is_static, is_multi; }; struct ast_expression_assert { struct ast_expression *cond; struct ast_expression *message; bool is_static; }; struct ast_expression_assign { enum binarithm_operator op; struct ast_expression *object, *value; }; struct ast_expression_binarithm { enum binarithm_operator op; struct ast_expression *lvalue, *rvalue; }; struct ast_binding_unpack { char *name; struct ast_binding_unpack *next; }; struct ast_expression_binding { char *name; struct ast_binding_unpack *unpack; struct ast_type *type; unsigned int flags; bool is_static; struct ast_expression *initializer; struct ast_expression_binding *next; }; struct ast_expression_call { struct ast_expression *lvalue; struct ast_expression_list *args; bool variadic; // last argument is a variadic argument list }; struct ast_expression_cast { enum cast_kind kind; struct ast_expression *value; struct ast_type *type; }; struct ast_expression_literal { enum type_storage storage; union { int64_t ival; uint64_t uval; double fval; uint32_t rune; bool bval; struct { size_t len; char *value; } string; struct { struct ast_expression_list *exprs; bool expand; } array; }; }; struct ast_expression_control { char *label; struct ast_expression *value; // Only set for yield }; struct ast_expression_defer { struct ast_expression *deferred; }; struct ast_expression_delete { struct ast_expression *expr; bool is_static; }; struct ast_expression_for { enum for_kind kind; char *label; struct ast_expression *bindings; struct ast_expression *cond; struct ast_expression *afterthought; struct ast_expression *body; }; struct ast_expression_free { struct ast_expression *expr; }; struct ast_expression_if { struct ast_expression *cond; struct ast_expression *true_branch, *false_branch; }; struct ast_expression_compound { char *label; struct location label_loc; struct ast_expression_list list; }; struct ast_match_case { char *name; // May be null struct ast_type *type; struct ast_expression_list exprs; struct ast_match_case *next; }; struct ast_expression_match { char *label; struct ast_expression *value; struct ast_match_case *cases; }; enum measure_operator { M_ALIGN, M_LEN, M_SIZE, M_OFFSET, }; struct ast_expression_measure { enum measure_operator op; union { struct ast_expression *value; struct ast_type *type; // TODO: Field selection }; }; struct ast_expression_propagate { struct ast_expression *value; bool abort; }; struct ast_expression_return { struct ast_expression *value; }; struct ast_expression_slice { struct ast_expression *object; struct ast_expression *start, *end; }; struct ast_case_option { struct ast_expression *value; struct ast_case_option *next; }; struct ast_switch_case { struct ast_case_option *options; // NULL for * struct ast_expression_list exprs; struct ast_switch_case *next; }; struct ast_expression_switch { char *label; struct ast_expression *value; struct ast_switch_case *cases; }; struct ast_field_value { char *name; struct ast_type *type; struct ast_expression *initializer; struct ast_field_value *next; }; struct ast_expression_struct { bool autofill; struct identifier type; struct ast_field_value *fields; }; struct ast_expression_tuple { struct ast_expression *expr; struct ast_expression_tuple *next; }; struct ast_expression_unarithm { enum unarithm_operator op; struct ast_expression *operand; }; struct ast_expression_vaarg { struct ast_expression *ap; }; struct ast_expression { struct location loc; enum expr_type type; union { struct ast_expression_access access; struct ast_expression_alloc alloc; struct ast_expression_append append; // also insert struct ast_expression_assert assert; struct ast_expression_assign assign; struct ast_expression_binarithm binarithm; struct ast_expression_binding binding; struct ast_expression_call call; struct ast_expression_cast cast; struct ast_expression_compound compound; struct ast_expression_control control; struct ast_expression_defer defer; struct ast_expression_delete delete; struct ast_expression_for _for; struct ast_expression_free free; struct ast_expression_if _if; struct ast_expression_literal literal; struct ast_expression_match match; struct ast_expression_measure measure; struct ast_expression_propagate propagate; struct ast_expression_return _return; struct ast_expression_slice slice; struct ast_expression_struct _struct; struct ast_expression_switch _switch; struct ast_expression_tuple tuple; struct ast_expression_unarithm unarithm; struct ast_expression_vaarg vaarg; }; }; struct ast_global_decl { char *symbol; bool threadlocal; struct identifier ident; struct ast_type *type; struct ast_expression *init; struct ast_global_decl *next; }; struct ast_type_decl { struct identifier ident; struct ast_type *type; struct ast_type_decl *next; }; enum func_decl_flags { FN_FINI = 1 << 0, FN_INIT = 1 << 1, FN_TEST = 1 << 2, }; struct ast_function_decl { char *symbol; struct identifier ident; struct ast_function_type prototype; struct ast_expression *body; enum func_decl_flags flags; }; enum ast_decl_type { ADECL_FUNC, ADECL_TYPE, ADECL_GLOBAL, ADECL_CONST, ADECL_ASSERT, }; struct ast_decl { struct location loc; enum ast_decl_type decl_type; bool exported; union { struct ast_global_decl global; struct ast_global_decl constant; struct ast_type_decl type; struct ast_function_decl function; struct ast_expression_assert assert; }; }; struct ast_decls { struct ast_decl decl; struct ast_decls *next; }; struct ast_subunit { struct ast_imports *imports; struct ast_decls *decls; struct ast_subunit *next; }; struct ast_unit { struct identifier *ns; struct ast_subunit subunits; }; #endif harec-0.24.2/include/check.h000066400000000000000000000070311464473277600155570ustar00rootroot00000000000000#ifndef HARE_CHECK_H #define HARE_CHECK_H #include #include #include "ast.h" #include "identifier.h" #include "scope.h" #include "types.h" #include "type_store.h" struct expression; #define MODCACHE_BUCKETS 256 struct modcache { struct identifier ident; struct scope *scope; struct modcache *next; }; struct errors { struct location loc; char *msg; struct errors *next; }; struct context { type_store *store; struct modcache **modcache; const struct type *fntype; struct identifier *ns; struct scope *unit; struct scope *scope; struct scope *defines; const char *mainsym; bool is_test; int id; struct errors *errors; struct errors **next; struct declarations *decls; struct ast_types *unresolved; }; struct constant_decl { const struct type *type; const struct expression *value; }; struct function_decl { const struct type *type; struct expression *body; struct scope *scope; unsigned int flags; // enum func_decl_flags }; struct global_decl { const struct type *type; struct expression *value; // EXPR_LITERAL bool threadlocal; }; enum decl_type { DECL_FUNC, DECL_TYPE, DECL_GLOBAL, DECL_CONST, }; struct declaration { enum decl_type decl_type; int file; struct identifier ident; char *symbol; bool exported; // XXX: this bool takes up 8 bytes and i am in pain union { struct constant_decl constant; struct function_decl func; struct global_decl global; const struct type *type; }; }; struct declarations { struct declaration decl; struct declarations *next; }; struct unit { struct identifier *ns; struct declarations *declarations; struct identifiers *imports; }; enum idecl_type { IDECL_DECL, IDECL_ENUM_FLD, }; // Keeps track of enum specific context required for enum field resolution struct incomplete_enum_field { struct ast_enum_field *field; struct scope *enum_scope; }; // Keeps track of context required to resolve a declaration or an enum field // Extends the scope_object struct so it can be inserted into a scope struct incomplete_declaration { struct scope_object obj; struct scope *imports; // the scope of this declaration's subunit enum idecl_type type; bool in_progress; bool dealias_in_progress; union { struct ast_decl decl; struct incomplete_enum_field *field; }; }; void mkident(struct context *ctx, struct identifier *out, const struct identifier *in, const char *symbol); void mkstrliteral(struct expression *expr, const char *fmt, ...); char *gen_typename(const struct type *type); struct expression *lower_implicit_cast(struct context *ctx, const struct type *to, struct expression *expr); typedef void (*resolvefn)(struct context *, struct incomplete_declaration *idecl); void resolve_dimensions(struct context *ctx, struct incomplete_declaration *idecl); void resolve_type(struct context *ctx, struct incomplete_declaration *idecl); void wrap_resolver(struct context *ctx, struct scope_object *obj, resolvefn resolver); struct scope *check(type_store *ts, bool is_test, const char *mainsym, const struct ast_global_decl *defines, const struct ast_unit *aunit, struct unit *unit); struct scope *check_internal(type_store *ts, struct modcache **cache, bool is_test, const char *mainsym, const struct ast_global_decl *defines, const struct ast_unit *aunit, struct unit *unit, bool scan_only); void check_expression(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint); void error(struct context *ctx, struct location loc, struct expression *expr, const char *fmt, ...); #endif harec-0.24.2/include/emit.h000066400000000000000000000002231464473277600154340ustar00rootroot00000000000000#ifndef HAREC_EMIT_H #define HAREC_EMIT_H #include struct qbe_program; void emit(const struct qbe_program *program, FILE *out); #endif harec-0.24.2/include/eval.h000066400000000000000000000004051464473277600154270ustar00rootroot00000000000000#ifndef HAREC_EVAL_H #define HAREC_EVAL_H #include struct expression; struct context; // Evaluates an expression at compile time. bool eval_expr(struct context *ctx, const struct expression *restrict in, struct expression *restrict out); #endif harec-0.24.2/include/expr.h000066400000000000000000000164451464473277600154710ustar00rootroot00000000000000#ifndef HAREC_EXPR_H #define HAREC_EXPR_H #include #include "lex.h" #include "types.h" struct scope; struct scope_object; enum expr_type { EXPR_ACCESS, EXPR_ALLOC, EXPR_APPEND, EXPR_ASSERT, EXPR_ASSIGN, EXPR_BINARITHM, EXPR_BINDING, EXPR_BREAK, EXPR_CALL, EXPR_CAST, EXPR_COMPOUND, EXPR_CONTINUE, EXPR_DEFER, EXPR_DEFINE, EXPR_DELETE, EXPR_FOR, EXPR_FREE, EXPR_IF, EXPR_INSERT, EXPR_LEN, EXPR_MEASURE = EXPR_LEN, // for use in AST EXPR_LITERAL, EXPR_MATCH, EXPR_PROPAGATE, EXPR_RETURN, EXPR_SLICE, EXPR_STRUCT, EXPR_SWITCH, EXPR_TUPLE, EXPR_UNARITHM, EXPR_VAARG, EXPR_VAEND, EXPR_VASTART, EXPR_YIELD, }; struct expressions { struct expression *expr; struct expressions *next; }; enum access_type { ACCESS_IDENTIFIER, ACCESS_INDEX, ACCESS_FIELD, ACCESS_TUPLE, }; struct expression_access { enum access_type type; union { struct scope_object *object; struct { struct expression *array; struct expression *index; bool bounds_checked; }; struct { struct expression *_struct; const struct struct_field *field; }; struct { struct expression *tuple; const struct type_tuple *tvalue; size_t tindex; }; }; }; enum alloc_kind { ALLOC_OBJECT, // alloc(42) ALLOC_CAP, // alloc([], 42) ALLOC_LEN, // alloc([0...], 42) ALLOC_COPY, // alloc(x...); }; struct expression_alloc { enum alloc_kind kind; struct expression *init; struct expression *cap; }; struct expression_append { struct expression *object; struct expression *value; struct expression *length; bool is_static, is_multi; }; enum fixed_aborts { ABORT_OOB = 0, ABORT_TYPE_ASSERTION = 1, ABORT_ALLOC_FAILURE = 2, ABORT_STATIC_EXCEEDED = 3, ABORT_UNREACHABLE = 4, ABORT_CAP_TOO_SMALL = 5, ABORT_ANON_ASSERTION_FAILED = 6, ABORT_PROPAGATE_ERROR_OCCURRED = 7, }; struct expression_assert { struct expression *cond; struct expression *message; enum fixed_aborts fixed_reason; }; enum binarithm_operator { BIN_BAND, // & BIN_BOR, // | BIN_DIV, // / BIN_GREATER, // > BIN_GREATEREQ, // >= BIN_LAND, // && BIN_LEQUAL, // == BIN_LESS, // < BIN_LESSEQ, // <= BIN_LOR, // || BIN_LSHIFT, // << BIN_LXOR, // ^^ BIN_MINUS, // - BIN_MODULO, // % BIN_NEQUAL, // != BIN_PLUS, // + BIN_RSHIFT, // >> BIN_TIMES, // * BIN_BXOR, // ^ BIN_LAST = BIN_BXOR, }; struct expression_assign { enum binarithm_operator op; struct expression *object, *value; }; struct expression_binarithm { enum binarithm_operator op; struct expression *lvalue, *rvalue; }; struct binding_unpack { const struct scope_object *object; size_t offset; struct binding_unpack *next; }; struct expression_binding { const struct scope_object *object; struct binding_unpack *unpack; struct expression *initializer; struct expression_binding *next; }; enum cast_kind { C_CAST, C_ASSERTION, C_TEST, }; struct expression_cast { enum cast_kind kind; const struct type *secondary; struct expression *value; bool lowered; }; struct call_argument { struct expression *value; struct call_argument *next; }; struct expression_call { struct expression *lvalue; struct call_argument *args; }; struct expression_compound { char *label; struct scope *scope; struct expressions exprs; }; struct array_literal { struct expression *value; struct array_literal *next; }; // Invariant: these are sorted by field offset struct struct_literal { const struct struct_field *field; struct expression *value; struct struct_literal *next; }; struct tuple_literal { const struct type_tuple *field; struct expression *value; struct tuple_literal *next; }; struct tagged_literal { const struct type *tag; struct expression *value; }; struct expression_literal { // If non-null, ival is an offset from this object's address const struct scope_object *object; union { bool bval; double fval; int64_t ival; uint64_t uval; uint32_t rune; struct { size_t len; char *value; } string; struct { union { // if object is null struct array_literal *array; // if object is non-null size_t offset; }; size_t start; size_t len; size_t cap; } slice; struct array_literal *array; struct struct_literal *_struct; struct tuple_literal *tuple; struct tagged_literal tagged; }; }; struct expression_control { char *label; const struct scope *scope; struct expression *value; // Only set for yield }; struct expression_defer { struct scope *scope; struct expression *deferred; }; struct expression_delete { struct expression *expr; bool is_static; }; enum for_kind { FOR_ACCUMULATOR, FOR_EACH_VALUE, FOR_EACH_POINTER, FOR_EACH_ITERATOR, }; struct expression_for { enum for_kind kind; char *label; struct scope *scope; struct expression *bindings; struct expression *cond; struct expression *afterthought; struct expression *body; }; struct expression_free { struct expression *expr; }; struct expression_if { struct expression *cond; struct expression *true_branch, *false_branch; }; struct match_case { const struct scope_object *object; // NULL if not bound const struct type *type; // NULL if default struct expression *value; struct match_case *next; }; struct expression_len { struct expression *value; }; struct expression_match { struct expression *value; struct match_case *cases; }; struct expression_return { struct expression *value; }; struct expression_slice { struct expression *object; struct expression *start, *end; bool bounds_checked; }; struct case_option { struct expression *value; struct case_option *next; }; struct switch_case { struct case_option *options; // NULL for default case struct expression *value; struct switch_case *next; }; struct expression_switch { struct expression *value; struct switch_case *cases; }; struct expr_struct_field { const struct struct_field *field; struct expression *value; struct expr_struct_field *next; }; struct expression_struct { struct expr_struct_field *fields; bool autofill; }; struct expression_tuple { struct expression *value; struct expression_tuple *next; }; enum unarithm_operator { UN_ADDRESS, // & UN_BNOT, // ~ UN_DEREF, // * UN_LNOT, // ! UN_MINUS, // - }; struct expression_unarithm { enum unarithm_operator op; struct expression *operand; }; struct expression_vaarg { struct expression *ap; }; struct expression { const struct type *result; enum expr_type type; struct location loc; // For fixed aborts union { struct expression_access access; struct expression_alloc alloc; struct expression_append append; // and insert struct expression_assert assert; struct expression_assign assign; struct expression_binarithm binarithm; struct expression_binding binding; struct expression_call call; struct expression_cast cast; struct expression_compound compound; struct expression_defer defer; struct expression_delete delete; struct expression_control control; struct expression_for _for; struct expression_free free; struct expression_if _if; struct expression_len len; struct expression_literal literal; struct expression_match match; struct expression_return _return; struct expression_switch _switch; struct expression_struct _struct; struct expression_slice slice; struct expression_tuple tuple; struct expression_unarithm unarithm; struct expression_vaarg vaarg; void *user; }; }; uint32_t expr_hash(const struct expression *expr); #endif harec-0.24.2/include/gen.h000066400000000000000000000052501464473277600152540ustar00rootroot00000000000000#ifndef HAREC_GEN_H #define HAREC_GEN_H #include #include "identifier.h" #include "qbe.h" #include "type_store.h" #include "types.h" #include "scope.h" struct gen_arch { const struct qbe_type *ptr; const struct qbe_type *sz; }; enum gen_value_kind { GV_CONST, GV_GLOBAL, GV_TEMP, }; struct gen_value { enum gen_value_kind kind; bool threadlocal; const struct type *type; union { char *name; uint32_t wval; uint64_t lval; float sval; double dval; }; }; struct gen_slice { struct qbe_value base, len, cap; }; struct gen_binding { const struct scope_object *object; struct gen_value value; struct gen_binding *next; }; struct gen_defer { const struct expression *expr; struct gen_defer *next; }; struct gen_scope { const char *label; const struct scope *scope; struct gen_value result; struct gen_value *out; struct qbe_value *after; struct qbe_value *end; struct gen_defer *defers; struct gen_scope *parent; }; struct rt { struct qbe_value abort, ensure, fixedabort, free, malloc, memcpy, memmove, memset, strcmp, unensure; }; struct gen_context { struct qbe_program *out; struct gen_arch arch; struct identifier *ns; struct rt rt; struct gen_value *sources; int id; struct qbe_func *current; const struct type *functype; struct gen_binding *bindings; struct gen_scope *scope; }; struct unit; void gen(const struct unit *unit, struct qbe_program *out); // genutil.c void rtfunc_init(struct gen_context *ctx); struct gen_value mkgtemp(struct gen_context *ctx, const struct type *type, const char *fmt); struct qbe_value mkqval(struct gen_context *ctx, const struct gen_value *value); struct qbe_value mklval(struct gen_context *ctx, const struct gen_value *value); struct qbe_value mkcopy(struct gen_context *ctx, const struct gen_value *value, const char *fmt); struct qbe_value mkqtmp(struct gen_context *ctx, const struct qbe_type *qtype, const char *fmt); struct qbe_value mklabel(struct gen_context *ctx, struct qbe_statement *stmt, const char *fmt); void branch_copyresult(struct gen_context *ctx, struct gen_value result, struct gen_value merged, struct gen_value *out); struct qbe_value compute_tagged_memb_offset(const struct type *subtype); // qinstr.c enum qbe_instr alloc_for_align(size_t align); enum qbe_instr store_for_type(struct gen_context *ctx, const struct type *type); enum qbe_instr load_for_type(struct gen_context *ctx, const struct type *type); enum qbe_instr binarithm_for_op(struct gen_context *ctx, enum binarithm_operator op, const struct type *type); // qtype.c const struct qbe_type *qtype_lookup(struct gen_context *ctx, const struct type *type, bool xtype); bool type_is_aggregate(const struct type *type); #endif harec-0.24.2/include/identifier.h000066400000000000000000000020051464473277600166200ustar00rootroot00000000000000#ifndef HARE_IDENTIFIER_H #define HARE_IDENTIFIER_H #include #include // Maximum length of an identifier, as the sum of the lengths (excluding NUL // terminators) of its parts plus one for each namespace deliniation. // // In other words, the length of "a::b::c" is 5. #define IDENT_MAX 255 // Minimum buffer size needed to store an unparsed identifier, including the // terminating NUL byte. #define IDENT_BUFSIZ (IDENT_MAX / 2 + IDENT_MAX + 1) struct identifier { char *name; struct identifier *ns; }; struct identifiers { struct identifier ident; struct identifiers *next; }; uint32_t identifier_hash(uint32_t init, const struct identifier *ident); char *identifier_unparse(const struct identifier *ident); int identifier_unparse_static(const struct identifier *ident, char *buf); char *ident_to_sym(const struct identifier *ident); void identifier_dup(struct identifier *new, const struct identifier *ident); bool identifier_eq(const struct identifier *a, const struct identifier *b); #endif harec-0.24.2/include/lex.h000066400000000000000000000045541464473277600153010ustar00rootroot00000000000000#ifndef HAREC_LEX_H #define HAREC_LEX_H #include #include #include "types.h" #include "utf8.h" #define C_EOF UTF8_INVALID // Keep sorted enum lexical_token { T_ATTR_FINI, T_ATTR_INIT, T_ATTR_OFFSET, T_ATTR_PACKED, T_ATTR_SYMBOL, T_ATTR_TEST, T_ATTR_THREADLOCAL, T_UNDERSCORE, T_ABORT, T_ALIGN, T_ALLOC, T_APPEND, T_AS, T_ASSERT, T_BOOL, T_BREAK, T_CASE, T_CONST, T_CONTINUE, T_DEF, T_DEFER, T_DELETE, T_DONE, T_ELSE, T_ENUM, T_EXPORT, T_F32, T_F64, T_FALSE, T_FN, T_FOR, T_FREE, T_I16, T_I32, T_I64, T_I8, T_IF, T_INSERT, T_INT, T_IS, T_LEN, T_LET, T_MATCH, T_NEVER, T_NULL, T_NULLABLE, T_OFFSET, T_OPAQUE, T_RETURN, T_RUNE, T_SIZE, T_STATIC, T_STR, T_STRUCT, T_SWITCH, T_TRUE, T_TYPE, T_U16, T_U32, T_U64, T_U8, T_UINT, T_UINTPTR, T_UNION, T_USE, T_VAARG, T_VAEND, T_VALIST, T_VASTART, T_VOID, T_YIELD, T_LAST_KEYWORD = T_YIELD, // Operators T_ARROW, T_BANDEQ, T_BAND, T_BNOT, T_BOR, T_COLON, T_COMMA, T_DIV, T_DIVEQ, T_DOT, T_DOUBLE_COLON, T_DOUBLE_DOT, T_ELLIPSIS, T_EQUAL, T_GREATER, T_GREATEREQ, T_LAND, T_LANDEQ, T_LBRACE, T_LBRACKET, T_LEQUAL, T_LESS, T_LESSEQ, T_LNOT, T_LOR, T_LOREQ, T_LPAREN, T_LSHIFT, T_LSHIFTEQ, T_LXOR, T_LXOREQ, T_MINUS, T_MINUSEQ, T_MODEQ, T_MODULO, T_NEQUAL, T_BOREQ, T_PLUS, T_PLUSEQ, T_QUESTION, T_RBRACE, T_RBRACKET, T_RPAREN, T_RSHIFT, T_RSHIFTEQ, T_SEMICOLON, T_TIMES, T_TIMESEQ, T_BXOR, T_BXOREQ, T_LAST_OPERATOR = T_BXOREQ, // Tokens with additional information T_NAME, T_NUMBER, // Magic tokens T_EOF, T_NONE, }; struct location { int file; int lineno, colno; }; struct token { struct location loc; enum lexical_token token; enum type_storage storage; union { char *name; uint32_t rune; int64_t ival; uint64_t uval; double fval; struct { size_t len; char *value; } string; }; }; struct lexer { FILE *in; char *buf; size_t bufsz, buflen; uint32_t c[2]; struct token un; struct location loc; bool require_int; }; void lex_init(struct lexer *lexer, FILE *f, int fileid); void lex_finish(struct lexer *lexer); enum lexical_token lex(struct lexer *lexer, struct token *out); void unlex(struct lexer *lexer, const struct token *in); void token_finish(struct token *tok); const char *token_str(const struct token *tok); const char *lexical_token_str(enum lexical_token tok); #endif harec-0.24.2/include/mod.h000066400000000000000000000003651464473277600152640ustar00rootroot00000000000000#ifndef HARE_MOD_H #define HARE_MOD_H struct ast_global_decl; struct context; struct scope; struct identifier; struct scope *module_resolve(struct context *ctx, const struct ast_global_decl *defines, const struct identifier *ident); #endif harec-0.24.2/include/parse.h000066400000000000000000000006251464473277600156160ustar00rootroot00000000000000#ifndef HAREC_PARSE_H #define HAREC_PARSE_H #include struct ast_expression; struct ast_subunit; struct ast_type; struct lexer; void parse(struct lexer *lexer, struct ast_subunit *unit); bool parse_identifier(struct lexer *lexer, struct identifier *ident, bool trailing); struct ast_type *parse_type(struct lexer *lexer); struct ast_expression *parse_expression(struct lexer *lexer); #endif harec-0.24.2/include/qbe.h000066400000000000000000000074431464473277600152600ustar00rootroot00000000000000#ifndef HAREC_QBE_H #define HAREC_QBE_H #include #include #include enum qbe_stype { Q__VOID = 'V', Q_BYTE = 'b', Q_HALF = 'h', Q_WORD = 'w', Q_LONG = 'l', Q_SINGLE = 's', Q_DOUBLE = 'd', Q__AGGREGATE = 'A', Q__UNION = 'U', }; struct qbe_type; struct qbe_field { const struct qbe_type *type; size_t count; struct qbe_field *next; }; struct qbe_type { enum qbe_stype stype; size_t size; // Aggregate types only: char *name; struct qbe_field fields; const struct type *base; }; // Simple type singletons extern const struct qbe_type qbe_byte, qbe_half, qbe_word, qbe_long, qbe_single, qbe_double, qbe_void, qbe_aggregate; enum qbe_value_kind { QV_CONST, QV_GLOBAL, QV_LABEL, QV_TEMPORARY, QV_VARIADIC, }; struct qbe_value { enum qbe_value_kind kind; bool threadlocal; const struct qbe_type *type; union { char *name; uint32_t wval; uint64_t lval; float sval; double dval; }; }; enum qbe_instr { Q_ADD, Q_ALLOC16, Q_ALLOC4, Q_ALLOC8, Q_AND, Q_BLIT, Q_CALL, Q_CAST, Q_CEQD, Q_CEQL, Q_CEQS, Q_CEQW, Q_CGED, Q_CGES, Q_CGTD, Q_CGTS, Q_CLED, Q_CLES, Q_CLTD, Q_CLTS, Q_CNED, Q_CNEL, Q_CNES, Q_CNEW, Q_COD, Q_COPY, Q_COS, Q_CSGEL, Q_CSGEW, Q_CSGTL, Q_CSGTW, Q_CSLEL, Q_CSLEW, Q_CSLTL, Q_CSLTW, Q_CUGEL, Q_CUGEW, Q_CUGTL, Q_CUGTW, Q_CULEL, Q_CULEW, Q_CULTL, Q_CULTW, Q_CUOD, Q_CUOS, Q_DBGLOC, Q_DIV, Q_DTOSI, Q_DTOUI, Q_EXTS, Q_EXTSB, Q_EXTSH, Q_EXTSW, Q_EXTUB, Q_EXTUH, Q_EXTUW, Q_HLT, Q_JMP, Q_JNZ, Q_LOADD, Q_LOADL, Q_LOADS, Q_LOADSB, Q_LOADSH, Q_LOADSW, Q_LOADUB, Q_LOADUH, Q_LOADUW, Q_MUL, Q_OR, Q_REM, Q_RET, Q_NEG, Q_SAR, Q_SHL, Q_SHR, Q_SLTOF, Q_STOREB, Q_STORED, Q_STOREH, Q_STOREL, Q_STORES, Q_STOREW, Q_STOSI, Q_STOUI, Q_SUB, Q_SWTOF, Q_TRUNCD, Q_UDIV, Q_ULTOF, Q_UREM, Q_UWTOF, Q_VAARG, Q_VASTART, Q_XOR, Q_LAST_INSTR, }; extern const char *qbe_instr[Q_LAST_INSTR]; enum qbe_statement_type { Q_COMMENT, Q_INSTR, Q_LABEL, }; struct qbe_arguments { struct qbe_value value; struct qbe_arguments *next; }; struct qbe_statement { enum qbe_statement_type type; union { struct { enum qbe_instr instr; struct qbe_value *out; struct qbe_arguments *args; }; char *label; char *comment; }; }; struct qbe_func_param { char *name; const struct qbe_type *type; struct qbe_func_param *next; }; struct qbe_statements { size_t ln, sz; struct qbe_statement *stmts; }; struct qbe_func { const struct qbe_type *returns; struct qbe_func_param *params; bool variadic; struct qbe_statements prelude, body; }; enum qbe_datatype { QD_ZEROED, QD_VALUE, QD_STRING, QD_SYMOFFS, }; struct qbe_data_item { enum qbe_datatype type; union { struct qbe_value value; size_t zeroed; struct { char *str; size_t sz; }; struct { char *sym; int64_t offset; }; }; struct qbe_data_item *next; }; struct qbe_data { size_t align; char *section, *secflags; bool threadlocal; struct qbe_data_item items; }; enum qbe_defkind { Q_TYPE, Q_FUNC, Q_DATA, }; struct qbe_def { char *name; int file; enum qbe_defkind kind; bool exported; union { struct qbe_func func; struct qbe_type type; struct qbe_data data; }; struct qbe_def *next; }; struct qbe_program { struct qbe_def *defs; struct qbe_def **next; }; void qbe_append_def(struct qbe_program *prog, struct qbe_def *def); void pushi(struct qbe_func *func, const struct qbe_value *out, enum qbe_instr instr, ...); void pushprei(struct qbe_func *func, const struct qbe_value *out, enum qbe_instr instr, ...); void pushc(struct qbe_func *func, const char *fmt, ...); void push(struct qbe_statements *stmts, struct qbe_statement *stmt); struct qbe_value constl(uint64_t l); struct qbe_value constw(uint32_t w); struct qbe_value consts(float s); struct qbe_value constd(double d); #endif harec-0.24.2/include/scope.h000066400000000000000000000045741464473277600156240ustar00rootroot00000000000000#ifndef HAREC_SCOPE_H #define HAREC_SCOPE_H #include "expr.h" #include "identifier.h" #define SCOPE_BUCKETS 4096 enum object_type { O_BIND, O_CONST, O_DECL, O_SCAN, O_TYPE, }; enum scope_object_flags { SO_THREADLOCAL = 1 << 0, SO_FOR_EACH_SUBJECT = 1 << 1, }; struct scope_object { enum object_type otype; // name is the name of the object within this scope (for lookups) // ident is the global identifier (these may be different in some cases) struct identifier name, ident; enum scope_object_flags flags; union { const struct type *type; struct expression *value; // For O_CONST }; struct scope_object *lnext; // Linked list struct scope_object *mnext; // Hash map }; enum scope_class { SCOPE_COMPOUND, SCOPE_DEFER, SCOPE_ENUM, SCOPE_FUNC, SCOPE_LOOP, SCOPE_MATCH, SCOPE_SUBUNIT, SCOPE_UNIT, SCOPE_DEFINES, }; struct yield { struct expression **expression; struct yield *next; }; struct scope { // Used for for loops bool has_break; enum scope_class class; const char *label; struct scope *parent; const struct type *hint; struct type_tagged_union *results; struct yield *yields; // Linked list in insertion order // Used for function parameters and enum values, where order matters struct scope_object *objects; struct scope_object **next; // Hash map in reverse insertion order // Used for lookups, and accounts for shadowing struct scope_object *buckets[SCOPE_BUCKETS]; }; struct scopes { struct scope *scope; struct scopes *next; }; struct scope *scope_push(struct scope **stack, enum scope_class class); struct scope *scope_pop(struct scope **stack); struct scope *scope_lookup_class(struct scope *scope, enum scope_class class); struct scope *scope_lookup_label(struct scope *scope, const char *label); void scope_free(struct scope *scope); void scope_free_all(struct scopes *scopes); void scope_object_init(struct scope_object *obj, enum object_type otype, const struct identifier *ident, const struct identifier *name, const struct type *type, struct expression *value); void scope_insert_from_object(struct scope *scope, struct scope_object *object); struct scope_object *scope_insert( struct scope *scope, enum object_type otype, const struct identifier *ident, const struct identifier *name, const struct type *type, struct expression *value); struct scope_object *scope_lookup(struct scope *scope, const struct identifier *ident); #endif harec-0.24.2/include/type_store.h000066400000000000000000000042271464473277600167030ustar00rootroot00000000000000#ifndef HARE_TYPESTORE_H #define HARE_TYPESTORE_H #include "ast.h" #include "lex.h" #include "types.h" #define TYPE_STORE_BUCKETS 65536 struct type_bucket { struct type type; struct type_bucket *next; }; struct context; typedef struct type_bucket *type_store[TYPE_STORE_BUCKETS]; // Applies the type reduction algorithm to the given tagged union. const struct type *type_store_reduce_result(struct context *ctx, struct location loc, struct type_tagged_union *in); struct ast_type; const struct type *type_store_lookup_atype( struct context *ctx, const struct ast_type *atype); struct dimensions type_store_lookup_dimensions( struct context *ctx, const struct ast_type *atype); const struct type *builtin_type_for_storage( enum type_storage storage, bool is_const); const struct type *type_store_lookup_with_flags(struct context *ctx, const struct type *type, unsigned int flags); const struct type *type_store_lookup_pointer(struct context *ctx, struct location loc, const struct type *referent, unsigned int ptrflags); const struct type *type_store_lookup_array(struct context *ctx, struct location loc, const struct type *members, size_t len, bool expandable); const struct type *type_store_lookup_slice(struct context *ctx, struct location loc, const struct type *members); // Looks up a type alias, which may be incomplete. If the dimensions of the // type are known, provide them as a hint in the dims argument (which can be // NULL otherwise). This is used as a hint to skip adding padding to packed // struct types. const struct type *type_store_lookup_alias(struct context *ctx, const struct type *secondary, const struct dimensions *dims); const struct type *type_store_lookup_tagged(struct context *ctx, struct location loc, struct type_tagged_union *tags); // Returns a (non-tagged) union of the members of a tagged union type const struct type *type_store_tagged_to_union( struct context *ctx, const struct type *tagged); const struct type *type_store_lookup_tuple(struct context *ctx, struct location loc, struct type_tuple *values); const struct type *type_store_lookup_enum(struct context *ctx, const struct ast_type *atype, bool exported); #endif harec-0.24.2/include/typedef.h000066400000000000000000000003211464473277600161350ustar00rootroot00000000000000#ifndef HARE_TYPEDEF_H #define HARE_TYPEDEF_H #include struct type; struct unit; void emit_type(const struct type *type, FILE *out); void emit_typedefs(const struct unit *unit, FILE *out); #endif harec-0.24.2/include/types.h000066400000000000000000000135571464473277600156600ustar00rootroot00000000000000#ifndef HARE_TYPES_H #define HARE_TYPES_H #include #include #include "identifier.h" enum type_storage { // Built-in types // The order of these is important STORAGE_BOOL, STORAGE_DONE, STORAGE_F32, STORAGE_F64, STORAGE_I16, STORAGE_I32, STORAGE_I64, STORAGE_I8, STORAGE_INT, STORAGE_NEVER, STORAGE_NULL, STORAGE_OPAQUE, STORAGE_RUNE, STORAGE_SIZE, STORAGE_STRING, STORAGE_U16, STORAGE_U32, STORAGE_U64, STORAGE_U8, STORAGE_UINT, STORAGE_UINTPTR, STORAGE_VOID, // Other types STORAGE_ALIAS, STORAGE_ARRAY, STORAGE_ENUM, STORAGE_FUNCTION, STORAGE_POINTER, STORAGE_SLICE, STORAGE_STRUCT, STORAGE_TAGGED, STORAGE_TUPLE, STORAGE_UNION, STORAGE_VALIST, STORAGE_FCONST, STORAGE_ICONST, STORAGE_RCONST, // For internal use only STORAGE_ERROR, }; struct context; struct type; #define SIZE_UNDEFINED ((size_t)-1) #define ALIGN_UNDEFINED ((size_t)-1) struct type_alias { struct identifier ident; struct identifier name; const struct type *type; bool exported; // Used to make sure unexported aliases aren't emitted }; struct type_array { size_t length; // SIZE_UNDEFINED for [*] and slices const struct type *members; bool expandable; }; struct type_enum { struct scope *values; }; enum variadism { VARIADISM_NONE, VARIADISM_C, VARIADISM_HARE, }; struct type_func_param { const struct type *type; struct expression *default_value; struct type_func_param *next; }; struct type_func { const struct type *result; enum variadism variadism; struct type_func_param *params; }; struct type_flexible { int64_t min, max; uint32_t id; const struct type ***refs; size_t nrefs; size_t zrefs; }; enum pointer_flags { PTR_NULLABLE = 1 << 0, }; struct type_pointer { const struct type *referent; unsigned int flags; }; struct struct_field { char *name; const struct type *type; size_t offset; size_t size; struct struct_field *next; }; struct type_struct_union { struct struct_field *fields; // c_compat is false if explicit offsets are used, or if the type is a // union. The latter is actually still C-compatible, but this is an // implementation detail which changes the way the QBE IL is generated, // and in the case of unions, the altered behavior for explicit-offset // structs is also correct for all cases of unions. bool c_compat; bool packed; }; struct type_tuple { const struct type *type; size_t offset; struct type_tuple *next; }; struct type_tagged_union { const struct type *type; struct type_tagged_union *next; }; enum type_flags { TYPE_CONST = 1 << 0, TYPE_ERROR = 1 << 1, }; struct type { enum type_storage storage; uint32_t id; unsigned int flags; size_t size, align; union { struct { struct type_alias alias; struct type_enum _enum; }; struct type_array array; struct type_flexible flexible; struct type_func func; struct type_pointer pointer; struct type_struct_union struct_union; struct type_tagged_union tagged; struct type_tuple tuple; }; }; struct dimensions { size_t size; size_t align; }; const struct type *type_dereference(struct context *ctx, const struct type *type); const struct type *type_dealias(struct context *ctx, const struct type *type); const struct struct_field *type_get_field(struct context *ctx, const struct type *type, const char *name); const struct type_tuple *type_get_value( const struct type *type, uint64_t index); struct type_tagged_union * tagged_dup_tags(const struct type_tagged_union *tags); const struct type *tagged_select_subtype(struct context *ctx, const struct type *tagged, const struct type *subtype, bool strip); bool tagged_subset_compat(struct context *ctx, const struct type *to, const struct type *from); const char *type_storage_unparse(enum type_storage storage); bool type_is_signed(struct context *ctx, const struct type *type); bool type_is_integer(struct context *ctx, const struct type *type); bool type_is_numeric(struct context *ctx, const struct type *type); bool type_is_float(struct context *ctx, const struct type *type); bool type_is_flexible(const struct type *type); bool type_has_error(struct context *ctx, const struct type *type); uint32_t type_hash(const struct type *type); const struct type *promote_flexible(struct context *ctx, const struct type *a, const struct type *b); bool type_is_assignable(struct context *ctx, const struct type *to, const struct type *from); const struct type *type_is_castable(struct context *ctx, const struct type *to, const struct type *from); const struct type *type_create_flexible(enum type_storage storage, int64_t min, int64_t max); const struct type *lower_flexible(struct context *ctx, const struct type *old, const struct type *new); void flexible_refer(const struct type *type, const struct type **ref); void flexible_reset_refs(const struct type *type); void builtin_types_init(const char *target); // Built-in type singletons extern struct type // Primitive builtin_type_bool, builtin_type_error, builtin_type_f32, builtin_type_f64, builtin_type_i8, builtin_type_i16, builtin_type_i32, builtin_type_i64, builtin_type_int, builtin_type_never, builtin_type_opaque, builtin_type_u8, builtin_type_u16, builtin_type_u32, builtin_type_u64, builtin_type_uint, builtin_type_uintptr, builtin_type_null, builtin_type_rune, builtin_type_size, builtin_type_void, builtin_type_done, // Const primitives builtin_type_const_bool, builtin_type_const_f32, builtin_type_const_f64, builtin_type_const_i8, builtin_type_const_i16, builtin_type_const_i32, builtin_type_const_i64, builtin_type_const_int, builtin_type_const_never, builtin_type_const_opaque, builtin_type_const_u8, builtin_type_const_u16, builtin_type_const_u32, builtin_type_const_u64, builtin_type_const_uint, builtin_type_const_uintptr, builtin_type_const_rune, builtin_type_const_size, builtin_type_const_void, builtin_type_const_done, // etc builtin_type_str, builtin_type_const_str, builtin_type_valist; #endif harec-0.24.2/include/utf8.h000066400000000000000000000007651464473277600153770ustar00rootroot00000000000000#ifndef HAREC_UTF8_H #define HAREC_UTF8_H #include #include #include #define UTF8_MAX_SIZE 4 #define UTF8_INVALID UINT32_MAX /** * Grabs the next UTF-8 codepoint and advances the string pointer */ uint32_t utf8_decode(const char **str); /** * Encodes a codepoint as UTF-8 and returns the length of that codepoint. */ size_t utf8_encode(char *str, uint32_t ch); /** * Reads and returns the next codepoint from the file. */ uint32_t utf8_get(FILE *f); #endif harec-0.24.2/include/util.h000066400000000000000000000030141464473277600154540ustar00rootroot00000000000000#ifndef HARE_UTIL_H #define HARE_UTIL_H #include #include #include #include #include #include "lex.h" enum exit_status { /* EXIT_SUCCESS = 0 (defined in stdlib.h) */ EXIT_USER = 1, EXIT_LEX = 2, EXIT_PARSE = 3, EXIT_CHECK = 4, EXIT_ABNORMAL = 255, }; extern const char **sources; extern size_t nsources; #define FNV1A_INIT 2166136261u uint32_t fnv1a(uint32_t hash, unsigned char c); uint32_t fnv1a_u32(uint32_t hash, uint32_t u32); uint32_t fnv1a_u64(uint32_t hash, uint64_t u64); uint32_t fnv1a_size(uint32_t hash, size_t sz); uint32_t fnv1a_s(uint32_t hash, const char *str); void *xcalloc(size_t n, size_t s); void *xrealloc(void *p, size_t s); char *xstrdup(const char *s); #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 3) #define FORMAT(FIRST) __attribute__((__format__(__printf__, 2, FIRST))) #else #define FORMAT(FIRST) #endif int xfprintf(FILE *restrict f, const char *restrict fmt, ...) FORMAT(3); int xvfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) FORMAT(0); #undef FORMAT #define malloc(a) (void *)sizeof(struct { static_assert(0, "Use xcalloc instead"); int _; }) #define calloc(a, b) (void *)sizeof(struct { static_assert(0, "Use xcalloc instead"); int _; }) #define realloc(a, b) (void *)sizeof(struct { static_assert(0, "Use xrealloc instead"); int _; }) #define strdup(s) (char *)(sizeof(struct { static_assert(0, "Use xstrdup instead"); int _; }) char *gen_name(int *id, const char *fmt); void errline(struct location loc); #endif harec-0.24.2/makefiles/000077500000000000000000000000001464473277600146455ustar00rootroot00000000000000harec-0.24.2/makefiles/freebsd.mk000066400000000000000000000003011464473277600166020ustar00rootroot00000000000000RTSCRIPT = rt/hare.sc _rt_ha = \ rt/malloc.ha \ rt/+$(PLATFORM)/syscallno.ha \ rt/+$(PLATFORM)/segmalloc.ha _rt_s = \ rt/+$(PLATFORM)/start+$(ARCH).s \ rt/+$(PLATFORM)/syscall+$(ARCH).s harec-0.24.2/makefiles/linux.mk000066400000000000000000000003111464473277600163300ustar00rootroot00000000000000RTSCRIPT = rt/hare.sc _rt_ha = \ rt/malloc.ha \ rt/+$(PLATFORM)/syscallno+$(ARCH).ha \ rt/+$(PLATFORM)/segmalloc.ha _rt_s = \ rt/+$(PLATFORM)/start+$(ARCH).s \ rt/+$(PLATFORM)/syscall+$(ARCH).s harec-0.24.2/makefiles/netbsd.mk000066400000000000000000000003151464473277600164540ustar00rootroot00000000000000RTSCRIPT = rt/hare+$(PLATFORM).sc _rt_ha = \ rt/malloc.ha \ rt/+$(PLATFORM)/syscallno.ha \ rt/+$(PLATFORM)/segmalloc.ha _rt_s = \ rt/+$(PLATFORM)/start+$(ARCH).s \ rt/+$(PLATFORM)/syscall+$(ARCH).s harec-0.24.2/makefiles/openbsd.mk000066400000000000000000000001511464473277600166250ustar00rootroot00000000000000RTSCRIPT = rt/hare+$(PLATFORM).sc _rt_ha = \ rt/malloc+libc.ha _rt_s = \ rt/+openbsd/platformstart.s harec-0.24.2/makefiles/tests.mk000066400000000000000000000442011464473277600163410ustar00rootroot00000000000000TDENV = env HARE_TD_rt=$(HARECACHE)/rt.td HARE_TD_testmod=$(HARECACHE)/testmod.td test_objects = \ src/lex.o \ src/parse.o \ src/type_store.o \ src/scope.o \ src/identifier.o \ src/util.o \ src/types.o \ src/check.o \ src/utf8.o \ src/eval.o \ src/expr.o \ src/typedef.o \ src/mod.o testmod_ha = testmod/measurement.ha testmod/testmod.ha $(HARECACHE)/testmod.ssa: $(testmod_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ -t $(HARECACHE)/testmod.td.tmp -N testmod $(testmod_ha) rt_ha = \ rt/abort.ha \ rt/compile.ha \ rt/cstrings.ha \ rt/ensure.ha \ rt/itos.ha \ rt/memcpy.ha \ rt/memmove.ha \ rt/memset.ha \ rt/strcmp.ha \ rt/+$(PLATFORM)/errno.ha \ rt/+$(PLATFORM)/syscalls.ha \ rt/+$(PLATFORM)/start.ha \ $(_rt_ha) $(HARECACHE)/rt.ssa: $(rt_ha) $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ -t $(HARECACHE)/rt.td.tmp -N rt $(rt_ha) rt_s = $(HARECACHE)/rt.s $(_rt_s) $(HARECACHE)/rt.o: $(rt_s) @printf 'AS\t%s\n' '$@' @$(AS) $(ASFLAGS) -o $@ $(rt_s) tests = \ tests/00-literals \ tests/01-arrays \ tests/02-integers \ tests/03-pointers \ tests/04-strings \ tests/05-implicit-casts \ tests/06-structs \ tests/07-aliases \ tests/08-slices \ tests/09-funcs \ tests/10-binarithms \ tests/11-globals \ tests/12-loops \ tests/13-tagged \ tests/14-switch \ tests/15-enums \ tests/16-defer \ tests/17-alloc \ tests/18-match \ tests/19-append \ tests/20-if \ tests/21-tuples \ tests/22-delete \ tests/23-errors \ tests/24-imports \ tests/25-promotion \ tests/26-regression \ tests/27-rt \ tests/28-insert \ tests/29-unarithm \ tests/30-reduction \ tests/31-postfix \ tests/32-copy \ tests/33-yield \ tests/34-declarations \ tests/35-floats \ tests/36-defines tests/00-literals: $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_00_literals.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_00_literals.o tests_00_literals_ha = tests/00-literals.ha $(HARECACHE)/tests_00_literals.ssa: $(tests_00_literals_ha) $(HARECACHE)/rt.td $(HARECACHE)/testmod.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_00_literals_ha) tests/01-arrays: $(HARECACHE)/rt.o $(HARECACHE)/tests_01_arrays.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_01_arrays.o tests_01_arrays_ha = tests/01-arrays.ha $(HARECACHE)/tests_01_arrays.ssa: $(tests_01_arrays_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_01_arrays_ha) tests/02-integers: $(HARECACHE)/rt.o $(HARECACHE)/tests_02_integers.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_02_integers.o tests_02_integers_ha = tests/02-integers.ha $(HARECACHE)/tests_02_integers.ssa: $(tests_02_integers_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_02_integers_ha) tests/03-pointers: $(HARECACHE)/rt.o $(HARECACHE)/tests_03_pointers.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_03_pointers.o tests_03_pointers_ha = tests/03-pointers.ha $(HARECACHE)/tests_03_pointers.ssa: $(tests_03_pointers_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_03_pointers_ha) tests/04-strings: $(HARECACHE)/rt.o $(HARECACHE)/tests_04_strings.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_04_strings.o tests_04_strings_ha = tests/04-strings.ha $(HARECACHE)/tests_04_strings.ssa: $(tests_04_strings_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_04_strings_ha) tests/05-implicit-casts: $(HARECACHE)/rt.o $(HARECACHE)/tests_05_implicit_casts.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_05_implicit_casts.o tests_05_implicit_casts_ha = tests/05-implicit-casts.ha $(HARECACHE)/tests_05_implicit_casts.ssa: $(tests_05_implicit_casts_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_05_implicit_casts_ha) tests/06-structs: $(HARECACHE)/rt.o $(HARECACHE)/tests_06_structs.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_06_structs.o tests_06_structs_ha = tests/06-structs.ha $(HARECACHE)/tests_06_structs.ssa: $(tests_06_structs_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_06_structs_ha) tests/07-aliases: $(HARECACHE)/rt.o $(HARECACHE)/tests_07_aliases.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_07_aliases.o tests_07_aliases_ha = tests/07-aliases.ha $(HARECACHE)/tests_07_aliases.ssa: $(tests_07_aliases_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_07_aliases_ha) tests/08-slices: $(HARECACHE)/rt.o $(HARECACHE)/tests_08_slices.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_08_slices.o tests_08_slices_ha = tests/08-slices.ha $(HARECACHE)/tests_08_slices.ssa: $(tests_08_slices_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_08_slices_ha) tests/09-funcs: $(HARECACHE)/rt.o $(HARECACHE)/tests_09_funcs.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_09_funcs.o tests_09_funcs_ha = tests/09-funcs.ha $(HARECACHE)/tests_09_funcs.ssa: $(tests_09_funcs_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_09_funcs_ha) tests/10-binarithms: $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_10_binarithms.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_10_binarithms.o tests_10_binarithms_ha = tests/10-binarithms.ha $(HARECACHE)/tests_10_binarithms.ssa: $(tests_10_binarithms_ha) $(HARECACHE)/rt.td $(HARECACHE)/testmod.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_10_binarithms_ha) tests/11-globals: $(HARECACHE)/rt.o $(HARECACHE)/tests_11_globals.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_11_globals.o tests_11_globals_ha = tests/11-globals.ha $(HARECACHE)/tests_11_globals.ssa: $(tests_11_globals_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_11_globals_ha) tests/12-loops: $(HARECACHE)/rt.o $(HARECACHE)/tests_12_loops.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_12_loops.o tests_12_loops_ha = tests/12-loops.ha $(HARECACHE)/tests_12_loops.ssa: $(tests_12_loops_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_12_loops_ha) tests/13-tagged: $(HARECACHE)/rt.o $(HARECACHE)/tests_13_tagged.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_13_tagged.o tests_13_tagged_ha = tests/13-tagged.ha $(HARECACHE)/tests_13_tagged.ssa: $(tests_13_tagged_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_13_tagged_ha) tests/14-switch: $(HARECACHE)/rt.o $(HARECACHE)/tests_14_switch.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_14_switch.o tests_14_switch_ha = tests/14-switch.ha $(HARECACHE)/tests_14_switch.ssa: $(tests_14_switch_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_14_switch_ha) tests/15-enums: $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_15_enums.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_15_enums.o tests_15_enums_ha = tests/15-enums.ha $(HARECACHE)/tests_15_enums.ssa: $(tests_15_enums_ha) $(HARECACHE)/rt.td $(HARECACHE)/testmod.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_15_enums_ha) tests/16-defer: $(HARECACHE)/rt.o $(HARECACHE)/tests_16_defer.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_16_defer.o tests_16_defer_ha = tests/16-defer.ha $(HARECACHE)/tests_16_defer.ssa: $(tests_16_defer_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_16_defer_ha) tests/17-alloc: $(HARECACHE)/rt.o $(HARECACHE)/tests_17_alloc.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_17_alloc.o tests_17_alloc_ha = tests/17-alloc.ha $(HARECACHE)/tests_17_alloc.ssa: $(tests_17_alloc_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_17_alloc_ha) tests/18-match: $(HARECACHE)/rt.o $(HARECACHE)/tests_18_match.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_18_match.o tests_18_match_ha = tests/18-match.ha $(HARECACHE)/tests_18_match.ssa: $(tests_18_match_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_18_match_ha) tests/19-append: $(HARECACHE)/rt.o $(HARECACHE)/tests_19_append.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_19_append.o tests_19_append_ha = tests/19-append.ha $(HARECACHE)/tests_19_append.ssa: $(tests_19_append_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_19_append_ha) tests/20-if: $(HARECACHE)/rt.o $(HARECACHE)/tests_20_if.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_20_if.o tests_20_if_ha = tests/20-if.ha $(HARECACHE)/tests_20_if.ssa: $(tests_20_if_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_20_if_ha) tests/21-tuples: $(HARECACHE)/rt.o $(HARECACHE)/tests_21_tuples.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_21_tuples.o tests_21_tuples_ha = tests/21-tuples.ha $(HARECACHE)/tests_21_tuples.ssa: $(tests_21_tuples_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_21_tuples_ha) tests/22-delete: $(HARECACHE)/rt.o $(HARECACHE)/tests_22_delete.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_22_delete.o tests_22_delete_ha = tests/22-delete.ha $(HARECACHE)/tests_22_delete.ssa: $(tests_22_delete_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_22_delete_ha) tests/23-errors: $(HARECACHE)/rt.o $(HARECACHE)/tests_23_errors.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_23_errors.o tests_23_errors_ha = tests/23-errors.ha $(HARECACHE)/tests_23_errors.ssa: $(tests_23_errors_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_23_errors_ha) tests/24-imports: $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_24_imports.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_24_imports.o tests_24_imports_ha = tests/24-imports.ha $(HARECACHE)/tests_24_imports.ssa: $(tests_24_imports_ha) $(HARECACHE)/rt.td $(HARECACHE)/testmod.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_24_imports_ha) tests/25-promotion: $(HARECACHE)/rt.o $(HARECACHE)/tests_25_promotion.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_25_promotion.o tests_25_promotion_ha = tests/25-promotion.ha $(HARECACHE)/tests_25_promotion.ssa: $(tests_25_promotion_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_25_promotion_ha) tests/26-regression: $(HARECACHE)/rt.o $(HARECACHE)/tests_26_regression.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_26_regression.o tests_26_regression_ha = tests/26-regression.ha $(HARECACHE)/tests_26_regression.ssa: $(tests_26_regression_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_26_regression_ha) tests/27-rt: $(HARECACHE)/rt.o $(HARECACHE)/tests_27_rt.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_27_rt.o tests_27_rt_ha = tests/27-rt.ha $(HARECACHE)/tests_27_rt.ssa: $(tests_27_rt_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_27_rt_ha) tests/28-insert: $(HARECACHE)/rt.o $(HARECACHE)/tests_28_insert.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_28_insert.o tests_28_insert_ha = tests/28-insert.ha $(HARECACHE)/tests_28_insert.ssa: $(tests_28_insert_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_28_insert_ha) tests/29-unarithm: $(HARECACHE)/rt.o $(HARECACHE)/tests_29_unarithm.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_29_unarithm.o tests_29_unarithm_ha = tests/29-unarithm.ha $(HARECACHE)/tests_29_unarithm.ssa: $(tests_29_unarithm_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_29_unarithm_ha) tests/30-reduction: tests/30-reduction.o $(test_objects) @printf 'CCLD\t%s\n' '$@' @$(CC) $(LDFLAGS) $(LIBS) -o $@ tests/30-reduction.o $(test_objects) tests/31-postfix: $(HARECACHE)/rt.o $(HARECACHE)/tests_31_postfix.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_31_postfix.o tests_31_postfix_ha = tests/31-postfix.ha $(HARECACHE)/tests_31_postfix.ssa: $(tests_31_postfix_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_31_postfix_ha) tests/32-copy: $(HARECACHE)/rt.o $(HARECACHE)/tests_32_copy.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_32_copy.o tests_32_copy_ha = tests/32-copy.ha $(HARECACHE)/tests_32_copy.ssa: $(tests_32_copy_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_32_copy_ha) tests/33-yield: $(HARECACHE)/rt.o $(HARECACHE)/tests_33_yield.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_33_yield.o tests_33_yield_ha = tests/33-yield.ha $(HARECACHE)/tests_33_yield.ssa: $(tests_33_yield_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_33_yield_ha) tests/34-declarations: $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_34_declarations.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/testmod.o $(HARECACHE)/tests_34_declarations.o tests_34_declarations_ha = tests/34-declarations.ha $(HARECACHE)/tests_34_declarations.ssa: $(tests_34_declarations_ha) $(HARECACHE)/rt.td $(HARECACHE)/testmod.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_34_declarations_ha) tests/35-floats: $(HARECACHE)/rt.o $(HARECACHE)/tests_35_floats.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_35_floats.o tests_35_floats_ha = tests/35-floats.ha $(HARECACHE)/tests_35_floats.ssa: $(tests_35_floats_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_35_floats_ha) tests/36-defines: $(HARECACHE)/rt.o $(HARECACHE)/tests_36_defines.o @printf 'LD\t%s\t\n' '$@' @$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_36_defines.o tests_36_defines_ha = tests/36-defines.ha $(HARECACHE)/tests_36_defines.ssa: $(tests_36_defines_ha) $(HARECACHE)/rt.td $(BINOUT)/harec @mkdir -p -- $(HARECACHE) @printf 'HAREC\t%s\n' '$@' @$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_36_defines_ha) harec-0.24.2/rt/000077500000000000000000000000001464473277600133325ustar00rootroot00000000000000harec-0.24.2/rt/+freebsd/000077500000000000000000000000001464473277600150175ustar00rootroot00000000000000harec-0.24.2/rt/+freebsd/errno.ha000066400000000000000000000057721464473277600164710ustar00rootroot00000000000000export def EPERM: int = 1; export def ENOENT: int = 2; export def ESRCH: int = 3; export def EINTR: int = 4; export def EIO: int = 5; export def ENXIO: int = 6; export def E2BIG: int = 7; export def ENOEXEC: int = 8; export def EBADF: int = 9; export def ECHILD: int = 10; export def EDEADLK: int = 11; export def ENOMEM: int = 12; export def EACCES: int = 13; export def EFAULT: int = 14; export def ENOTBLK: int = 15; export def EBUSY: int = 16; export def EEXIST: int = 17; export def EXDEV: int = 18; export def ENODEV: int = 19; export def ENOTDIR: int = 20; export def EISDIR: int = 21; export def EINVAL: int = 22; export def ENFILE: int = 23; export def EMFILE: int = 24; export def ENOTTY: int = 25; export def ETXTBSY: int = 26; export def EFBIG: int = 27; export def ENOSPC: int = 28; export def ESPIPE: int = 29; export def EROFS: int = 30; export def EMLINK: int = 31; export def EPIPE: int = 32; export def EDOM: int = 33; export def ERANGE: int = 34; export def EAGAIN: int = 35; export def EWOULDBLOCK: int = EAGAIN; export def EINPROGRESS: int = 36; export def EALREADY: int = 37; export def ENOTSOCK: int = 38; export def EDESTADDRREQ: int = 39; export def EMSGSIZE: int = 40; export def EPROTOTYPE: int = 41; export def ENOPROTOOPT: int = 42; export def EPROTONOSUPPORT: int = 43; export def ESOCKTNOSUPPORT: int = 44; export def EOPNOTSUPP: int = 45; export def ENOTSUP: int = EOPNOTSUPP; export def EPFNOSUPPORT: int = 46; export def EAFNOSUPPORT: int = 47; export def EADDRINUSE: int = 48; export def EADDRNOTAVAIL: int = 49; export def ENETDOWN: int = 50; export def ENETUNREACH: int = 51; export def ENETRESET: int = 52; export def ECONNABORTED: int = 53; export def ECONNRESET: int = 54; export def ENOBUFS: int = 55; export def EISCONN: int = 56; export def ENOTCONN: int = 57; export def ESHUTDOWN: int = 58; export def ETOOMANYREFS: int = 59; export def ETIMEDOUT: int = 60; export def ECONNREFUSED: int = 61; export def ELOOP: int = 62; export def ENAMETOOLONG: int = 63; export def EHOSTDOWN: int = 64; export def EHOSTUNREACH: int = 65; export def ENOTEMPTY: int = 66; export def EPROCLIM: int = 67; export def EUSERS: int = 68; export def EDQUOT: int = 69; export def ESTALE: int = 70; export def EREMOTE: int = 71; export def EBADRPC: int = 72; export def ERPCMISMATCH: int = 73; export def EPROGUNAVAIL: int = 74; export def EPROGMISMATCH: int = 75; export def EPROCUNAVAIL: int = 76; export def ENOLCK: int = 77; export def ENOSYS: int = 78; export def EFTYPE: int = 79; export def EAUTH: int = 80; export def ENEEDAUTH: int = 81; export def EIDRM: int = 82; export def ENOMSG: int = 83; export def EOVERFLOW: int = 84; export def ECANCELED: int = 85; export def EILSEQ: int = 86; export def ENOATTR: int = 87; export def EDOOFUS: int = 88; export def EBADMSG: int = 89; export def EMULTIHOP: int = 90; export def ENOLINK: int = 91; export def EPROTO: int = 92; export def ENOTCAPABLE: int = 93; export def ECAPMODE: int = 94; export def ENOTRECOVERABLE: int = 95; export def EOWNERDEAD: int = 96; export def EINTEGRITY: int = 97; harec-0.24.2/rt/+freebsd/segmalloc.ha000066400000000000000000000004711464473277600173010ustar00rootroot00000000000000// Allocates a segment. fn segmalloc(n: size) nullable *opaque = { let p: *opaque = mmap(null, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); return if (p: uintptr: int == -ENOMEM) null else p; }; // Frees a segment allocated with segmalloc. fn segfree(p: *opaque, s: size) int = munmap(p, s); harec-0.24.2/rt/+freebsd/start+aarch64.s000066400000000000000000000001301464473277600175560ustar00rootroot00000000000000.text .global _start _start: mov x29, #0 mov x30, #0 and sp, sp, #-16 b rt.start_ha harec-0.24.2/rt/+freebsd/start+riscv64.s000066400000000000000000000001011464473277600176240ustar00rootroot00000000000000.text .global _start _start: andi sp, sp, -16 tail rt.start_ha harec-0.24.2/rt/+freebsd/start+x86_64.s000066400000000000000000000001201464473277600172630ustar00rootroot00000000000000.text .global _start _start: xor %rbp, %rbp andq $-16, %rsp call rt.start_ha harec-0.24.2/rt/+freebsd/start.ha000066400000000000000000000016311464473277600164670ustar00rootroot00000000000000@symbol("main") fn main() void; const @symbol("__init_array_start") init_start: [*]*fn() void; const @symbol("__init_array_end") init_end: [*]*fn() void; const @symbol("__fini_array_start") fini_start: [*]*fn() void; const @symbol("__fini_array_end") fini_end: [*]*fn() void; let argc: size = 0; let argv: *[*]*const u8 = null: *[*]*const u8; let envp: *[*]nullable *const u8 = null: *[*]nullable *const u8; export let exit_status = 0; export fn start_ha(iv: *[*]uintptr) never = { const ninit = (&init_end: uintptr - &init_start: uintptr): size / size(*fn() void); for (let i = 0z; i < ninit; i += 1) { init_start[i](); }; argc = iv[0]: size; argv = &iv[1]: *[*]*const u8; envp = &argv[argc + 1]: *[*]nullable *const u8; main(); const nfini = (&fini_end: uintptr - &fini_start: uintptr): size / size(*fn() void); for (let i = 0z; i < nfini; i += 1) { fini_start[i](); }; exit(exit_status); }; harec-0.24.2/rt/+freebsd/syscall+aarch64.s000066400000000000000000000015161464473277600201040ustar00rootroot00000000000000.section .text.rt.syscall0 .global rt.syscall0 rt.syscall0: mov x8, x0 svc 0 ret .section .text.rt.syscall1 .global rt.syscall1 rt.syscall1: mov x8, x0 mov x0, x1 svc 0 ret .section .text.rt.syscall2 .global rt.syscall2 rt.syscall2: mov x8, x0 mov x0, x1 mov x1, x2 svc 0 ret .section .text.rt.syscall3 .global rt.syscall3 rt.syscall3: mov x8, x0 mov x0, x1 mov x1, x2 mov x2, x3 svc 0 ret .section .text.rt.syscall4 .global rt.syscall4 rt.syscall4: mov x8, x0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 svc 0 ret .section .text.rt.syscall5 .global rt.syscall5 rt.syscall5: mov x8, x0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 mov x4, x5 svc 0 ret .section .text.rt.syscall6 .global rt.syscall6 rt.syscall6: mov x8, x0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 mov x4, x5 mov x5, x6 svc 0 ret harec-0.24.2/rt/+freebsd/syscall+riscv64.s000066400000000000000000000014621464473277600201540ustar00rootroot00000000000000.section .text.rt.syscall0 .global rt.syscall0 rt.syscall0: mv a7, a0 ecall ret .section .text.rt.syscall1 .global rt.syscall1 rt.syscall1: mv a7, a0 mv a0, a1 ecall ret .section .text.rt.syscall2 .global rt.syscall2 rt.syscall2: mv a7, a0 mv a0, a1 mv a1, a2 ecall ret .section .text.rt.syscall3 .global rt.syscall3 rt.syscall3: mv a7, a0 mv a0, a1 mv a1, a2 mv a2, a3 ecall ret .section .text.rt.syscall4 .global rt.syscall4 rt.syscall4: mv a7, a0 mv a0, a1 mv a1, a2 mv a2, a3 mv a3, a4 ecall ret .section .text.rt.syscall5 .global rt.syscall5 rt.syscall5: mv a7, a0 mv a0, a1 mv a1, a2 mv a2, a3 mv a3, a4 mv a4, a5 ecall ret .section .text.rt.syscall6 .global rt.syscall6 rt.syscall6: mv a7, a0 mv a0, a1 mv a1, a2 mv a2, a3 mv a3, a4 mv a4, a5 mv a5, a6 ecall ret harec-0.24.2/rt/+freebsd/syscall+x86_64.s000066400000000000000000000017431464473277600176140ustar00rootroot00000000000000.section .text.rt.syscall0 .global rt.syscall0 rt.syscall0: movq %rdi, %rax syscall ret .section .text.rt.syscall1 .global rt.syscall1 rt.syscall1: movq %rdi, %rax movq %rsi, %rdi syscall ret .section .text.rt.syscall2 .global rt.syscall2 rt.syscall2: movq %rdi, %rax movq %rsi, %rdi movq %rdx, %rsi syscall ret .section .text.rt.syscall3 .global rt.syscall3 rt.syscall3: movq %rdi, %rax movq %rsi, %rdi movq %rdx, %rsi movq %rcx, %rdx syscall ret .section .text.rt.syscall4 .global rt.syscall4 rt.syscall4: movq %rdi, %rax movq %r8, %r10 movq %rsi, %rdi movq %rdx, %rsi movq %rcx, %rdx syscall ret .section .text.rt.syscall5 .global rt.syscall5 rt.syscall5: movq %rdi, %rax movq %r8, %r10 movq %rsi, %rdi movq %r9, %r8 movq %rdx, %rsi movq %rcx, %rdx syscall ret .section .text.rt.syscall6 .global rt.syscall6 rt.syscall6: movq %rdi, %rax movq %r8, %r10 movq %rsi, %rdi movq %r9, %r8 movq %rdx, %rsi movq 8(%rsp), %r9 movq %rcx, %rdx syscall ret harec-0.24.2/rt/+freebsd/syscallno.ha000066400000000000000000000365751464473277600173600ustar00rootroot00000000000000export def SYS_syscall: u64 = 0; export def SYS_exit: u64 = 1; export def SYS_fork: u64 = 2; export def SYS_read: u64 = 3; export def SYS_write: u64 = 4; export def SYS_open: u64 = 5; export def SYS_close: u64 = 6; export def SYS_wait4: u64 = 7; export def SYS_link: u64 = 9; export def SYS_unlink: u64 = 10; export def SYS_chdir: u64 = 12; export def SYS_fchdir: u64 = 13; export def SYS_freebsd11_mknod: u64 = 14; export def SYS_chmod: u64 = 15; export def SYS_chown: u64 = 16; export def SYS_break: u64 = 17; export def SYS_getpid: u64 = 20; export def SYS_mount: u64 = 21; export def SYS_unmount: u64 = 22; export def SYS_setuid: u64 = 23; export def SYS_getuid: u64 = 24; export def SYS_geteuid: u64 = 25; export def SYS_ptrace: u64 = 26; export def SYS_recvmsg: u64 = 27; export def SYS_sendmsg: u64 = 28; export def SYS_recvfrom: u64 = 29; export def SYS_accept: u64 = 30; export def SYS_getpeername: u64 = 31; export def SYS_getsockname: u64 = 32; export def SYS_access: u64 = 33; export def SYS_chflags: u64 = 34; export def SYS_fchflags: u64 = 35; export def SYS_sync: u64 = 36; export def SYS_kill: u64 = 37; export def SYS_getppid: u64 = 39; export def SYS_dup: u64 = 41; export def SYS_freebsd10_pipe: u64 = 42; export def SYS_getegid: u64 = 43; export def SYS_profil: u64 = 44; export def SYS_ktrace: u64 = 45; export def SYS_getgid: u64 = 47; export def SYS_getlogin: u64 = 49; export def SYS_setlogin: u64 = 50; export def SYS_acct: u64 = 51; export def SYS_sigaltstack: u64 = 53; export def SYS_ioctl: u64 = 54; export def SYS_reboot: u64 = 55; export def SYS_revoke: u64 = 56; export def SYS_symlink: u64 = 57; export def SYS_readlink: u64 = 58; export def SYS_execve: u64 = 59; export def SYS_umask: u64 = 60; export def SYS_chroot: u64 = 61; export def SYS_msync: u64 = 65; export def SYS_vfork: u64 = 66; export def SYS_sbrk: u64 = 69; export def SYS_sstk: u64 = 70; export def SYS_freebsd11_vadvise: u64 = 72; export def SYS_munmap: u64 = 73; export def SYS_mprotect: u64 = 74; export def SYS_madvise: u64 = 75; export def SYS_mincore: u64 = 78; export def SYS_getgroups: u64 = 79; export def SYS_setgroups: u64 = 80; export def SYS_getpgrp: u64 = 81; export def SYS_setpgid: u64 = 82; export def SYS_setitimer: u64 = 83; export def SYS_swapon: u64 = 85; export def SYS_getitimer: u64 = 86; export def SYS_getdtablesize: u64 = 89; export def SYS_dup2: u64 = 90; export def SYS_fcntl: u64 = 92; export def SYS_select: u64 = 93; export def SYS_fsync: u64 = 95; export def SYS_setpriority: u64 = 96; export def SYS_socket: u64 = 97; export def SYS_connect: u64 = 98; export def SYS_getpriority: u64 = 100; export def SYS_bind: u64 = 104; export def SYS_setsockopt: u64 = 105; export def SYS_listen: u64 = 106; export def SYS_gettimeofday: u64 = 116; export def SYS_getrusage: u64 = 117; export def SYS_getsockopt: u64 = 118; export def SYS_readv: u64 = 120; export def SYS_writev: u64 = 121; export def SYS_settimeofday: u64 = 122; export def SYS_fchown: u64 = 123; export def SYS_fchmod: u64 = 124; export def SYS_setreuid: u64 = 126; export def SYS_setregid: u64 = 127; export def SYS_rename: u64 = 128; export def SYS_flock: u64 = 131; export def SYS_mkfifo: u64 = 132; export def SYS_sendto: u64 = 133; export def SYS_shutdown: u64 = 134; export def SYS_socketpair: u64 = 135; export def SYS_mkdir: u64 = 136; export def SYS_rmdir: u64 = 137; export def SYS_utimes: u64 = 138; export def SYS_adjtime: u64 = 140; export def SYS_setsid: u64 = 147; export def SYS_quotactl: u64 = 148; export def SYS_nlm_syscall: u64 = 154; export def SYS_nfssvc: u64 = 155; export def SYS_lgetfh: u64 = 160; export def SYS_getfh: u64 = 161; export def SYS_sysarch: u64 = 165; export def SYS_rtprio: u64 = 166; export def SYS_semsys: u64 = 169; export def SYS_msgsys: u64 = 170; export def SYS_shmsys: u64 = 171; export def SYS_setfib: u64 = 175; export def SYS_ntp_adjtime: u64 = 176; export def SYS_setgid: u64 = 181; export def SYS_setegid: u64 = 182; export def SYS_seteuid: u64 = 183; export def SYS_freebsd11_stat: u64 = 188; export def SYS_freebsd11_fstat: u64 = 189; export def SYS_freebsd11_lstat: u64 = 190; export def SYS_pathconf: u64 = 191; export def SYS_fpathconf: u64 = 192; export def SYS_getrlimit: u64 = 194; export def SYS_setrlimit: u64 = 195; export def SYS_freebsd11_getdirentries: u64 = 196; export def SYS___syscall: u64 = 198; export def SYS___sysctl: u64 = 202; export def SYS_mlock: u64 = 203; export def SYS_munlock: u64 = 204; export def SYS_undelete: u64 = 205; export def SYS_futimes: u64 = 206; export def SYS_getpgid: u64 = 207; export def SYS_poll: u64 = 209; export def SYS_freebsd7___semctl: u64 = 220; export def SYS_semget: u64 = 221; export def SYS_semop: u64 = 222; export def SYS_freebsd7_msgctl: u64 = 224; export def SYS_msgget: u64 = 225; export def SYS_msgsnd: u64 = 226; export def SYS_msgrcv: u64 = 227; export def SYS_shmat: u64 = 228; export def SYS_freebsd7_shmctl: u64 = 229; export def SYS_shmdt: u64 = 230; export def SYS_shmget: u64 = 231; export def SYS_clock_gettime: u64 = 232; export def SYS_clock_settime: u64 = 233; export def SYS_clock_getres: u64 = 234; export def SYS_ktimer_create: u64 = 235; export def SYS_ktimer_delete: u64 = 236; export def SYS_ktimer_settime: u64 = 237; export def SYS_ktimer_gettime: u64 = 238; export def SYS_ktimer_getoverrun: u64 = 239; export def SYS_nanosleep: u64 = 240; export def SYS_ffclock_getcounter: u64 = 241; export def SYS_ffclock_setestimate: u64 = 242; export def SYS_ffclock_getestimate: u64 = 243; export def SYS_clock_nanosleep: u64 = 244; export def SYS_clock_getcpuclockid2: u64 = 247; export def SYS_ntp_gettime: u64 = 248; export def SYS_minherit: u64 = 250; export def SYS_rfork: u64 = 251; export def SYS_issetugid: u64 = 253; export def SYS_lchown: u64 = 254; export def SYS_aio_read: u64 = 255; export def SYS_aio_write: u64 = 256; export def SYS_lio_listio: u64 = 257; export def SYS_freebsd11_getdents: u64 = 272; export def SYS_lchmod: u64 = 274; export def SYS_lutimes: u64 = 276; export def SYS_freebsd11_nstat: u64 = 278; export def SYS_freebsd11_nfstat: u64 = 279; export def SYS_freebsd11_nlstat: u64 = 280; export def SYS_preadv: u64 = 289; export def SYS_pwritev: u64 = 290; export def SYS_fhopen: u64 = 298; export def SYS_freebsd11_fhstat: u64 = 299; export def SYS_modnext: u64 = 300; export def SYS_modstat: u64 = 301; export def SYS_modfnext: u64 = 302; export def SYS_modfind: u64 = 303; export def SYS_kldload: u64 = 304; export def SYS_kldunload: u64 = 305; export def SYS_kldfind: u64 = 306; export def SYS_kldnext: u64 = 307; export def SYS_kldstat: u64 = 308; export def SYS_kldfirstmod: u64 = 309; export def SYS_getsid: u64 = 310; export def SYS_setresuid: u64 = 311; export def SYS_setresgid: u64 = 312; export def SYS_aio_return: u64 = 314; export def SYS_aio_suspend: u64 = 315; export def SYS_aio_cancel: u64 = 316; export def SYS_aio_error: u64 = 317; export def SYS_yield: u64 = 321; export def SYS_mlockall: u64 = 324; export def SYS_munlockall: u64 = 325; export def SYS___getcwd: u64 = 326; export def SYS_sched_setparam: u64 = 327; export def SYS_sched_getparam: u64 = 328; export def SYS_sched_setscheduler: u64 = 329; export def SYS_sched_getscheduler: u64 = 330; export def SYS_sched_yield: u64 = 331; export def SYS_sched_get_priority_max: u64 = 332; export def SYS_sched_get_priority_min: u64 = 333; export def SYS_sched_rr_get_u64erval: u64 = 334; export def SYS_utrace: u64 = 335; export def SYS_kldsym: u64 = 337; export def SYS_jail: u64 = 338; export def SYS_nnpfs_syscall: u64 = 339; export def SYS_sigprocmask: u64 = 340; export def SYS_sigsuspend: u64 = 341; export def SYS_sigpending: u64 = 343; export def SYS_sigtimedwait: u64 = 345; export def SYS_sigwaitinfo: u64 = 346; export def SYS___acl_get_file: u64 = 347; export def SYS___acl_set_file: u64 = 348; export def SYS___acl_get_fd: u64 = 349; export def SYS___acl_set_fd: u64 = 350; export def SYS___acl_delete_file: u64 = 351; export def SYS___acl_delete_fd: u64 = 352; export def SYS___acl_aclcheck_file: u64 = 353; export def SYS___acl_aclcheck_fd: u64 = 354; export def SYS_extattrctl: u64 = 355; export def SYS_extattr_set_file: u64 = 356; export def SYS_extattr_get_file: u64 = 357; export def SYS_extattr_delete_file: u64 = 358; export def SYS_aio_waitcomplete: u64 = 359; export def SYS_getresuid: u64 = 360; export def SYS_getresgid: u64 = 361; export def SYS_kqueue: u64 = 362; export def SYS_freebsd11_kevent: u64 = 363; export def SYS_extattr_set_fd: u64 = 371; export def SYS_extattr_get_fd: u64 = 372; export def SYS_extattr_delete_fd: u64 = 373; export def SYS___setugid: u64 = 374; export def SYS_eaccess: u64 = 376; export def SYS_afs3_syscall: u64 = 377; export def SYS_nmount: u64 = 378; export def SYS___mac_get_proc: u64 = 384; export def SYS___mac_set_proc: u64 = 385; export def SYS___mac_get_fd: u64 = 386; export def SYS___mac_get_file: u64 = 387; export def SYS___mac_set_fd: u64 = 388; export def SYS___mac_set_file: u64 = 389; export def SYS_kenv: u64 = 390; export def SYS_lchflags: u64 = 391; export def SYS_uuidgen: u64 = 392; export def SYS_sendfile: u64 = 393; export def SYS_mac_syscall: u64 = 394; export def SYS_freebsd11_getfsstat: u64 = 395; export def SYS_freebsd11_statfs: u64 = 396; export def SYS_freebsd11_fstatfs: u64 = 397; export def SYS_freebsd11_fhstatfs: u64 = 398; export def SYS_ksem_close: u64 = 400; export def SYS_ksem_post: u64 = 401; export def SYS_ksem_wait: u64 = 402; export def SYS_ksem_trywait: u64 = 403; export def SYS_ksem_init: u64 = 404; export def SYS_ksem_open: u64 = 405; export def SYS_ksem_unlink: u64 = 406; export def SYS_ksem_getvalue: u64 = 407; export def SYS_ksem_destroy: u64 = 408; export def SYS___mac_get_pid: u64 = 409; export def SYS___mac_get_link: u64 = 410; export def SYS___mac_set_link: u64 = 411; export def SYS_extattr_set_link: u64 = 412; export def SYS_extattr_get_link: u64 = 413; export def SYS_extattr_delete_link: u64 = 414; export def SYS___mac_execve: u64 = 415; export def SYS_sigaction: u64 = 416; export def SYS_sigreturn: u64 = 417; export def SYS_getcontext: u64 = 421; export def SYS_setcontext: u64 = 422; export def SYS_swapcontext: u64 = 423; export def SYS_swapoff: u64 = 424; export def SYS___acl_get_link: u64 = 425; export def SYS___acl_set_link: u64 = 426; export def SYS___acl_delete_link: u64 = 427; export def SYS___acl_aclcheck_link: u64 = 428; export def SYS_sigwait: u64 = 429; export def SYS_thr_create: u64 = 430; export def SYS_thr_exit: u64 = 431; export def SYS_thr_self: u64 = 432; export def SYS_thr_kill: u64 = 433; export def SYS_jail_attach: u64 = 436; export def SYS_extattr_list_fd: u64 = 437; export def SYS_extattr_list_file: u64 = 438; export def SYS_extattr_list_link: u64 = 439; export def SYS_ksem_timedwait: u64 = 441; export def SYS_thr_suspend: u64 = 442; export def SYS_thr_wake: u64 = 443; export def SYS_kldunloadf: u64 = 444; export def SYS_audit: u64 = 445; export def SYS_auditon: u64 = 446; export def SYS_getauid: u64 = 447; export def SYS_setauid: u64 = 448; export def SYS_getaudit: u64 = 449; export def SYS_setaudit: u64 = 450; export def SYS_getaudit_addr: u64 = 451; export def SYS_setaudit_addr: u64 = 452; export def SYS_auditctl: u64 = 453; export def SYS__umtx_op: u64 = 454; export def SYS_thr_new: u64 = 455; export def SYS_sigqueue: u64 = 456; export def SYS_kmq_open: u64 = 457; export def SYS_kmq_setattr: u64 = 458; export def SYS_kmq_timedreceive: u64 = 459; export def SYS_kmq_timedsend: u64 = 460; export def SYS_kmq_notify: u64 = 461; export def SYS_kmq_unlink: u64 = 462; export def SYS_abort2: u64 = 463; export def SYS_thr_set_name: u64 = 464; export def SYS_aio_fsync: u64 = 465; export def SYS_rtprio_thread: u64 = 466; export def SYS_sctp_peeloff: u64 = 471; export def SYS_sctp_generic_sendmsg: u64 = 472; export def SYS_sctp_generic_sendmsg_iov: u64 = 473; export def SYS_sctp_generic_recvmsg: u64 = 474; export def SYS_pread: u64 = 475; export def SYS_pwrite: u64 = 476; export def SYS_mmap: u64 = 477; export def SYS_lseek: u64 = 478; export def SYS_truncate: u64 = 479; export def SYS_ftruncate: u64 = 480; export def SYS_thr_kill2: u64 = 481; export def SYS_freebsd12_shm_open: u64 = 482; export def SYS_shm_unlink: u64 = 483; export def SYS_cpuset: u64 = 484; export def SYS_cpuset_setid: u64 = 485; export def SYS_cpuset_getid: u64 = 486; export def SYS_cpuset_getaffinity: u64 = 487; export def SYS_cpuset_setaffinity: u64 = 488; export def SYS_faccessat: u64 = 489; export def SYS_fchmodat: u64 = 490; export def SYS_fchownat: u64 = 491; export def SYS_fexecve: u64 = 492; export def SYS_freebsd11_fstatat: u64 = 493; export def SYS_futimesat: u64 = 494; export def SYS_linkat: u64 = 495; export def SYS_mkdirat: u64 = 496; export def SYS_mkfifoat: u64 = 497; export def SYS_freebsd11_mknodat: u64 = 498; export def SYS_openat: u64 = 499; export def SYS_readlinkat: u64 = 500; export def SYS_renameat: u64 = 501; export def SYS_symlinkat: u64 = 502; export def SYS_unlinkat: u64 = 503; export def SYS_posix_openpt: u64 = 504; export def SYS_gssd_syscall: u64 = 505; export def SYS_jail_get: u64 = 506; export def SYS_jail_set: u64 = 507; export def SYS_jail_remove: u64 = 508; export def SYS_freebsd12_closefrom: u64 = 509; export def SYS___semctl: u64 = 510; export def SYS_msgctl: u64 = 511; export def SYS_shmctl: u64 = 512; export def SYS_lpathconf: u64 = 513; export def SYS___cap_rights_get: u64 = 515; export def SYS_cap_enter: u64 = 516; export def SYS_cap_getmode: u64 = 517; export def SYS_pdfork: u64 = 518; export def SYS_pdkill: u64 = 519; export def SYS_pdgetpid: u64 = 520; export def SYS_pselect: u64 = 522; export def SYS_getloginclass: u64 = 523; export def SYS_setloginclass: u64 = 524; export def SYS_rctl_get_racct: u64 = 525; export def SYS_rctl_get_rules: u64 = 526; export def SYS_rctl_get_limits: u64 = 527; export def SYS_rctl_add_rule: u64 = 528; export def SYS_rctl_remove_rule: u64 = 529; export def SYS_posix_fallocate: u64 = 530; export def SYS_posix_fadvise: u64 = 531; export def SYS_wait6: u64 = 532; export def SYS_cap_rights_limit: u64 = 533; export def SYS_cap_ioctls_limit: u64 = 534; export def SYS_cap_ioctls_get: u64 = 535; export def SYS_cap_fcntls_limit: u64 = 536; export def SYS_cap_fcntls_get: u64 = 537; export def SYS_bindat: u64 = 538; export def SYS_connectat: u64 = 539; export def SYS_chflagsat: u64 = 540; export def SYS_accept4: u64 = 541; export def SYS_pipe2: u64 = 542; export def SYS_aio_mlock: u64 = 543; export def SYS_procctl: u64 = 544; export def SYS_ppoll: u64 = 545; export def SYS_futimens: u64 = 546; export def SYS_utimensat: u64 = 547; export def SYS_fdatasync: u64 = 550; export def SYS_fstat: u64 = 551; export def SYS_fstatat: u64 = 552; export def SYS_fhstat: u64 = 553; export def SYS_getdirentries: u64 = 554; export def SYS_statfs: u64 = 555; export def SYS_fstatfs: u64 = 556; export def SYS_getfsstat: u64 = 557; export def SYS_fhstatfs: u64 = 558; export def SYS_mknodat: u64 = 559; export def SYS_kevent: u64 = 560; export def SYS_cpuset_getdomain: u64 = 561; export def SYS_cpuset_setdomain: u64 = 562; export def SYS_getrandom: u64 = 563; export def SYS_getfhat: u64 = 564; export def SYS_fhlink: u64 = 565; export def SYS_fhlinkat: u64 = 566; export def SYS_fhreadlink: u64 = 567; export def SYS_funlinkat: u64 = 568; export def SYS_copy_file_range: u64 = 569; export def SYS___sysctlbyname: u64 = 570; export def SYS_shm_open2: u64 = 571; export def SYS_shm_rename: u64 = 572; export def SYS_sigfastblock: u64 = 573; export def SYS___realpathat: u64 = 574; export def SYS_close_range: u64 = 575; export def SYS_rpctls_syscall: u64 = 576; export def SYS___specialfd: u64 = 577; export def SYS_aio_writev: u64 = 578; export def SYS_aio_readv: u64 = 579; harec-0.24.2/rt/+freebsd/syscalls.ha000066400000000000000000000052261464473277600171730ustar00rootroot00000000000000fn syscall0(u64) u64; fn syscall1(u64, u64) u64; fn syscall2(u64, u64, u64) u64; fn syscall3(u64, u64, u64, u64) u64; fn syscall4(u64, u64, u64, u64, u64) u64; fn syscall5(u64, u64, u64, u64, u64, u64) u64; fn syscall6(u64, u64, u64, u64, u64, u64, u64) u64; export fn write(fd: int, buf: *const opaque, count: size) size = syscall3(SYS_write, fd: u64, buf: uintptr: u64, count: u64): size; export fn close(fd: int) int = syscall1(SYS_close, fd: u64): int; export fn dup2(old: int, new: int) int = syscall2(SYS_dup2, old: u64, new: u64): int; export fn getpid() int = syscall0(SYS_getpid): int; export def EXIT_SUCCESS: int = 0; export fn exit(status: int) never = { syscall1(SYS_exit, status: u64); abort(); }; export fn fork() int = syscall0(SYS_fork): int; export fn execve( path: *const u8, argv: *[*]nullable *const u8, envp: *[*]nullable *const u8, ) int = syscall3(SYS_execve, path: uintptr: u64, argv: uintptr: u64, envp: uintptr: u64): int; export fn wait4(pid: int, status: *int, options: int, rusage: nullable *opaque) void = { syscall4(SYS_wait4, pid: u64, status: uintptr: u64, options: u64, rusage: uintptr: u64); }; export fn wifexited(status: int) bool = wtermsig(status) == 0; export fn wexitstatus(status: int) int = (status & 0xff00) >> 8; export fn wtermsig(status: int) int = status & 0x7f; export fn wifsignaled(status: int) bool = wtermsig(status) != 0o177 && wtermsig(status) != 0 && status != 0x13; export fn kill(pid: int, signal: int) int = syscall2(SYS_kill, pid: u64, signal: u64): int; export fn pipe2(pipefd: *[2]int, flags: int) int = syscall2(SYS_pipe2, pipefd: uintptr: u64, flags: u64): int; export def MAP_SHARED: uint = 0x0001; export def MAP_PRIVATE: uint = 0x0002; export def MAP_FIXED: uint = 0x0010; export def MAP_HASSEMAPHORE: uint = 0x0200; export def MAP_STACK: uint = 0x0400; export def MAP_NOSYNC: uint = 0x0800; export def MAP_FILE: uint = 0x0000; export def MAP_ANON: uint = 0x1000; export def MAP_GUARD: uint = 0x00002000; export def MAP_EXCL: uint = 0x00004000; export def MAP_NOCORE: uint = 0x00020000; export def MAP_PREFAULT_READ: uint = 0x00040000; export def MAP_32BIT: uint = 0x00080000; def PROT_NONE: uint = 0x00; def PROT_READ: uint = 0x01; def PROT_WRITE: uint = 0x02; def PROT_EXEC: uint = 0x04; export fn mmap( addr: nullable *opaque, length: size, prot: uint, flags: uint, fd: int, offs: size ) *opaque = { return syscall6(SYS_mmap, addr: uintptr: u64, length: u64, prot: u64, flags: u64, fd: u64, offs: u64): uintptr: *opaque; }; export fn munmap(addr: *opaque, length: size) int = syscall2(SYS_munmap, addr: uintptr: u64, length: u64): int; export def SIGABRT: int = 6; export def SIGCHLD: int = 20; harec-0.24.2/rt/+linux/000077500000000000000000000000001464473277600145445ustar00rootroot00000000000000harec-0.24.2/rt/+linux/errno.ha000066400000000000000000000100041464473277600161760ustar00rootroot00000000000000export def EPERM: int = 1; export def ENOENT: int = 2; export def ESRCH: int = 3; export def EINTR: int = 4; export def EIO: int = 5; export def ENXIO: int = 6; export def E2BIG: int = 7; export def ENOEXEC: int = 8; export def EBADF: int = 9; export def ECHILD: int = 10; export def EAGAIN: int = 11; export def ENOMEM: int = 12; export def EACCES: int = 13; export def EFAULT: int = 14; export def ENOTBLK: int = 15; export def EBUSY: int = 16; export def EEXIST: int = 17; export def EXDEV: int = 18; export def ENODEV: int = 19; export def ENOTDIR: int = 20; export def EISDIR: int = 21; export def EINVAL: int = 22; export def ENFILE: int = 23; export def EMFILE: int = 24; export def ENOTTY: int = 25; export def ETXTBSY: int = 26; export def EFBIG: int = 27; export def ENOSPC: int = 28; export def ESPIPE: int = 29; export def EROFS: int = 30; export def EMLINK: int = 31; export def EPIPE: int = 32; export def EDOM: int = 33; export def ERANGE: int = 34; export def EDEADLK: int = 35; export def ENAMETOOLONG: int = 36; export def ENOLCK: int = 37; export def ENOSYS: int = 38; export def ENOTEMPTY: int = 39; export def ELOOP: int = 40; export def ENOMSG: int = 42; export def EIDRM: int = 43; export def ECHRNG: int = 44; export def EL2NSYNC: int = 45; export def EL3HLT: int = 46; export def EL3RST: int = 47; export def ELNRNG: int = 48; export def EUNATCH: int = 49; export def ENOCSI: int = 50; export def EL2HLT: int = 51; export def EBADE: int = 52; export def EBADR: int = 53; export def EXFULL: int = 54; export def ENOANO: int = 55; export def EBADRQC: int = 56; export def EBADSLT: int = 57; export def EBFONT: int = 59; export def ENOSTR: int = 60; export def ENODATA: int = 61; export def ETIME: int = 62; export def ENOSR: int = 63; export def ENONET: int = 64; export def ENOPKG: int = 65; export def EREMOTE: int = 66; export def ENOLINK: int = 67; export def EADV: int = 68; export def ESRMNT: int = 69; export def ECOMM: int = 70; export def EPROTO: int = 71; export def EMULTIHOP: int = 72; export def EDOTDOT: int = 73; export def EBADMSG: int = 74; export def EOVERFLOW: int = 75; export def ENOTUNIQ: int = 76; export def EBADFD: int = 77; export def EREMCHG: int = 78; export def ELIBACC: int = 79; export def ELIBBAD: int = 80; export def ELIBSCN: int = 81; export def ELIBMAX: int = 82; export def ELIBEXEC: int = 83; export def EILSEQ: int = 84; export def ERESTART: int = 85; export def ESTRPIPE: int = 86; export def EUSERS: int = 87; export def ENOTSOCK: int = 88; export def EDESTADDRREQ: int = 89; export def EMSGSIZE: int = 90; export def EPROTOTYPE: int = 91; export def ENOPROTOOPT: int = 92; export def EPROTONOSUPPORT: int = 93; export def ESOCKTNOSUPPORT: int = 94; export def EOPNOTSUPP: int = 95; export def EPFNOSUPPORT: int = 96; export def EAFNOSUPPORT: int = 97; export def EADDRINUSE: int = 98; export def EADDRNOTAVAIL: int = 99; export def ENETDOWN: int = 100; export def ENETUNREACH: int = 101; export def ENETRESET: int = 102; export def ECONNABORTED: int = 103; export def ECONNRESET: int = 104; export def ENOBUFS: int = 105; export def EISCONN: int = 106; export def ENOTCONN: int = 107; export def ESHUTDOWN: int = 108; export def ETOOMANYREFS: int = 109; export def ETIMEDOUT: int = 110; export def ECONNREFUSED: int = 111; export def EHOSTDOWN: int = 112; export def EHOSTUNREACH: int = 113; export def EALREADY: int = 114; export def EINPROGRESS: int = 115; export def ESTALE: int = 116; export def EUCLEAN: int = 117; export def ENOTNAM: int = 118; export def ENAVAIL: int = 119; export def EISNAM: int = 120; export def EREMOTEIO: int = 121; export def EDQUOT: int = 122; export def ENOMEDIUM: int = 123; export def EMEDIUMTYPE: int = 124; export def ECANCELED: int = 125; export def ENOKEY: int = 126; export def EKEYEXPIRED: int = 127; export def EKEYREVOKED: int = 128; export def EKEYREJECTED: int = 129; export def EOWNERDEAD: int = 130; export def ENOTRECOVERABLE: int = 131; export def ERFKILL: int = 132; export def EHWPOISON: int = 133; harec-0.24.2/rt/+linux/segmalloc.ha000066400000000000000000000004711464473277600170260ustar00rootroot00000000000000// Allocates a segment. fn segmalloc(n: size) nullable *opaque = { let p: *opaque = mmap(null, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); return if (p: uintptr: int == -ENOMEM) null else p; }; // Frees a segment allocated with segmalloc. fn segfree(p: *opaque, s: size) int = munmap(p, s); harec-0.24.2/rt/+linux/start+aarch64.s000066400000000000000000000001441464473277600173100ustar00rootroot00000000000000.text .global _start _start: mov x29, #0 mov x30, #0 mov x0, sp add sp, x0, #-16 b rt.start_ha harec-0.24.2/rt/+linux/start+riscv64.s000066400000000000000000000001141464473277600173550ustar00rootroot00000000000000.text .global _start _start: mv a0, sp andi sp, sp, -16 tail rt.start_ha harec-0.24.2/rt/+linux/start+x86_64.s000066400000000000000000000001411464473277600170130ustar00rootroot00000000000000.text .global _start _start: xor %rbp, %rbp movq %rsp, %rdi andq $-16, %rsp call rt.start_ha harec-0.24.2/rt/+linux/start.ha000066400000000000000000000016311464473277600162140ustar00rootroot00000000000000@symbol("main") fn main() void; const @symbol("__init_array_start") init_start: [*]*fn() void; const @symbol("__init_array_end") init_end: [*]*fn() void; const @symbol("__fini_array_start") fini_start: [*]*fn() void; const @symbol("__fini_array_end") fini_end: [*]*fn() void; let argc: size = 0; let argv: *[*]*const u8 = null: *[*]*const u8; let envp: *[*]nullable *const u8 = null: *[*]nullable *const u8; export let exit_status = 0; export fn start_ha(iv: *[*]uintptr) never = { const ninit = (&init_end: uintptr - &init_start: uintptr): size / size(*fn() void); for (let i = 0z; i < ninit; i += 1) { init_start[i](); }; argc = iv[0]: size; argv = &iv[1]: *[*]*const u8; envp = &argv[argc + 1]: *[*]nullable *const u8; main(); const nfini = (&fini_end: uintptr - &fini_start: uintptr): size / size(*fn() void); for (let i = 0z; i < nfini; i += 1) { fini_start[i](); }; exit(exit_status); }; harec-0.24.2/rt/+linux/syscall+aarch64.s000066400000000000000000000015161464473277600176310ustar00rootroot00000000000000.section .text.rt.syscall0 .global rt.syscall0 rt.syscall0: mov x8, x0 svc 0 ret .section .text.rt.syscall1 .global rt.syscall1 rt.syscall1: mov x8, x0 mov x0, x1 svc 0 ret .section .text.rt.syscall2 .global rt.syscall2 rt.syscall2: mov x8, x0 mov x0, x1 mov x1, x2 svc 0 ret .section .text.rt.syscall3 .global rt.syscall3 rt.syscall3: mov x8, x0 mov x0, x1 mov x1, x2 mov x2, x3 svc 0 ret .section .text.rt.syscall4 .global rt.syscall4 rt.syscall4: mov x8, x0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 svc 0 ret .section .text.rt.syscall5 .global rt.syscall5 rt.syscall5: mov x8, x0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 mov x4, x5 svc 0 ret .section .text.rt.syscall6 .global rt.syscall6 rt.syscall6: mov x8, x0 mov x0, x1 mov x1, x2 mov x2, x3 mov x3, x4 mov x4, x5 mov x5, x6 svc 0 ret harec-0.24.2/rt/+linux/syscall+riscv64.s000066400000000000000000000014621464473277600177010ustar00rootroot00000000000000.section .text.rt.syscall0 .global rt.syscall0 rt.syscall0: mv a7, a0 ecall ret .section .text.rt.syscall1 .global rt.syscall1 rt.syscall1: mv a7, a0 mv a0, a1 ecall ret .section .text.rt.syscall2 .global rt.syscall2 rt.syscall2: mv a7, a0 mv a0, a1 mv a1, a2 ecall ret .section .text.rt.syscall3 .global rt.syscall3 rt.syscall3: mv a7, a0 mv a0, a1 mv a1, a2 mv a2, a3 ecall ret .section .text.rt.syscall4 .global rt.syscall4 rt.syscall4: mv a7, a0 mv a0, a1 mv a1, a2 mv a2, a3 mv a3, a4 ecall ret .section .text.rt.syscall5 .global rt.syscall5 rt.syscall5: mv a7, a0 mv a0, a1 mv a1, a2 mv a2, a3 mv a3, a4 mv a4, a5 ecall ret .section .text.rt.syscall6 .global rt.syscall6 rt.syscall6: mv a7, a0 mv a0, a1 mv a1, a2 mv a2, a3 mv a3, a4 mv a4, a5 mv a5, a6 ecall ret harec-0.24.2/rt/+linux/syscall+x86_64.s000066400000000000000000000017431464473277600173410ustar00rootroot00000000000000.section .text.rt.syscall0 .global rt.syscall0 rt.syscall0: movq %rdi, %rax syscall ret .section .text.rt.syscall1 .global rt.syscall1 rt.syscall1: movq %rdi, %rax movq %rsi, %rdi syscall ret .section .text.rt.syscall2 .global rt.syscall2 rt.syscall2: movq %rdi, %rax movq %rsi, %rdi movq %rdx, %rsi syscall ret .section .text.rt.syscall3 .global rt.syscall3 rt.syscall3: movq %rdi, %rax movq %rsi, %rdi movq %rdx, %rsi movq %rcx, %rdx syscall ret .section .text.rt.syscall4 .global rt.syscall4 rt.syscall4: movq %rdi, %rax movq %r8, %r10 movq %rsi, %rdi movq %rdx, %rsi movq %rcx, %rdx syscall ret .section .text.rt.syscall5 .global rt.syscall5 rt.syscall5: movq %rdi, %rax movq %r8, %r10 movq %rsi, %rdi movq %r9, %r8 movq %rdx, %rsi movq %rcx, %rdx syscall ret .section .text.rt.syscall6 .global rt.syscall6 rt.syscall6: movq %rdi, %rax movq %r8, %r10 movq %rsi, %rdi movq %r9, %r8 movq %rdx, %rsi movq 8(%rsp), %r9 movq %rcx, %rdx syscall ret harec-0.24.2/rt/+linux/syscallno+aarch64.ha000066400000000000000000000256721464473277600203250ustar00rootroot00000000000000export def SYS_io_setup: u64 = 0; export def SYS_io_destroy: u64 = 1; export def SYS_io_submit: u64 = 2; export def SYS_io_cancel: u64 = 3; export def SYS_io_getevents: u64 = 4; export def SYS_setxattr: u64 = 5; export def SYS_lsetxattr: u64 = 6; export def SYS_fsetxattr: u64 = 7; export def SYS_getxattr: u64 = 8; export def SYS_lgetxattr: u64 = 9; export def SYS_fgetxattr: u64 = 10; export def SYS_listxattr: u64 = 11; export def SYS_llistxattr: u64 = 12; export def SYS_flistxattr: u64 = 13; export def SYS_removexattr: u64 = 14; export def SYS_lremovexattr: u64 = 15; export def SYS_fremovexattr: u64 = 16; export def SYS_getcwd: u64 = 17; export def SYS_lookup_dcookie: u64 = 18; export def SYS_eventfd2: u64 = 19; export def SYS_epoll_create1: u64 = 20; export def SYS_epoll_ctl: u64 = 21; export def SYS_epoll_pwait: u64 = 22; export def SYS_dup: u64 = 23; export def SYS_dup3: u64 = 24; export def SYS_fcntl: u64 = 25; export def SYS_inotify_init1: u64 = 26; export def SYS_inotify_add_watch: u64 = 27; export def SYS_inotify_rm_watch: u64 = 28; export def SYS_ioctl: u64 = 29; export def SYS_ioprio_set: u64 = 30; export def SYS_ioprio_get: u64 = 31; export def SYS_flock: u64 = 32; export def SYS_mknodat: u64 = 33; export def SYS_mkdirat: u64 = 34; export def SYS_unlinkat: u64 = 35; export def SYS_symlinkat: u64 = 36; export def SYS_linkat: u64 = 37; export def SYS_renameat: u64 = 38; export def SYS_umount2: u64 = 39; export def SYS_mount: u64 = 40; export def SYS_pivot_root: u64 = 41; export def SYS_nfsservctl: u64 = 42; export def SYS_statfs: u64 = 43; export def SYS_fstatfs: u64 = 44; export def SYS_truncate: u64 = 45; export def SYS_ftruncate: u64 = 46; export def SYS_fallocate: u64 = 47; export def SYS_faccessat: u64 = 48; export def SYS_chdir: u64 = 49; export def SYS_fchdir: u64 = 50; export def SYS_chroot: u64 = 51; export def SYS_fchmod: u64 = 52; export def SYS_fchmodat: u64 = 53; export def SYS_fchownat: u64 = 54; export def SYS_fchown: u64 = 55; export def SYS_openat: u64 = 56; export def SYS_close: u64 = 57; export def SYS_vhangup: u64 = 58; export def SYS_pipe2: u64 = 59; export def SYS_quotactl: u64 = 60; export def SYS_getdents64: u64 = 61; export def SYS_lseek: u64 = 62; export def SYS_read: u64 = 63; export def SYS_write: u64 = 64; export def SYS_readv: u64 = 65; export def SYS_writev: u64 = 66; export def SYS_pread64: u64 = 67; export def SYS_pwrite64: u64 = 68; export def SYS_preadv: u64 = 69; export def SYS_pwritev: u64 = 70; export def SYS_sendfile: u64 = 71; export def SYS_pselect6: u64 = 72; export def SYS_ppoll: u64 = 73; export def SYS_signalfd4: u64 = 74; export def SYS_vmsplice: u64 = 75; export def SYS_splice: u64 = 76; export def SYS_tee: u64 = 77; export def SYS_readlinkat: u64 = 78; export def SYS_newfstatat: u64 = 79; export def SYS_fstat: u64 = 80; export def SYS_sync: u64 = 81; export def SYS_fsync: u64 = 82; export def SYS_fdatasync: u64 = 83; export def SYS_sync_file_range: u64 = 84; export def SYS_timerfd_create: u64 = 85; export def SYS_timerfd_settime: u64 = 86; export def SYS_timerfd_gettime: u64 = 87; export def SYS_utimensat: u64 = 88; export def SYS_acct: u64 = 89; export def SYS_capget: u64 = 90; export def SYS_capset: u64 = 91; export def SYS_personality: u64 = 92; export def SYS_exit: u64 = 93; export def SYS_exit_group: u64 = 94; export def SYS_waitid: u64 = 95; export def SYS_set_tid_address: u64 = 96; export def SYS_unshare: u64 = 97; export def SYS_futex: u64 = 98; export def SYS_set_robust_list: u64 = 99; export def SYS_get_robust_list: u64 = 100; export def SYS_nanosleep: u64 = 101; export def SYS_getitimer: u64 = 102; export def SYS_setitimer: u64 = 103; export def SYS_kexec_load: u64 = 104; export def SYS_init_module: u64 = 105; export def SYS_delete_module: u64 = 106; export def SYS_timer_create: u64 = 107; export def SYS_timer_gettime: u64 = 108; export def SYS_timer_getoverrun: u64 = 109; export def SYS_timer_settime: u64 = 110; export def SYS_timer_delete: u64 = 111; export def SYS_clock_settime: u64 = 112; export def SYS_clock_gettime: u64 = 113; export def SYS_clock_getres: u64 = 114; export def SYS_clock_nanosleep: u64 = 115; export def SYS_syslog: u64 = 116; export def SYS_ptrace: u64 = 117; export def SYS_sched_setparam: u64 = 118; export def SYS_sched_setscheduler: u64 = 119; export def SYS_sched_getscheduler: u64 = 120; export def SYS_sched_getparam: u64 = 121; export def SYS_sched_setaffinity: u64 = 122; export def SYS_sched_getaffinity: u64 = 123; export def SYS_sched_yield: u64 = 124; export def SYS_sched_get_priority_max: u64 = 125; export def SYS_sched_get_priority_min: u64 = 126; export def SYS_sched_rr_get_interval: u64 = 127; export def SYS_restart_syscall: u64 = 128; export def SYS_kill: u64 = 129; export def SYS_tkill: u64 = 130; export def SYS_tgkill: u64 = 131; export def SYS_sigaltstack: u64 = 132; export def SYS_rt_sigsuspend: u64 = 133; export def SYS_rt_sigaction: u64 = 134; export def SYS_rt_sigprocmask: u64 = 135; export def SYS_rt_sigpending: u64 = 136; export def SYS_rt_sigtimedwait: u64 = 137; export def SYS_rt_sigqueueinfo: u64 = 138; export def SYS_rt_sigreturn: u64 = 139; export def SYS_setpriority: u64 = 140; export def SYS_getpriority: u64 = 141; export def SYS_reboot: u64 = 142; export def SYS_setregid: u64 = 143; export def SYS_setgid: u64 = 144; export def SYS_setreuid: u64 = 145; export def SYS_setuid: u64 = 146; export def SYS_setresuid: u64 = 147; export def SYS_getresuid: u64 = 148; export def SYS_setresgid: u64 = 149; export def SYS_getresgid: u64 = 150; export def SYS_setfsuid: u64 = 151; export def SYS_setfsgid: u64 = 152; export def SYS_times: u64 = 153; export def SYS_setpgid: u64 = 154; export def SYS_getpgid: u64 = 155; export def SYS_getsid: u64 = 156; export def SYS_setsid: u64 = 157; export def SYS_getgroups: u64 = 158; export def SYS_setgroups: u64 = 159; export def SYS_uname: u64 = 160; export def SYS_sethostname: u64 = 161; export def SYS_setdomainname: u64 = 162; export def SYS_getrlimit: u64 = 163; export def SYS_setrlimit: u64 = 164; export def SYS_getrusage: u64 = 165; export def SYS_umask: u64 = 166; export def SYS_prctl: u64 = 167; export def SYS_getcpu: u64 = 168; export def SYS_gettimeofday: u64 = 169; export def SYS_settimeofday: u64 = 170; export def SYS_adjtimex: u64 = 171; export def SYS_getpid: u64 = 172; export def SYS_getppid: u64 = 173; export def SYS_getuid: u64 = 174; export def SYS_geteuid: u64 = 175; export def SYS_getgid: u64 = 176; export def SYS_getegid: u64 = 177; export def SYS_gettid: u64 = 178; export def SYS_sysinfo: u64 = 179; export def SYS_mq_open: u64 = 180; export def SYS_mq_unlink: u64 = 181; export def SYS_mq_timedsend: u64 = 182; export def SYS_mq_timedreceive: u64 = 183; export def SYS_mq_notify: u64 = 184; export def SYS_mq_getsetattr: u64 = 185; export def SYS_msgget: u64 = 186; export def SYS_msgctl: u64 = 187; export def SYS_msgrcv: u64 = 188; export def SYS_msgsnd: u64 = 189; export def SYS_semget: u64 = 190; export def SYS_semctl: u64 = 191; export def SYS_semtimedop: u64 = 192; export def SYS_semop: u64 = 193; export def SYS_shmget: u64 = 194; export def SYS_shmctl: u64 = 195; export def SYS_shmat: u64 = 196; export def SYS_shmdt: u64 = 197; export def SYS_socket: u64 = 198; export def SYS_socketpair: u64 = 199; export def SYS_bind: u64 = 200; export def SYS_listen: u64 = 201; export def SYS_accept: u64 = 202; export def SYS_connect: u64 = 203; export def SYS_getsockname: u64 = 204; export def SYS_getpeername: u64 = 205; export def SYS_sendto: u64 = 206; export def SYS_recvfrom: u64 = 207; export def SYS_setsockopt: u64 = 208; export def SYS_getsockopt: u64 = 209; export def SYS_shutdown: u64 = 210; export def SYS_sendmsg: u64 = 211; export def SYS_recvmsg: u64 = 212; export def SYS_readahead: u64 = 213; export def SYS_brk: u64 = 214; export def SYS_munmap: u64 = 215; export def SYS_mremap: u64 = 216; export def SYS_add_key: u64 = 217; export def SYS_request_key: u64 = 218; export def SYS_keyctl: u64 = 219; export def SYS_clone: u64 = 220; export def SYS_execve: u64 = 221; export def SYS_mmap: u64 = 222; export def SYS_fadvise64: u64 = 223; export def SYS_swapon: u64 = 224; export def SYS_swapoff: u64 = 225; export def SYS_mprotect: u64 = 226; export def SYS_msync: u64 = 227; export def SYS_mlock: u64 = 228; export def SYS_munlock: u64 = 229; export def SYS_mlockall: u64 = 230; export def SYS_munlockall: u64 = 231; export def SYS_mincore: u64 = 232; export def SYS_madvise: u64 = 233; export def SYS_remap_file_pages: u64 = 234; export def SYS_mbind: u64 = 235; export def SYS_get_mempolicy: u64 = 236; export def SYS_set_mempolicy: u64 = 237; export def SYS_migrate_pages: u64 = 238; export def SYS_move_pages: u64 = 239; export def SYS_rt_tgsigqueueinfo: u64 = 240; export def SYS_perf_event_open: u64 = 241; export def SYS_accept4: u64 = 242; export def SYS_recvmmsg: u64 = 243; export def SYS_wait4: u64 = 260; export def SYS_prlimit64: u64 = 261; export def SYS_fanotify_init: u64 = 262; export def SYS_fanotify_mark: u64 = 263; export def SYS_name_to_handle_at: u64 = 264; export def SYS_open_by_handle_at: u64 = 265; export def SYS_clock_adjtime: u64 = 266; export def SYS_syncfs: u64 = 267; export def SYS_setns: u64 = 268; export def SYS_sendmmsg: u64 = 269; export def SYS_process_vm_readv: u64 = 270; export def SYS_process_vm_writev: u64 = 271; export def SYS_kcmp: u64 = 272; export def SYS_finit_module: u64 = 273; export def SYS_sched_setattr: u64 = 274; export def SYS_sched_getattr: u64 = 275; export def SYS_renameat2: u64 = 276; export def SYS_seccomp: u64 = 277; export def SYS_getrandom: u64 = 278; export def SYS_memfd_create: u64 = 279; export def SYS_bpf: u64 = 280; export def SYS_execveat: u64 = 281; export def SYS_userfaultfd: u64 = 282; export def SYS_membarrier: u64 = 283; export def SYS_mlock2: u64 = 284; export def SYS_copy_file_range: u64 = 285; export def SYS_preadv2: u64 = 286; export def SYS_pwritev2: u64 = 287; export def SYS_pkey_mprotect: u64 = 288; export def SYS_pkey_alloc: u64 = 289; export def SYS_pkey_free: u64 = 290; export def SYS_statx: u64 = 291; export def SYS_io_pgetevents: u64 = 292; export def SYS_rseq: u64 = 293; export def SYS_kexec_file_load: u64 = 294; export def SYS_pidfd_send_signal: u64 = 424; export def SYS_io_uring_setup: u64 = 425; export def SYS_io_uring_enter: u64 = 426; export def SYS_io_uring_register: u64 = 427; export def SYS_open_tree: u64 = 428; export def SYS_move_mount: u64 = 429; export def SYS_fsopen: u64 = 430; export def SYS_fsconfig: u64 = 431; export def SYS_fsmount: u64 = 432; export def SYS_fspick: u64 = 433; export def SYS_pidfd_open: u64 = 434; export def SYS_clone3: u64 = 435; harec-0.24.2/rt/+linux/syscallno+riscv64.ha000066400000000000000000000253501464473277600203660ustar00rootroot00000000000000export def SYS_io_setup: u64 = 0; export def SYS_io_destroy: u64 = 1; export def SYS_io_submit: u64 = 2; export def SYS_io_cancel: u64 = 3; export def SYS_io_getevents: u64 = 4; export def SYS_setxattr: u64 = 5; export def SYS_lsetxattr: u64 = 6; export def SYS_fsetxattr: u64 = 7; export def SYS_getxattr: u64 = 8; export def SYS_lgetxattr: u64 = 9; export def SYS_fgetxattr: u64 = 10; export def SYS_listxattr: u64 = 11; export def SYS_llistxattr: u64 = 12; export def SYS_flistxattr: u64 = 13; export def SYS_removexattr: u64 = 14; export def SYS_lremovexattr: u64 = 15; export def SYS_fremovexattr: u64 = 16; export def SYS_getcwd: u64 = 17; export def SYS_lookup_dcookie: u64 = 18; export def SYS_eventfd2: u64 = 19; export def SYS_epoll_create1: u64 = 20; export def SYS_epoll_ctl: u64 = 21; export def SYS_epoll_pwait: u64 = 22; export def SYS_dup: u64 = 23; export def SYS_dup3: u64 = 24; export def SYS_fcntl: u64 = 25; export def SYS_inotify_init1: u64 = 26; export def SYS_inotify_add_watch: u64 = 27; export def SYS_inotify_rm_watch: u64 = 28; export def SYS_ioctl: u64 = 29; export def SYS_ioprio_set: u64 = 30; export def SYS_ioprio_get: u64 = 31; export def SYS_flock: u64 = 32; export def SYS_mknodat: u64 = 33; export def SYS_mkdirat: u64 = 34; export def SYS_unlinkat: u64 = 35; export def SYS_symlinkat: u64 = 36; export def SYS_linkat: u64 = 37; export def SYS_umount2: u64 = 39; export def SYS_mount: u64 = 40; export def SYS_pivot_root: u64 = 41; export def SYS_nfsservctl: u64 = 42; export def SYS_statfs: u64 = 43; export def SYS_fstatfs: u64 = 44; export def SYS_truncate: u64 = 45; export def SYS_ftruncate: u64 = 46; export def SYS_fallocate: u64 = 47; export def SYS_faccessat: u64 = 48; export def SYS_chdir: u64 = 49; export def SYS_fchdir: u64 = 50; export def SYS_chroot: u64 = 51; export def SYS_fchmod: u64 = 52; export def SYS_fchmodat: u64 = 53; export def SYS_fchownat: u64 = 54; export def SYS_fchown: u64 = 55; export def SYS_openat: u64 = 56; export def SYS_close: u64 = 57; export def SYS_vhangup: u64 = 58; export def SYS_pipe2: u64 = 59; export def SYS_quotactl: u64 = 60; export def SYS_getdents64: u64 = 61; export def SYS_lseek: u64 = 62; export def SYS_read: u64 = 63; export def SYS_write: u64 = 64; export def SYS_readv: u64 = 65; export def SYS_writev: u64 = 66; export def SYS_pread64: u64 = 67; export def SYS_pwrite64: u64 = 68; export def SYS_preadv: u64 = 69; export def SYS_pwritev: u64 = 70; export def SYS_sendfile: u64 = 71; export def SYS_pselect6: u64 = 72; export def SYS_ppoll: u64 = 73; export def SYS_signalfd4: u64 = 74; export def SYS_vmsplice: u64 = 75; export def SYS_splice: u64 = 76; export def SYS_tee: u64 = 77; export def SYS_readlinkat: u64 = 78; export def SYS_fstatat: u64 = 79; export def SYS_fstat: u64 = 80; export def SYS_sync: u64 = 81; export def SYS_fsync: u64 = 82; export def SYS_fdatasync: u64 = 83; export def SYS_sync_file_range: u64 = 84; export def SYS_timerfd_create: u64 = 85; export def SYS_timerfd_settime: u64 = 86; export def SYS_timerfd_gettime: u64 = 87; export def SYS_utimensat: u64 = 88; export def SYS_acct: u64 = 89; export def SYS_capget: u64 = 90; export def SYS_capset: u64 = 91; export def SYS_personality: u64 = 92; export def SYS_exit: u64 = 93; export def SYS_exit_group: u64 = 94; export def SYS_waitid: u64 = 95; export def SYS_set_tid_address: u64 = 96; export def SYS_unshare: u64 = 97; export def SYS_futex: u64 = 98; export def SYS_set_robust_list: u64 = 99; export def SYS_get_robust_list: u64 = 100; export def SYS_nanosleep: u64 = 101; export def SYS_getitimer: u64 = 102; export def SYS_setitimer: u64 = 103; export def SYS_kexec_load: u64 = 104; export def SYS_init_module: u64 = 105; export def SYS_delete_module: u64 = 106; export def SYS_timer_create: u64 = 107; export def SYS_timer_gettime: u64 = 108; export def SYS_timer_getoverrun: u64 = 109; export def SYS_timer_settime: u64 = 110; export def SYS_timer_delete: u64 = 111; export def SYS_clock_settime: u64 = 112; export def SYS_clock_gettime: u64 = 113; export def SYS_clock_getres: u64 = 114; export def SYS_clock_nanosleep: u64 = 115; export def SYS_syslog: u64 = 116; export def SYS_ptrace: u64 = 117; export def SYS_sched_setparam: u64 = 118; export def SYS_sched_setscheduler: u64 = 119; export def SYS_sched_getscheduler: u64 = 120; export def SYS_sched_getparam: u64 = 121; export def SYS_sched_setaffinity: u64 = 122; export def SYS_sched_getaffinity: u64 = 123; export def SYS_sched_yield: u64 = 124; export def SYS_sched_get_priority_max: u64 = 125; export def SYS_sched_get_priority_min: u64 = 126; export def SYS_sched_rr_get_interval: u64 = 127; export def SYS_restart_syscall: u64 = 128; export def SYS_kill: u64 = 129; export def SYS_tkill: u64 = 130; export def SYS_tgkill: u64 = 131; export def SYS_sigaltstack: u64 = 132; export def SYS_rt_sigsuspend: u64 = 133; export def SYS_rt_sigaction: u64 = 134; export def SYS_rt_sigprocmask: u64 = 135; export def SYS_rt_sigpending: u64 = 136; export def SYS_rt_sigtimedwait: u64 = 137; export def SYS_rt_sigqueueinfo: u64 = 138; export def SYS_rt_sigreturn: u64 = 139; export def SYS_setpriority: u64 = 140; export def SYS_getpriority: u64 = 141; export def SYS_reboot: u64 = 142; export def SYS_setregid: u64 = 143; export def SYS_setgid: u64 = 144; export def SYS_setreuid: u64 = 145; export def SYS_setuid: u64 = 146; export def SYS_setresuid: u64 = 147; export def SYS_getresuid: u64 = 148; export def SYS_setresgid: u64 = 149; export def SYS_getresgid: u64 = 150; export def SYS_setfsuid: u64 = 151; export def SYS_setfsgid: u64 = 152; export def SYS_times: u64 = 153; export def SYS_setpgid: u64 = 154; export def SYS_getpgid: u64 = 155; export def SYS_getsid: u64 = 156; export def SYS_setsid: u64 = 157; export def SYS_getgroups: u64 = 158; export def SYS_setgroups: u64 = 159; export def SYS_uname: u64 = 160; export def SYS_sethostname: u64 = 161; export def SYS_setdomainname: u64 = 162; export def SYS_getrlimit: u64 = 163; export def SYS_setrlimit: u64 = 164; export def SYS_getrusage: u64 = 165; export def SYS_umask: u64 = 166; export def SYS_prctl: u64 = 167; export def SYS_getcpu: u64 = 168; export def SYS_gettimeofday: u64 = 169; export def SYS_settimeofday: u64 = 170; export def SYS_adjtimex: u64 = 171; export def SYS_getpid: u64 = 172; export def SYS_getppid: u64 = 173; export def SYS_getuid: u64 = 174; export def SYS_geteuid: u64 = 175; export def SYS_getgid: u64 = 176; export def SYS_getegid: u64 = 177; export def SYS_gettid: u64 = 178; export def SYS_sysinfo: u64 = 179; export def SYS_mq_open: u64 = 180; export def SYS_mq_unlink: u64 = 181; export def SYS_mq_timedsend: u64 = 182; export def SYS_mq_timedreceive: u64 = 183; export def SYS_mq_notify: u64 = 184; export def SYS_mq_getsetattr: u64 = 185; export def SYS_msgget: u64 = 186; export def SYS_msgctl: u64 = 187; export def SYS_msgrcv: u64 = 188; export def SYS_msgsnd: u64 = 189; export def SYS_semget: u64 = 190; export def SYS_semctl: u64 = 191; export def SYS_semtimedop: u64 = 192; export def SYS_semop: u64 = 193; export def SYS_shmget: u64 = 194; export def SYS_shmctl: u64 = 195; export def SYS_shmat: u64 = 196; export def SYS_shmdt: u64 = 197; export def SYS_socket: u64 = 198; export def SYS_socketpair: u64 = 199; export def SYS_bind: u64 = 200; export def SYS_listen: u64 = 201; export def SYS_accept: u64 = 202; export def SYS_connect: u64 = 203; export def SYS_getsockname: u64 = 204; export def SYS_getpeername: u64 = 205; export def SYS_sendto: u64 = 206; export def SYS_recvfrom: u64 = 207; export def SYS_setsockopt: u64 = 208; export def SYS_getsockopt: u64 = 209; export def SYS_shutdown: u64 = 210; export def SYS_sendmsg: u64 = 211; export def SYS_recvmsg: u64 = 212; export def SYS_readahead: u64 = 213; export def SYS_brk: u64 = 214; export def SYS_munmap: u64 = 215; export def SYS_mremap: u64 = 216; export def SYS_add_key: u64 = 217; export def SYS_request_key: u64 = 218; export def SYS_keyctl: u64 = 219; export def SYS_clone: u64 = 220; export def SYS_execve: u64 = 221; export def SYS_mmap: u64 = 222; export def SYS_fadvise64: u64 = 223; export def SYS_swapon: u64 = 224; export def SYS_swapoff: u64 = 225; export def SYS_mprotect: u64 = 226; export def SYS_msync: u64 = 227; export def SYS_mlock: u64 = 228; export def SYS_munlock: u64 = 229; export def SYS_mlockall: u64 = 230; export def SYS_munlockall: u64 = 231; export def SYS_mincore: u64 = 232; export def SYS_madvise: u64 = 233; export def SYS_remap_file_pages: u64 = 234; export def SYS_mbind: u64 = 235; export def SYS_get_mempolicy: u64 = 236; export def SYS_set_mempolicy: u64 = 237; export def SYS_migrate_pages: u64 = 238; export def SYS_move_pages: u64 = 239; export def SYS_rt_tgsigqueueinfo: u64 = 240; export def SYS_perf_event_open: u64 = 241; export def SYS_accept4: u64 = 242; export def SYS_recvmmsg: u64 = 243; export def SYS_arch_specific_syscall: u64 = 244; export def SYS_wait4: u64 = 260; export def SYS_prlimit64: u64 = 261; export def SYS_fanotify_init: u64 = 262; export def SYS_fanotify_mark: u64 = 263; export def SYS_name_to_handle_at: u64 = 264; export def SYS_open_by_handle_at: u64 = 265; export def SYS_clock_adjtime: u64 = 266; export def SYS_syncfs: u64 = 267; export def SYS_setns: u64 = 268; export def SYS_sendmmsg: u64 = 269; export def SYS_process_vm_readv: u64 = 270; export def SYS_process_vm_writev: u64 = 271; export def SYS_kcmp: u64 = 272; export def SYS_finit_module: u64 = 273; export def SYS_sched_setattr: u64 = 274; export def SYS_sched_getattr: u64 = 275; export def SYS_renameat2: u64 = 276; export def SYS_seccomp: u64 = 277; export def SYS_getrandom: u64 = 278; export def SYS_memfd_create: u64 = 279; export def SYS_bpf: u64 = 280; export def SYS_execveat: u64 = 281; export def SYS_userfaultfd: u64 = 282; export def SYS_membarrier: u64 = 283; export def SYS_mlock2: u64 = 284; export def SYS_copy_file_range: u64 = 285; export def SYS_preadv2: u64 = 286; export def SYS_pwritev2: u64 = 287; export def SYS_pkey_mprotect: u64 = 288; export def SYS_pkey_alloc: u64 = 289; export def SYS_pkey_free: u64 = 290; export def SYS_statx: u64 = 291; export def SYS_io_pgetevents: u64 = 292; export def SYS_rseq: u64 = 293; export def SYS_kexec_file_load: u64 = 294; export def SYS_pidfd_send_signal: u64 = 424; export def SYS_io_uring_setup: u64 = 425; export def SYS_io_uring_enter: u64 = 426; export def SYS_io_uring_register: u64 = 427; export def SYS_open_tree: u64 = 428; export def SYS_move_mount: u64 = 429; export def SYS_fsopen: u64 = 430; export def SYS_fsconfig: u64 = 431; export def SYS_fsmount: u64 = 432; export def SYS_fspick: u64 = 433; export def SYS_pidfd_open: u64 = 434; export def SYS_clone3: u64 = 435; export def SYS_close_range: u64 = 436; export def SYS_openat2: u64 = 437; export def SYS_pidfd_getfd: u64 = 438; export def SYS_faccessat2: u64 = 439; // RISC-V specific export def SYS_sysriscv: u64 = SYS_arch_specific_syscall; export def SYS_riscv_flush_icache: u64 = SYS_sysriscv + 15; harec-0.24.2/rt/+linux/syscallno+x86_64.ha000066400000000000000000000316151464473277600200250ustar00rootroot00000000000000export def SYS_read: u64 = 0; export def SYS_write: u64 = 1; export def SYS_open: u64 = 2; export def SYS_close: u64 = 3; export def SYS_stat: u64 = 4; export def SYS_fstat: u64 = 5; export def SYS_lstat: u64 = 6; export def SYS_poll: u64 = 7; export def SYS_lseek: u64 = 8; export def SYS_mmap: u64 = 9; export def SYS_mprotect: u64 = 10; export def SYS_munmap: u64 = 11; export def SYS_brk: u64 = 12; export def SYS_rt_sigaction: u64 = 13; export def SYS_rt_sigprocmask: u64 = 14; export def SYS_rt_sigreturn: u64 = 15; export def SYS_ioctl: u64 = 16; export def SYS_pread64: u64 = 17; export def SYS_pwrite64: u64 = 18; export def SYS_readv: u64 = 19; export def SYS_writev: u64 = 20; export def SYS_access: u64 = 21; export def SYS_pipe: u64 = 22; export def SYS_select: u64 = 23; export def SYS_sched_yield: u64 = 24; export def SYS_mremap: u64 = 25; export def SYS_msync: u64 = 26; export def SYS_mincore: u64 = 27; export def SYS_madvise: u64 = 28; export def SYS_shmget: u64 = 29; export def SYS_shmat: u64 = 30; export def SYS_shmctl: u64 = 31; export def SYS_dup: u64 = 32; export def SYS_dup2: u64 = 33; export def SYS_pause: u64 = 34; export def SYS_nanosleep: u64 = 35; export def SYS_getitimer: u64 = 36; export def SYS_alarm: u64 = 37; export def SYS_setitimer: u64 = 38; export def SYS_getpid: u64 = 39; export def SYS_sendfile: u64 = 40; export def SYS_socket: u64 = 41; export def SYS_connect: u64 = 42; export def SYS_accept: u64 = 43; export def SYS_sendto: u64 = 44; export def SYS_recvfrom: u64 = 45; export def SYS_sendmsg: u64 = 46; export def SYS_recvmsg: u64 = 47; export def SYS_shutdown: u64 = 48; export def SYS_bind: u64 = 49; export def SYS_listen: u64 = 50; export def SYS_getsockname: u64 = 51; export def SYS_getpeername: u64 = 52; export def SYS_socketpair: u64 = 53; export def SYS_setsockopt: u64 = 54; export def SYS_getsockopt: u64 = 55; export def SYS_clone: u64 = 56; export def SYS_fork: u64 = 57; export def SYS_vfork: u64 = 58; export def SYS_execve: u64 = 59; export def SYS_exit: u64 = 60; export def SYS_wait4: u64 = 61; export def SYS_kill: u64 = 62; export def SYS_uname: u64 = 63; export def SYS_semget: u64 = 64; export def SYS_semop: u64 = 65; export def SYS_semctl: u64 = 66; export def SYS_shmdt: u64 = 67; export def SYS_msgget: u64 = 68; export def SYS_msgsnd: u64 = 69; export def SYS_msgrcv: u64 = 70; export def SYS_msgctl: u64 = 71; export def SYS_fcntl: u64 = 72; export def SYS_flock: u64 = 73; export def SYS_fsync: u64 = 74; export def SYS_fdatasync: u64 = 75; export def SYS_truncate: u64 = 76; export def SYS_ftruncate: u64 = 77; export def SYS_getdents: u64 = 78; export def SYS_getcwd: u64 = 79; export def SYS_chdir: u64 = 80; export def SYS_fchdir: u64 = 81; export def SYS_rename: u64 = 82; export def SYS_mkdir: u64 = 83; export def SYS_rmdir: u64 = 84; export def SYS_creat: u64 = 85; export def SYS_link: u64 = 86; export def SYS_unlink: u64 = 87; export def SYS_symlink: u64 = 88; export def SYS_readlink: u64 = 89; export def SYS_chmod: u64 = 90; export def SYS_fchmod: u64 = 91; export def SYS_chown: u64 = 92; export def SYS_fchown: u64 = 93; export def SYS_lchown: u64 = 94; export def SYS_umask: u64 = 95; export def SYS_gettimeofday: u64 = 96; export def SYS_getrlimit: u64 = 97; export def SYS_getrusage: u64 = 98; export def SYS_sysinfo: u64 = 99; export def SYS_times: u64 = 100; export def SYS_ptrace: u64 = 101; export def SYS_getuid: u64 = 102; export def SYS_syslog: u64 = 103; export def SYS_getgid: u64 = 104; export def SYS_setuid: u64 = 105; export def SYS_setgid: u64 = 106; export def SYS_geteuid: u64 = 107; export def SYS_getegid: u64 = 108; export def SYS_setpgid: u64 = 109; export def SYS_getppid: u64 = 110; export def SYS_getpgrp: u64 = 111; export def SYS_setsid: u64 = 112; export def SYS_setreuid: u64 = 113; export def SYS_setregid: u64 = 114; export def SYS_getgroups: u64 = 115; export def SYS_setgroups: u64 = 116; export def SYS_setresuid: u64 = 117; export def SYS_getresuid: u64 = 118; export def SYS_setresgid: u64 = 119; export def SYS_getresgid: u64 = 120; export def SYS_getpgid: u64 = 121; export def SYS_setfsuid: u64 = 122; export def SYS_setfsgid: u64 = 123; export def SYS_getsid: u64 = 124; export def SYS_capget: u64 = 125; export def SYS_capset: u64 = 126; export def SYS_rt_sigpending: u64 = 127; export def SYS_rt_sigtimedwait: u64 = 128; export def SYS_rt_sigqueueinfo: u64 = 129; export def SYS_rt_sigsuspend: u64 = 130; export def SYS_sigaltstack: u64 = 131; export def SYS_utime: u64 = 132; export def SYS_mknod: u64 = 133; export def SYS_uselib: u64 = 134; export def SYS_personality: u64 = 135; export def SYS_ustat: u64 = 136; export def SYS_statfs: u64 = 137; export def SYS_fstatfs: u64 = 138; export def SYS_sysfs: u64 = 139; export def SYS_getpriority: u64 = 140; export def SYS_setpriority: u64 = 141; export def SYS_sched_setparam: u64 = 142; export def SYS_sched_getparam: u64 = 143; export def SYS_sched_setscheduler: u64 = 144; export def SYS_sched_getscheduler: u64 = 145; export def SYS_sched_get_priority_max: u64 = 146; export def SYS_sched_get_priority_min: u64 = 147; export def SYS_sched_rr_get_interval: u64 = 148; export def SYS_mlock: u64 = 149; export def SYS_munlock: u64 = 150; export def SYS_mlockall: u64 = 151; export def SYS_munlockall: u64 = 152; export def SYS_vhangup: u64 = 153; export def SYS_modify_ldt: u64 = 154; export def SYS_pivot_root: u64 = 155; export def SYS__sysctl: u64 = 156; export def SYS_prctl: u64 = 157; export def SYS_arch_prctl: u64 = 158; export def SYS_adjtimex: u64 = 159; export def SYS_setrlimit: u64 = 160; export def SYS_chroot: u64 = 161; export def SYS_sync: u64 = 162; export def SYS_acct: u64 = 163; export def SYS_settimeofday: u64 = 164; export def SYS_mount: u64 = 165; export def SYS_umount2: u64 = 166; export def SYS_swapon: u64 = 167; export def SYS_swapoff: u64 = 168; export def SYS_reboot: u64 = 169; export def SYS_sethostname: u64 = 170; export def SYS_setdomainname: u64 = 171; export def SYS_iopl: u64 = 172; export def SYS_ioperm: u64 = 173; export def SYS_create_module: u64 = 174; export def SYS_init_module: u64 = 175; export def SYS_delete_module: u64 = 176; export def SYS_get_kernel_syms: u64 = 177; export def SYS_query_module: u64 = 178; export def SYS_quotactl: u64 = 179; export def SYS_nfsservctl: u64 = 180; export def SYS_getpmsg: u64 = 181; export def SYS_putpmsg: u64 = 182; export def SYS_afs_syscall: u64 = 183; export def SYS_tuxcall: u64 = 184; export def SYS_security: u64 = 185; export def SYS_gettid: u64 = 186; export def SYS_readahead: u64 = 187; export def SYS_setxattr: u64 = 188; export def SYS_lsetxattr: u64 = 189; export def SYS_fsetxattr: u64 = 190; export def SYS_getxattr: u64 = 191; export def SYS_lgetxattr: u64 = 192; export def SYS_fgetxattr: u64 = 193; export def SYS_listxattr: u64 = 194; export def SYS_llistxattr: u64 = 195; export def SYS_flistxattr: u64 = 196; export def SYS_removexattr: u64 = 197; export def SYS_lremovexattr: u64 = 198; export def SYS_fremovexattr: u64 = 199; export def SYS_tkill: u64 = 200; export def SYS_time: u64 = 201; export def SYS_futex: u64 = 202; export def SYS_sched_setaffinity: u64 = 203; export def SYS_sched_getaffinity: u64 = 204; export def SYS_set_thread_area: u64 = 205; export def SYS_io_setup: u64 = 206; export def SYS_io_destroy: u64 = 207; export def SYS_io_getevents: u64 = 208; export def SYS_io_submit: u64 = 209; export def SYS_io_cancel: u64 = 210; export def SYS_get_thread_area: u64 = 211; export def SYS_lookup_dcookie: u64 = 212; export def SYS_epoll_create: u64 = 213; export def SYS_epoll_ctl_old: u64 = 214; export def SYS_epoll_wait_old: u64 = 215; export def SYS_remap_file_pages: u64 = 216; export def SYS_getdents64: u64 = 217; export def SYS_set_tid_address: u64 = 218; export def SYS_restart_syscall: u64 = 219; export def SYS_semtimedop: u64 = 220; export def SYS_fadvise64: u64 = 221; export def SYS_timer_create: u64 = 222; export def SYS_timer_settime: u64 = 223; export def SYS_timer_gettime: u64 = 224; export def SYS_timer_getoverrun: u64 = 225; export def SYS_timer_delete: u64 = 226; export def SYS_clock_settime: u64 = 227; export def SYS_clock_gettime: u64 = 228; export def SYS_clock_getres: u64 = 229; export def SYS_clock_nanosleep: u64 = 230; export def SYS_exit_group: u64 = 231; export def SYS_epoll_wait: u64 = 232; export def SYS_epoll_ctl: u64 = 233; export def SYS_tgkill: u64 = 234; export def SYS_utimes: u64 = 235; export def SYS_vserver: u64 = 236; export def SYS_mbind: u64 = 237; export def SYS_set_mempolicy: u64 = 238; export def SYS_get_mempolicy: u64 = 239; export def SYS_mq_open: u64 = 240; export def SYS_mq_unlink: u64 = 241; export def SYS_mq_timedsend: u64 = 242; export def SYS_mq_timedreceive: u64 = 243; export def SYS_mq_notify: u64 = 244; export def SYS_mq_getsetattr: u64 = 245; export def SYS_kexec_load: u64 = 246; export def SYS_waitid: u64 = 247; export def SYS_add_key: u64 = 248; export def SYS_request_key: u64 = 249; export def SYS_keyctl: u64 = 250; export def SYS_ioprio_set: u64 = 251; export def SYS_ioprio_get: u64 = 252; export def SYS_inotify_init: u64 = 253; export def SYS_inotify_add_watch: u64 = 254; export def SYS_inotify_rm_watch: u64 = 255; export def SYS_migrate_pages: u64 = 256; export def SYS_openat: u64 = 257; export def SYS_mkdirat: u64 = 258; export def SYS_mknodat: u64 = 259; export def SYS_fchownat: u64 = 260; export def SYS_futimesat: u64 = 261; export def SYS_newfstatat: u64 = 262; export def SYS_unlinkat: u64 = 263; export def SYS_renameat: u64 = 264; export def SYS_linkat: u64 = 265; export def SYS_symlinkat: u64 = 266; export def SYS_readlinkat: u64 = 267; export def SYS_fchmodat: u64 = 268; export def SYS_faccessat: u64 = 269; export def SYS_pselect6: u64 = 270; export def SYS_ppoll: u64 = 271; export def SYS_unshare: u64 = 272; export def SYS_set_robust_list: u64 = 273; export def SYS_get_robust_list: u64 = 274; export def SYS_splice: u64 = 275; export def SYS_tee: u64 = 276; export def SYS_sync_file_range: u64 = 277; export def SYS_vmsplice: u64 = 278; export def SYS_move_pages: u64 = 279; export def SYS_utimensat: u64 = 280; export def SYS_epoll_pwait: u64 = 281; export def SYS_signalfd: u64 = 282; export def SYS_timerfd_create: u64 = 283; export def SYS_eventfd: u64 = 284; export def SYS_fallocate: u64 = 285; export def SYS_timerfd_settime: u64 = 286; export def SYS_timerfd_gettime: u64 = 287; export def SYS_accept4: u64 = 288; export def SYS_signalfd4: u64 = 289; export def SYS_eventfd2: u64 = 290; export def SYS_epoll_create1: u64 = 291; export def SYS_dup3: u64 = 292; export def SYS_pipe2: u64 = 293; export def SYS_inotify_init1: u64 = 294; export def SYS_preadv: u64 = 295; export def SYS_pwritev: u64 = 296; export def SYS_rt_tgsigqueueinfo: u64 = 297; export def SYS_perf_event_open: u64 = 298; export def SYS_recvmmsg: u64 = 299; export def SYS_fanotify_init: u64 = 300; export def SYS_fanotify_mark: u64 = 301; export def SYS_prlimit64: u64 = 302; export def SYS_name_to_handle_at: u64 = 303; export def SYS_open_by_handle_at: u64 = 304; export def SYS_clock_adjtime: u64 = 305; export def SYS_syncfs: u64 = 306; export def SYS_sendmmsg: u64 = 307; export def SYS_setns: u64 = 308; export def SYS_getcpu: u64 = 309; export def SYS_process_vm_readv: u64 = 310; export def SYS_process_vm_writev: u64 = 311; export def SYS_kcmp: u64 = 312; export def SYS_finit_module: u64 = 313; export def SYS_sched_setattr: u64 = 314; export def SYS_sched_getattr: u64 = 315; export def SYS_renameat2: u64 = 316; export def SYS_seccomp: u64 = 317; export def SYS_getrandom: u64 = 318; export def SYS_memfd_create: u64 = 319; export def SYS_kexec_file_load: u64 = 320; export def SYS_bpf: u64 = 321; export def SYS_execveat: u64 = 322; export def SYS_userfaultfd: u64 = 323; export def SYS_membarrier: u64 = 324; export def SYS_mlock2: u64 = 325; export def SYS_copy_file_range: u64 = 326; export def SYS_preadv2: u64 = 327; export def SYS_pwritev2: u64 = 328; export def SYS_pkey_mprotect: u64 = 329; export def SYS_pkey_alloc: u64 = 330; export def SYS_pkey_free: u64 = 331; export def SYS_statx: u64 = 332; export def SYS_io_pgetevents: u64 = 333; export def SYS_rseq: u64 = 334; export def SYS_pidfd_send_signal: u64 = 424; export def SYS_io_uring_setup: u64 = 425; export def SYS_io_uring_enter: u64 = 426; export def SYS_io_uring_register: u64 = 427; export def SYS_open_tree: u64 = 428; export def SYS_move_mount: u64 = 429; export def SYS_fsopen: u64 = 430; export def SYS_fsconfig: u64 = 431; export def SYS_fsmount: u64 = 432; export def SYS_fspick: u64 = 433; harec-0.24.2/rt/+linux/syscalls.ha000066400000000000000000000076541464473277600167270ustar00rootroot00000000000000fn syscall0(u64) u64; fn syscall1(u64, u64) u64; fn syscall2(u64, u64, u64) u64; fn syscall3(u64, u64, u64, u64) u64; fn syscall4(u64, u64, u64, u64, u64) u64; fn syscall5(u64, u64, u64, u64, u64, u64) u64; fn syscall6(u64, u64, u64, u64, u64, u64, u64) u64; export fn write(fd: int, buf: *const opaque, count: size) size = syscall3(SYS_write, fd: u64, buf: uintptr: u64, count: u64): size; export fn close(fd: int) int = syscall1(SYS_close, fd: u64): int; export fn dup3(old: int, new: int, flags: int) int = syscall3(SYS_dup3, old: u64, new: u64, flags: u64): int; export fn dup2(old: int, new: int) int = syscall3(SYS_dup3, old: u64, new: u64, 0): int; export fn getpid() int = syscall0(SYS_getpid): int; export def EXIT_SUCCESS: int = 0; export fn exit(status: int) never = { syscall1(SYS_exit, status: u64); abort(); }; export fn fork() int = syscall2(SYS_clone, SIGCHLD: u64, 0u64): int; export fn execve( path: *const u8, argv: *[*]nullable *const u8, envp: *[*]nullable *const u8, ) int = syscall3(SYS_execve, path: uintptr: u64, argv: uintptr: u64, envp: uintptr: u64): int; export fn wait4(pid: int, status: *int, options: int, rusage: nullable *opaque) void = { syscall4(SYS_wait4, pid: u64, status: uintptr: u64, options: u64, rusage: uintptr: u64); }; export fn wifexited(status: int) bool = wtermsig(status) == 0; export fn wexitstatus(status: int) int = (status & 0xff00) >> 8; export fn wtermsig(status: int) int = status & 0x7f; export fn wifsignaled(status: int) bool = (status & 0xffff) - 1 < 0xff; export fn kill(pid: int, signal: int) int = syscall2(SYS_kill, pid: u64, signal: u64): int; export fn pipe2(pipefd: *[2]int, flags: int) int = syscall2(SYS_pipe2, pipefd: uintptr: u64, flags: u64): int; export def MAP_SHARED: uint = 0x01; export def MAP_PRIVATE: uint = 0x02; export def MAP_SHARED_VALIDATE: uint = 0x03; export def MAP_FIXED: uint = 0x10; export def MAP_ANON: uint = 0x20; export def MAP_NORESERVE: uint = 0x4000; export def MAP_GROWSDOWN: uint = 0x0100; export def MAP_DENYWRITE: uint = 0x0800; export def MAP_EXECUTABLE: uint = 0x1000; export def MAP_LOCKED: uint = 0x2000; export def MAP_POPULATE: uint = 0x8000; export def MAP_NONBLOCK: uint = 0x10000; export def MAP_STACK: uint = 0x20000; export def MAP_HUGETLB: uint = 0x40000; export def MAP_SYNC: uint = 0x80000; export def MAP_FIXED_NOREPLACE: uint = 0x100000; export def MAP_FILE: uint = 0; export def MAP_HUGE_SHIFT: uint = 26; export def MAP_HUGE_MASK: uint = 0x3f; export def MAP_HUGE_64KB: uint = 16 << 26; export def MAP_HUGE_512KB: uint = 19 << 26; export def MAP_HUGE_1MB: uint = 20 << 26; export def MAP_HUGE_2MB: uint = 21 << 26; export def MAP_HUGE_8MB: uint = 23 << 26; export def MAP_HUGE_16MB: uint = 24 << 26; export def MAP_HUGE_32MB: uint = 25 << 26; export def MAP_HUGE_256MB: uint = 28 << 26; export def MAP_HUGE_512MB: uint = 29 << 26; export def MAP_HUGE_1GB: uint = 30 << 26; export def MAP_HUGE_2GB: uint = 31 << 26; export def MAP_HUGE_16GB: uint = 34 << 26; export def PROT_NONE: uint = 0; export def PROT_READ: uint = 1; export def PROT_WRITE: uint = 2; export def PROT_EXEC: uint = 4; export def PROT_GROWSDOWN: uint = 0x01000000; export def PROT_GROWSUP: uint = 0x02000000; export fn mmap( addr: nullable *opaque, length: size, prot: uint, flags: uint, fd: int, offs: size ) *opaque = { let r = syscall6(SYS_mmap, addr: uintptr: u64, length: u64, prot: u64, flags: u64, fd: u64, offs: u64): u64; return if (r: int == -EPERM && addr == null && flags & MAP_ANON > 0 && flags & MAP_FIXED == 0) { yield -ENOMEM: uintptr: *opaque; // Fix up incorrect EPERM from kernel } else r: uintptr: *opaque; }; export fn munmap(addr: *opaque, length: size) int = syscall2(SYS_munmap, addr: uintptr: u64, length: u64): int; export fn mprotect(addr: *opaque, length: size, prot: uint) int = syscall3(SYS_mprotect, addr: uintptr: u64, length: u64, prot: u64): int; export def SIGABRT: int = 6; export def SIGCHLD: int = 17; harec-0.24.2/rt/+netbsd/000077500000000000000000000000001464473277600146645ustar00rootroot00000000000000harec-0.24.2/rt/+netbsd/errno.ha000066400000000000000000000056721464473277600163350ustar00rootroot00000000000000export def EPERM: int = 1; export def ENOENT: int = 2; export def ESRCH: int = 3; export def EINTR: int = 4; export def EIO: int = 5; export def ENXIO: int = 6; export def E2BIG: int = 7; export def ENOEXEC: int = 8; export def EBADF: int = 9; export def ECHILD: int = 10; export def EDEADLK: int = 11; export def ENOMEM: int = 12; export def EACCES: int = 13; export def EFAULT: int = 14; export def ENOTBLK: int = 15; export def EBUSY: int = 16; export def EEXIST: int = 17; export def EXDEV: int = 18; export def ENODEV: int = 19; export def ENOTDIR: int = 20; export def EISDIR: int = 21; export def EINVAL: int = 22; export def ENFILE: int = 23; export def EMFILE: int = 24; export def ENOTTY: int = 25; export def ETXTBSY: int = 26; export def EFBIG: int = 27; export def ENOSPC: int = 28; export def ESPIPE: int = 29; export def EROFS: int = 30; export def EMLINK: int = 31; export def EPIPE: int = 32; export def EDOM: int = 33; export def ERANGE: int = 34; export def EAGAIN: int = 35; export def EWOULDBLOCK: int = EAGAIN; export def EINPROGRESS: int = 36; export def EALREADY: int = 37; export def ENOTSOCK: int = 38; export def EDESTADDRREQ: int = 39; export def EMSGSIZE: int = 40; export def EPROTOTYPE: int = 41; export def ENOPROTOOPT: int = 42; export def EPROTONOSUPPORT: int = 43; export def ESOCKTNOSUPPORT: int = 44; export def EOPNOTSUPP: int = 45; export def EPFNOSUPPORT: int = 46; export def EAFNOSUPPORT: int = 47; export def EADDRINUSE: int = 48; export def EADDRNOTAVAIL: int = 49; export def ENETDOWN: int = 50; export def ENETUNREACH: int = 51; export def ENETRESET: int = 52; export def ECONNABORTED: int = 53; export def ECONNRESET: int = 54; export def ENOBUFS: int = 55; export def EISCONN: int = 56; export def ENOTCONN: int = 57; export def ESHUTDOWN: int = 58; export def ETOOMANYREFS: int = 59; export def ETIMEDOUT: int = 60; export def ECONNREFUSED: int = 61; export def ELOOP: int = 62; export def ENAMETOOLONG: int = 63; export def EHOSTDOWN: int = 64; export def EHOSTUNREACH: int = 65; export def ENOTEMPTY: int = 66; export def EPROCLIM: int = 67; export def EUSERS: int = 68; export def EDQUOT: int = 69; export def ESTALE: int = 70; export def EREMOTE: int = 71; export def EBADRPC: int = 72; export def ERPCMISMATCH: int = 73; export def EPROGUNAVAIL: int = 74; export def EPROGMISMATCH: int = 75; export def EPROCUNAVAIL: int = 76; export def ENOLCK: int = 77; export def ENOSYS: int = 78; export def EFTYPE: int = 79; export def EAUTH: int = 80; export def ENEEDAUTH: int = 81; export def EIDRM: int = 82; export def ENOMSG: int = 83; export def EOVERFLOW: int = 84; export def EILSEQ: int = 85; export def ENOTSUP: int = 86; export def ECANCELED: int = 87; export def EBADMSG: int = 88; export def ENODATA: int = 89; export def ENOSR: int = 90; export def ENOSTR: int = 91; export def ETIME: int = 92; export def ENOATTR: int = 93; export def EMULTIHOP: int = 94; export def ENOLINK: int = 95; export def EPROTO: int = 96; export def ELAST: int = 96; harec-0.24.2/rt/+netbsd/segmalloc.ha000066400000000000000000000004711464473277600171460ustar00rootroot00000000000000// Allocates a segment. fn segmalloc(n: size) nullable *opaque = { let p: *opaque = mmap(null, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); return if (p: uintptr: int == -ENOMEM) null else p; }; // Frees a segment allocated with segmalloc. fn segfree(p: *opaque, s: size) int = munmap(p, s); harec-0.24.2/rt/+netbsd/start+x86_64.s000066400000000000000000000004031464473277600171340ustar00rootroot00000000000000.section ".note.netbsd.ident", "a" .long 2f-1f .long 4f-3f .long 1 1: .asciz "NetBSD" 2: .p2align 2 3: .long 199905 4: .p2align 2 .text .global _start _start: xor %rbp, %rbp movq %rsp, %rdi andq $-16, %rsp call rt.start_ha harec-0.24.2/rt/+netbsd/start.ha000066400000000000000000000016311464473277600163340ustar00rootroot00000000000000@symbol("main") fn main() void; const @symbol("__init_array_start") init_start: [*]*fn() void; const @symbol("__init_array_end") init_end: [*]*fn() void; const @symbol("__fini_array_start") fini_start: [*]*fn() void; const @symbol("__fini_array_end") fini_end: [*]*fn() void; let argc: size = 0; let argv: *[*]*const u8 = null: *[*]*const u8; let envp: *[*]nullable *const u8 = null: *[*]nullable *const u8; export let exit_status = 0; export fn start_ha(iv: *[*]uintptr) never = { const ninit = (&init_end: uintptr - &init_start: uintptr): size / size(*fn() void); for (let i = 0z; i < ninit; i += 1) { init_start[i](); }; argc = iv[0]: size; argv = &iv[1]: *[*]*const u8; envp = &argv[argc + 1]: *[*]nullable *const u8; main(); const nfini = (&fini_end: uintptr - &fini_start: uintptr): size / size(*fn() void); for (let i = 0z; i < nfini; i += 1) { fini_start[i](); }; exit(exit_status); }; harec-0.24.2/rt/+netbsd/syscall+x86_64.s000066400000000000000000000017431464473277600174610ustar00rootroot00000000000000.section .text.rt.syscall0 .global rt.syscall0 rt.syscall0: movq %rdi, %rax syscall ret .section .text.rt.syscall1 .global rt.syscall1 rt.syscall1: movq %rdi, %rax movq %rsi, %rdi syscall ret .section .text.rt.syscall2 .global rt.syscall2 rt.syscall2: movq %rdi, %rax movq %rsi, %rdi movq %rdx, %rsi syscall ret .section .text.rt.syscall3 .global rt.syscall3 rt.syscall3: movq %rdi, %rax movq %rsi, %rdi movq %rdx, %rsi movq %rcx, %rdx syscall ret .section .text.rt.syscall4 .global rt.syscall4 rt.syscall4: movq %rdi, %rax movq %r8, %r10 movq %rsi, %rdi movq %rdx, %rsi movq %rcx, %rdx syscall ret .section .text.rt.syscall5 .global rt.syscall5 rt.syscall5: movq %rdi, %rax movq %r8, %r10 movq %rsi, %rdi movq %r9, %r8 movq %rdx, %rsi movq %rcx, %rdx syscall ret .section .text.rt.syscall6 .global rt.syscall6 rt.syscall6: movq %rdi, %rax movq %r8, %r10 movq %rsi, %rdi movq %r9, %r8 movq %rdx, %rsi movq 8(%rsp), %r9 movq %rcx, %rdx syscall ret harec-0.24.2/rt/+netbsd/syscallno.ha000066400000000000000000000412211464473277600172050ustar00rootroot00000000000000export def SYS_syscall: u64 = 0; export def SYS_exit: u64 = 1; export def SYS_fork: u64 = 2; export def SYS_read: u64 = 3; export def SYS_write: u64 = 4; export def SYS_open: u64 = 5; export def SYS_close: u64 = 6; export def SYS_compat_50_wait4: u64 = 7; export def SYS_compat_43_ocreat: u64 = 8; export def SYS_link: u64 = 9; export def SYS_unlink: u64 = 10; export def SYS_chdir: u64 = 12; export def SYS_fchdir: u64 = 13; export def SYS_compat_50_mknod: u64 = 14; export def SYS_chmod: u64 = 15; export def SYS_chown: u64 = 16; export def SYS_break: u64 = 17; export def SYS_compat_20_getfsstat: u64 = 18; export def SYS_compat_43_olseek: u64 = 19; export def SYS_getpid: u64 = 20; export def SYS_compat_40_mount: u64 = 21; export def SYS_unmount: u64 = 22; export def SYS_setuid: u64 = 23; export def SYS_getuid: u64 = 24; export def SYS_geteuid: u64 = 25; export def SYS_ptrace: u64 = 26; export def SYS_recvmsg: u64 = 27; export def SYS_sendmsg: u64 = 28; export def SYS_recvfrom: u64 = 29; export def SYS_accept: u64 = 30; export def SYS_getpeername: u64 = 31; export def SYS_getsockname: u64 = 32; export def SYS_access: u64 = 33; export def SYS_chflags: u64 = 34; export def SYS_fchflags: u64 = 35; export def SYS_sync: u64 = 36; export def SYS_kill: u64 = 37; export def SYS_compat_43_stat43: u64 = 38; export def SYS_getppid: u64 = 39; export def SYS_compat_43_lstat43: u64 = 40; export def SYS_dup: u64 = 41; export def SYS_pipe: u64 = 42; export def SYS_getegid: u64 = 43; export def SYS_profil: u64 = 44; export def SYS_ktrace: u64 = 45; export def SYS_compat_13_sigaction13: u64 = 46; export def SYS_getgid: u64 = 47; export def SYS_compat_13_sigprocmask13: u64 = 48; export def SYS___getlogin: u64 = 49; export def SYS___setlogin: u64 = 50; export def SYS_acct: u64 = 51; export def SYS_compat_13_sigpending13: u64 = 52; export def SYS_compat_13_sigaltstack13: u64 = 53; export def SYS_ioctl: u64 = 54; export def SYS_compat_12_oreboot: u64 = 55; export def SYS_revoke: u64 = 56; export def SYS_symlink: u64 = 57; export def SYS_readlink: u64 = 58; export def SYS_execve: u64 = 59; export def SYS_umask: u64 = 60; export def SYS_chroot: u64 = 61; export def SYS_compat_43_fstat43: u64 = 62; export def SYS_compat_43_ogetkerninfo: u64 = 63; export def SYS_compat_43_ogetpagesize: u64 = 64; export def SYS_compat_12_msync: u64 = 65; export def SYS_vfork: u64 = 66; export def SYS_compat_43_ommap: u64 = 71; export def SYS_vadvise: u64 = 72; export def SYS_munmap: u64 = 73; export def SYS_mprotect: u64 = 74; export def SYS_madvise: u64 = 75; export def SYS_mincore: u64 = 78; export def SYS_getgroups: u64 = 79; export def SYS_setgroups: u64 = 80; export def SYS_getpgrp: u64 = 81; export def SYS_setpgid: u64 = 82; export def SYS_compat_50_setitimer: u64 = 83; export def SYS_compat_43_owait: u64 = 84; export def SYS_compat_12_oswapon: u64 = 85; export def SYS_compat_50_getitimer: u64 = 86; export def SYS_compat_43_ogethostname: u64 = 87; export def SYS_compat_43_osethostname: u64 = 88; export def SYS_compat_43_ogetdtablesize: u64 = 89; export def SYS_dup2: u64 = 90; export def SYS_fcntl: u64 = 92; export def SYS_compat_50_select: u64 = 93; export def SYS_fsync: u64 = 95; export def SYS_setpriority: u64 = 96; export def SYS_compat_30_socket: u64 = 97; export def SYS_connect: u64 = 98; export def SYS_compat_43_oaccept: u64 = 99; export def SYS_getpriority: u64 = 100; export def SYS_compat_43_osend: u64 = 101; export def SYS_compat_43_orecv: u64 = 102; export def SYS_compat_13_sigreturn13: u64 = 103; export def SYS_bind: u64 = 104; export def SYS_setsockopt: u64 = 105; export def SYS_listen: u64 = 106; export def SYS_compat_43_osigvec: u64 = 108; export def SYS_compat_43_osigblock: u64 = 109; export def SYS_compat_43_osigsetmask: u64 = 110; export def SYS_compat_13_sigsuspend13: u64 = 111; export def SYS_compat_43_osigstack: u64 = 112; export def SYS_compat_43_orecvmsg: u64 = 113; export def SYS_compat_43_osendmsg: u64 = 114; export def SYS_compat_50_gettimeofday: u64 = 116; export def SYS_compat_50_getrusage: u64 = 117; export def SYS_getsockopt: u64 = 118; export def SYS_readv: u64 = 120; export def SYS_writev: u64 = 121; export def SYS_compat_50_settimeofday: u64 = 122; export def SYS_fchown: u64 = 123; export def SYS_fchmod: u64 = 124; export def SYS_compat_43_orecvfrom: u64 = 125; export def SYS_setreuid: u64 = 126; export def SYS_setregid: u64 = 127; export def SYS_rename: u64 = 128; export def SYS_compat_43_otruncate: u64 = 129; export def SYS_compat_43_oftruncate: u64 = 130; export def SYS_flock: u64 = 131; export def SYS_mkfifo: u64 = 132; export def SYS_sendto: u64 = 133; export def SYS_shutdown: u64 = 134; export def SYS_socketpair: u64 = 135; export def SYS_mkdir: u64 = 136; export def SYS_rmdir: u64 = 137; export def SYS_compat_50_utimes: u64 = 138; export def SYS_compat_50_adjtime: u64 = 140; export def SYS_compat_43_ogetpeername: u64 = 141; export def SYS_compat_43_ogethostid: u64 = 142; export def SYS_compat_43_osethostid: u64 = 143; export def SYS_compat_43_ogetrlimit: u64 = 144; export def SYS_compat_43_osetrlimit: u64 = 145; export def SYS_compat_43_okillpg: u64 = 146; export def SYS_setsid: u64 = 147; export def SYS_compat_50_quotactl: u64 = 148; export def SYS_compat_43_oquota: u64 = 149; export def SYS_compat_43_ogetsockname: u64 = 150; export def SYS_nfssvc: u64 = 155; export def SYS_compat_43_ogetdirentries: u64 = 156; export def SYS_compat_20_statfs: u64 = 157; export def SYS_compat_20_fstatfs: u64 = 158; export def SYS_compat_30_getfh: u64 = 161; export def SYS_compat_09_ogetdomainname: u64 = 162; export def SYS_compat_09_osetdomainname: u64 = 163; export def SYS_compat_09_ouname: u64 = 164; export def SYS_sysarch: u64 = 165; export def SYS_compat_10_osemsys: u64 = 169; export def SYS_compat_10_omsgsys: u64 = 170; export def SYS_compat_10_oshmsys: u64 = 171; export def SYS_pread: u64 = 173; export def SYS_pwrite: u64 = 174; export def SYS_compat_30_ntp_gettime: u64 = 175; export def SYS_ntp_adjtime: u64 = 176; export def SYS_setgid: u64 = 181; export def SYS_setegid: u64 = 182; export def SYS_seteuid: u64 = 183; export def SYS_lfs_bmapv: u64 = 184; export def SYS_lfs_markv: u64 = 185; export def SYS_lfs_segclean: u64 = 186; export def SYS_compat_50_lfs_segwait: u64 = 187; export def SYS_compat_12_stat12: u64 = 188; export def SYS_compat_12_fstat12: u64 = 189; export def SYS_compat_12_lstat12: u64 = 190; export def SYS_pathconf: u64 = 191; export def SYS_fpathconf: u64 = 192; export def SYS_getsockopt2: u64 = 193; export def SYS_getrlimit: u64 = 194; export def SYS_setrlimit: u64 = 195; export def SYS_compat_12_getdirentries: u64 = 196; export def SYS_mmap: u64 = 197; export def SYS___syscall: u64 = 198; export def SYS_lseek: u64 = 199; export def SYS_truncate: u64 = 200; export def SYS_ftruncate: u64 = 201; export def SYS___sysctl: u64 = 202; export def SYS_mlock: u64 = 203; export def SYS_munlock: u64 = 204; export def SYS_undelete: u64 = 205; export def SYS_compat_50_futimes: u64 = 206; export def SYS_getpgid: u64 = 207; export def SYS_reboot: u64 = 208; export def SYS_poll: u64 = 209; export def SYS_afssys: u64 = 210; export def SYS_compat_14___semctl: u64 = 220; export def SYS_semget: u64 = 221; export def SYS_semop: u64 = 222; export def SYS_semconfig: u64 = 223; export def SYS_compat_14_msgctl: u64 = 224; export def SYS_msgget: u64 = 225; export def SYS_msgsnd: u64 = 226; export def SYS_msgrcv: u64 = 227; export def SYS_shmat: u64 = 228; export def SYS_compat_14_shmctl: u64 = 229; export def SYS_shmdt: u64 = 230; export def SYS_shmget: u64 = 231; export def SYS_compat_50_clock_gettime: u64 = 232; export def SYS_compat_50_clock_settime: u64 = 233; export def SYS_compat_50_clock_getres: u64 = 234; export def SYS_timer_create: u64 = 235; export def SYS_timer_delete: u64 = 236; export def SYS_compat_50_timer_settime: u64 = 237; export def SYS_compat_50_timer_gettime: u64 = 238; export def SYS_timer_getoverrun: u64 = 239; export def SYS_compat_50_nanosleep: u64 = 240; export def SYS_fdatasync: u64 = 241; export def SYS_mlockall: u64 = 242; export def SYS_munlockall: u64 = 243; export def SYS_compat_50___sigtimedwait: u64 = 244; export def SYS_sigqueueinfo: u64 = 245; export def SYS_modctl: u64 = 246; export def SYS__ksem_init: u64 = 247; export def SYS__ksem_open: u64 = 248; export def SYS__ksem_unlink: u64 = 249; export def SYS__ksem_close: u64 = 250; export def SYS__ksem_post: u64 = 251; export def SYS__ksem_wait: u64 = 252; export def SYS__ksem_trywait: u64 = 253; export def SYS__ksem_getvalue: u64 = 254; export def SYS__ksem_destroy: u64 = 255; export def SYS__ksem_timedwait: u64 = 256; export def SYS_mq_open: u64 = 257; export def SYS_mq_close: u64 = 258; export def SYS_mq_unlink: u64 = 259; export def SYS_mq_getattr: u64 = 260; export def SYS_mq_setattr: u64 = 261; export def SYS_mq_notify: u64 = 262; export def SYS_mq_send: u64 = 263; export def SYS_mq_receive: u64 = 264; export def SYS_compat_50_mq_timedsend: u64 = 265; export def SYS_compat_50_mq_timedreceive: u64 = 266; export def SYS___posix_rename: u64 = 270; export def SYS_swapctl: u64 = 271; export def SYS_compat_30_getdents: u64 = 272; export def SYS_minherit: u64 = 273; export def SYS_lchmod: u64 = 274; export def SYS_lchown: u64 = 275; export def SYS_compat_50_lutimes: u64 = 276; export def SYS___msync13: u64 = 277; export def SYS_compat_30___stat13: u64 = 278; export def SYS_compat_30___fstat13: u64 = 279; export def SYS_compat_30___lstat13: u64 = 280; export def SYS___sigaltstack14: u64 = 281; export def SYS___vfork14: u64 = 282; export def SYS___posix_chown: u64 = 283; export def SYS___posix_fchown: u64 = 284; export def SYS___posix_lchown: u64 = 285; export def SYS_getsid: u64 = 286; export def SYS___clone: u64 = 287; export def SYS_fktrace: u64 = 288; export def SYS_preadv: u64 = 289; export def SYS_pwritev: u64 = 290; export def SYS_compat_16___sigaction14: u64 = 291; export def SYS___sigpending14: u64 = 292; export def SYS___sigprocmask14: u64 = 293; export def SYS___sigsuspend14: u64 = 294; export def SYS_compat_16___sigreturn14: u64 = 295; export def SYS___getcwd: u64 = 296; export def SYS_fchroot: u64 = 297; export def SYS_compat_30_fhopen: u64 = 298; export def SYS_compat_30_fhstat: u64 = 299; export def SYS_compat_20_fhstatfs: u64 = 300; export def SYS_compat_50_____semctl13: u64 = 301; export def SYS_compat_50___msgctl13: u64 = 302; export def SYS_compat_50___shmctl13: u64 = 303; export def SYS_lchflags: u64 = 304; export def SYS_issetugid: u64 = 305; export def SYS_utrace: u64 = 306; export def SYS_getcontext: u64 = 307; export def SYS_setcontext: u64 = 308; export def SYS__lwp_create: u64 = 309; export def SYS__lwp_exit: u64 = 310; export def SYS__lwp_self: u64 = 311; export def SYS__lwp_wait: u64 = 312; export def SYS__lwp_suspend: u64 = 313; export def SYS__lwp_continue: u64 = 314; export def SYS__lwp_wakeup: u64 = 315; export def SYS__lwp_getprivate: u64 = 316; export def SYS__lwp_setprivate: u64 = 317; export def SYS__lwp_kill: u64 = 318; export def SYS__lwp_detach: u64 = 319; export def SYS_compat_50__lwp_park: u64 = 320; export def SYS__lwp_unpark: u64 = 321; export def SYS__lwp_unpark_all: u64 = 322; export def SYS__lwp_setname: u64 = 323; export def SYS__lwp_getname: u64 = 324; export def SYS__lwp_ctl: u64 = 325; export def SYS_compat_60_sa_register: u64 = 330; export def SYS_compat_60_sa_stacks: u64 = 331; export def SYS_compat_60_sa_enable: u64 = 332; export def SYS_compat_60_sa_setconcurrency: u64 = 333; export def SYS_compat_60_sa_yield: u64 = 334; export def SYS_compat_60_sa_preempt: u64 = 335; export def SYS___sigaction_sigtramp: u64 = 340; export def SYS_rasctl: u64 = 343; export def SYS_kqueue: u64 = 344; export def SYS_compat_50_kevent: u64 = 345; export def SYS__sched_setparam: u64 = 346; export def SYS__sched_getparam: u64 = 347; export def SYS__sched_setaffinity: u64 = 348; export def SYS__sched_getaffinity: u64 = 349; export def SYS_sched_yield: u64 = 350; export def SYS__sched_protect: u64 = 351; export def SYS_fsync_range: u64 = 354; export def SYS_uuidgen: u64 = 355; export def SYS_getvfsstat: u64 = 356; export def SYS_statvfs1: u64 = 357; export def SYS_fstatvfs1: u64 = 358; export def SYS_compat_30_fhstatvfs1: u64 = 359; export def SYS_extattrctl: u64 = 360; export def SYS_extattr_set_file: u64 = 361; export def SYS_extattr_get_file: u64 = 362; export def SYS_extattr_delete_file: u64 = 363; export def SYS_extattr_set_fd: u64 = 364; export def SYS_extattr_get_fd: u64 = 365; export def SYS_extattr_delete_fd: u64 = 366; export def SYS_extattr_set_link: u64 = 367; export def SYS_extattr_get_link: u64 = 368; export def SYS_extattr_delete_link: u64 = 369; export def SYS_extattr_list_fd: u64 = 370; export def SYS_extattr_list_file: u64 = 371; export def SYS_extattr_list_link: u64 = 372; export def SYS_compat_50_pselect: u64 = 373; export def SYS_compat_50_pollts: u64 = 374; export def SYS_setxattr: u64 = 375; export def SYS_lsetxattr: u64 = 376; export def SYS_fsetxattr: u64 = 377; export def SYS_getxattr: u64 = 378; export def SYS_lgetxattr: u64 = 379; export def SYS_fgetxattr: u64 = 380; export def SYS_listxattr: u64 = 381; export def SYS_llistxattr: u64 = 382; export def SYS_flistxattr: u64 = 383; export def SYS_removexattr: u64 = 384; export def SYS_lremovexattr: u64 = 385; export def SYS_fremovexattr: u64 = 386; export def SYS_compat_50___stat30: u64 = 387; export def SYS_compat_50___fstat30: u64 = 388; export def SYS_compat_50___lstat30: u64 = 389; export def SYS___getdents30: u64 = 390; export def SYS_compat_30___fhstat30: u64 = 392; export def SYS_compat_50___ntp_gettime30: u64 = 393; export def SYS___socket30: u64 = 394; export def SYS___getfh30: u64 = 395; export def SYS___fhopen40: u64 = 396; export def SYS___fhstatvfs140: u64 = 397; export def SYS_compat_50___fhstat40: u64 = 398; export def SYS_aio_cancel: u64 = 399; export def SYS_aio_error: u64 = 400; export def SYS_aio_fsync: u64 = 401; export def SYS_aio_read: u64 = 402; export def SYS_aio_return: u64 = 403; export def SYS_compat_50_aio_suspend: u64 = 404; export def SYS_aio_write: u64 = 405; export def SYS_lio_listio: u64 = 406; export def SYS___mount50: u64 = 410; export def SYS_mremap: u64 = 411; export def SYS_pset_create: u64 = 412; export def SYS_pset_destroy: u64 = 413; export def SYS_pset_assign: u64 = 414; export def SYS__pset_bind: u64 = 415; export def SYS___posix_fadvise50: u64 = 416; export def SYS___select50: u64 = 417; export def SYS___gettimeofday50: u64 = 418; export def SYS___settimeofday50: u64 = 419; export def SYS___utimes50: u64 = 420; export def SYS___adjtime50: u64 = 421; export def SYS___lfs_segwait50: u64 = 422; export def SYS___futimes50: u64 = 423; export def SYS___lutimes50: u64 = 424; export def SYS___setitimer50: u64 = 425; export def SYS___getitimer50: u64 = 426; export def SYS___clock_gettime50: u64 = 427; export def SYS___clock_settime50: u64 = 428; export def SYS___clock_getres50: u64 = 429; export def SYS___nanosleep50: u64 = 430; export def SYS_____sigtimedwait50: u64 = 431; export def SYS___mq_timedsend50: u64 = 432; export def SYS___mq_timedreceive50: u64 = 433; export def SYS_compat_60__lwp_park: u64 = 434; export def SYS___kevent50: u64 = 435; export def SYS___pselect50: u64 = 436; export def SYS___pollts50: u64 = 437; export def SYS___aio_suspend50: u64 = 438; export def SYS___stat50: u64 = 439; export def SYS___fstat50: u64 = 440; export def SYS___lstat50: u64 = 441; export def SYS_____semctl50: u64 = 442; export def SYS___shmctl50: u64 = 443; export def SYS___msgctl50: u64 = 444; export def SYS___getrusage50: u64 = 445; export def SYS___timer_settime50: u64 = 446; export def SYS___timer_gettime50: u64 = 447; export def SYS___ntp_gettime50: u64 = 448; export def SYS___wait450: u64 = 449; export def SYS___mknod50: u64 = 450; export def SYS___fhstat50: u64 = 451; export def SYS_pipe2: u64 = 453; export def SYS_dup3: u64 = 454; export def SYS_kqueue1: u64 = 455; export def SYS_paccept: u64 = 456; export def SYS_linkat: u64 = 457; export def SYS_renameat: u64 = 458; export def SYS_mkfifoat: u64 = 459; export def SYS_mknodat: u64 = 460; export def SYS_mkdirat: u64 = 461; export def SYS_faccessat: u64 = 462; export def SYS_fchmodat: u64 = 463; export def SYS_fchownat: u64 = 464; export def SYS_fexecve: u64 = 465; export def SYS_fstatat: u64 = 466; export def SYS_utimensat: u64 = 467; export def SYS_openat: u64 = 468; export def SYS_readlinkat: u64 = 469; export def SYS_symlinkat: u64 = 470; export def SYS_unlinkat: u64 = 471; export def SYS_futimens: u64 = 472; export def SYS___quotactl: u64 = 473; export def SYS_posix_spawn: u64 = 474; export def SYS_recvmmsg: u64 = 475; export def SYS_sendmmsg: u64 = 476; export def SYS_clock_nanosleep: u64 = 477; export def SYS____lwp_park60: u64 = 478; export def SYS_posix_fallocate: u64 = 479; export def SYS_fdiscard: u64 = 480; export def SYS_wait6: u64 = 481; export def SYS_clock_getcpuclockid2: u64 = 482; export def SYS_MAXSYSCALL: u64 = 483; export def SYS_NSYSENT: u64 = 512; harec-0.24.2/rt/+netbsd/syscalls.ha000066400000000000000000000047731464473277600170460ustar00rootroot00000000000000fn syscall0(u64) u64; fn syscall1(u64, u64) u64; fn syscall2(u64, u64, u64) u64; fn syscall3(u64, u64, u64, u64) u64; fn syscall4(u64, u64, u64, u64, u64) u64; fn syscall5(u64, u64, u64, u64, u64, u64) u64; fn syscall6(u64, u64, u64, u64, u64, u64, u64) u64; export fn write(fd: int, buf: *const opaque, count: size) size = syscall3(SYS_write, fd: u64, buf: uintptr: u64, count: u64): size; export fn close(fd: int) int = syscall1(SYS_close, fd: u64): int; export fn dup2(old: int, new: int) int = syscall2(SYS_dup2, old: u64, new: u64): int; export fn getpid() int = syscall0(SYS_getpid): int; export def EXIT_SUCCESS: int = 0; export fn exit(status: int) never = { syscall1(SYS_exit, status: u64); abort(); }; export fn fork() int = syscall0(SYS_fork): int; export fn execve( path: *const u8, argv: *[*]nullable *const u8, envp: *[*]nullable *const u8, ) int = syscall3(SYS_execve, path: uintptr: u64, argv: uintptr: u64, envp: uintptr: u64): int; export fn wait4(pid: int, status: *int, options: int, rusage: nullable *opaque) void = { syscall4(SYS_compat_50_wait4, pid: u64, status: uintptr: u64, options: u64, rusage: uintptr: u64); }; export fn wifexited(status: int) bool = wtermsig(status) == 0; export fn wexitstatus(status: int) int = (status & 0xff00) >> 8; export fn wtermsig(status: int) int = status & 0x7f; export fn wifsignaled(status: int) bool = wtermsig(status) != 0o177 && wtermsig(status) != 0 && status != 0x13; export fn kill(pid: int, signal: int) int = syscall2(SYS_kill, pid: u64, signal: u64): int; export fn pipe2(pipefd: *[2]int, flags: int) int = syscall2(SYS_pipe2, pipefd: uintptr: u64, flags: u64): int; export def MAP_SHARED: uint = 0x0001; export def MAP_PRIVATE: uint = 0x0002; export def MAP_FIXED: uint = 0x0010; export def __MAP_NOREPLACE: uint = 0x0800; export def MAP_ANON: uint = 0x1000; export def MAP_ANONYMOUS: uint = MAP_ANON; export def __MAP_NOFAULT: uint = 0x2000; export def MAP_STACK: uint = 0x4000; export def MAP_CONCEAL: uint = 0x8000; def PROT_NONE: uint = 0x00; def PROT_READ: uint = 0x01; def PROT_WRITE: uint = 0x02; def PROT_EXEC: uint = 0x04; export fn mmap( addr: nullable *opaque, length: size, prot: uint, flags: uint, fd: int, offs: size ) *opaque = { return syscall6(SYS_mmap, addr: uintptr: u64, length: u64, prot: u64, flags: u64, fd: u64, offs: u64): uintptr: *opaque; }; export fn munmap(addr: *opaque, length: size) int = syscall2(SYS_munmap, addr: uintptr: u64, length: u64): int; export def SIGABRT: int = 6; export def SIGCHLD: int = 20; harec-0.24.2/rt/+openbsd/000077500000000000000000000000001464473277600150375ustar00rootroot00000000000000harec-0.24.2/rt/+openbsd/errno.ha000066400000000000000000000056571464473277600165130ustar00rootroot00000000000000export def EPERM: int = 1; export def ENOENT: int = 2; export def ESRCH: int = 3; export def EINTR: int = 4; export def EIO: int = 5; export def ENXIO: int = 6; export def E2BIG: int = 7; export def ENOEXEC: int = 8; export def EBADF: int = 9; export def ECHILD: int = 10; export def EDEADLK: int = 11; export def ENOMEM: int = 12; export def EACCES: int = 13; export def EFAULT: int = 14; export def ENOTBLK: int = 15; export def EBUSY: int = 16; export def EEXIST: int = 17; export def EXDEV: int = 18; export def ENODEV: int = 19; export def ENOTDIR: int = 20; export def EISDIR: int = 21; export def EINVAL: int = 22; export def ENFILE: int = 23; export def EMFILE: int = 24; export def ENOTTY: int = 25; export def ETXTBSY: int = 26; export def EFBIG: int = 27; export def ENOSPC: int = 28; export def ESPIPE: int = 29; export def EROFS: int = 30; export def EMLINK: int = 31; export def EPIPE: int = 32; export def EDOM: int = 33; export def ERANGE: int = 34; export def EAGAIN: int = 35; export def EWOULDBLOCK: int = EAGAIN; export def EINPROGRESS: int = 36; export def EALREADY: int = 37; export def ENOTSOCK: int = 38; export def EDESTADDRREQ: int = 39; export def EMSGSIZE: int = 40; export def EPROTOTYPE: int = 41; export def ENOPROTOOPT: int = 42; export def EPROTONOSUPPORT: int = 43; export def ESOCKTNOSUPPORT: int = 44; export def EOPNOTSUPP: int = 45; export def EPFNOSUPPORT: int = 46; export def EAFNOSUPPORT: int = 47; export def EADDRINUSE: int = 48; export def EADDRNOTAVAIL: int = 49; export def ENETDOWN: int = 50; export def ENETUNREACH: int = 51; export def ENETRESET: int = 52; export def ECONNABORTED: int = 53; export def ECONNRESET: int = 54; export def ENOBUFS: int = 55; export def EISCONN: int = 56; export def ENOTCONN: int = 57; export def ESHUTDOWN: int = 58; export def ETOOMANYREFS: int = 59; export def ETIMEDOUT: int = 60; export def ECONNREFUSED: int = 61; export def ELOOP: int = 62; export def ENAMETOOLONG: int = 63; export def EHOSTDOWN: int = 64; export def EHOSTUNREACH: int = 65; export def ENOTEMPTY: int = 66; export def EPROCLIM: int = 67; export def EUSERS: int = 68; export def EDQUOT: int = 69; export def ESTALE: int = 70; export def EREMOTE: int = 71; export def EBADRPC: int = 72; export def ERPCMISMATCH: int = 73; export def EPROGUNAVAIL: int = 74; export def EPROGMISMATCH: int = 75; export def EPROCUNAVAIL: int = 76; export def ENOLCK: int = 77; export def ENOSYS: int = 78; export def EFTYPE: int = 79; export def EAUTH: int = 80; export def ENEEDAUTH: int = 81; export def EIPSEC: int = 82; export def ENOATTR: int = 83; export def EILSEQ: int = 84; export def ENOMEDIUM: int = 85; export def EMEDIUMTYPE: int = 86; export def EOVERFLOW: int = 87; export def ECANCELED: int = 88; export def EIDRM: int = 89; export def ENOMSG: int = 90; export def ENOTSUP: int = 91; export def EBADMSG: int = 92; export def ENOTRECOVERABLE: int = 93; export def EOWNERDEAD: int = 94; export def EPROTO: int = 95; export def ELAST: int = 95; harec-0.24.2/rt/+openbsd/platformstart.s000066400000000000000000000001231464473277600201210ustar00rootroot00000000000000.section ".preinit_array" .balign 8 .init.initfunc.0: .quad preinit_hare+0 harec-0.24.2/rt/+openbsd/start.ha000066400000000000000000000016501464473277600165100ustar00rootroot00000000000000// SPDX-License-Identifier: MPL-2.0 // (c) Hare authors let argc: size = 0; let argv: *[*]*const u8 = null: *[*]*const u8; let envp: *[*]nullable *const u8 = null: *[*]nullable *const u8; export let exit_status = 0; // The real main function. @symbol(".main") fn main() void; // The setup of envp and args is done here. This is called by crt0 before // normal init functions are called. export @symbol("preinit_hare") fn preinit_hare( c_argc: int, c_argv: *[*]*const u8, c_envp: *[*]nullable *const u8 ) void = { argc = c_argc: size; argv = c_argv; envp = c_envp; }; // The purpose of this "fake" main function is to make sure we exit with the // correct exit code in the case that rt::exit() is not called from within the // program. The intilization and finilization functions are not run from here, // they are ran by crt0. export @symbol("main") fn _main() void = { main(); exit(exit_status); }; harec-0.24.2/rt/+openbsd/syscalls.ha000066400000000000000000000042041464473277600172060ustar00rootroot00000000000000export @symbol("write") fn write(fd: int, buf: *const opaque, count: size) int; export @symbol("close") fn close(fd: int) int; export @symbol("dup2") fn dup2(old: int, new: int) int; export @symbol("getpid") fn getpid() int; export def EXIT_SUCCESS: int = 0; export @symbol("exit") fn exit(status: int) never; export @symbol("fork") fn fork() int; export @symbol("execve") fn execve(path: *const u8, argv: *[*]nullable *const u8, envp: *[*]nullable *const u8) int; export type time_t = i64; export type suseconds_t = i64; export type timeval = struct { tv_sec: time_t, tv_usec: suseconds_t, }; export type rusage = struct { ru_utime: timeval, ru_stime: timeval, ru_maxrss: i64, ru_ixrss: i64, ru_idrss: i64, ru_isrss: i64, ru_minflt: i64, ru_majflt: i64, ru_nswap: i64, ru_inblock: i64, ru_oublock: i64, ru_msgsnd: i64, ru_msgrcv: i64, ru_nsignals: i64, ru_nvcsw: i64, ru_nivcsw: i64, }; export @symbol("wait4") fn wait4( wpid: int, status: *int, options: int, rusage: nullable *rusage ) int; export fn wifexited(status: int) bool = wtermsig(status) == 0; export fn wexitstatus(status: int) int = (status & 0xff00) >> 8; export fn wtermsig(status: int) int = status & 0x7f; export fn wifsignaled(status: int) bool = wtermsig(status) != 0o177 && wtermsig(status) != 0 && status != 0x13; export @symbol("kill") fn kill(pid: int, signal: int) int; export @symbol("pipe2") fn pipe2(pipefd: *[2]int, flags: int) int; export def MAP_SHARED: uint = 0x0001; export def MAP_PRIVATE: uint = 0x0002; export def MAP_FIXED: uint = 0x0010; export def __MAP_NOREPLACE: uint = 0x0800; export def MAP_ANON: uint = 0x1000; export def MAP_ANONYMOUS: uint = MAP_ANON; export def __MAP_NOFAULT: uint = 0x2000; export def MAP_STACK: uint = 0x4000; export def MAP_CONCEAL: uint = 0x8000; def PROT_NONE: uint = 0x00; def PROT_READ: uint = 0x01; def PROT_WRITE: uint = 0x02; def PROT_EXEC: uint = 0x04; export @symbol("mmap") fn mmap( addr: nullable *opaque, length: size, prot: int, flags: int, fd: int, offs: i64 ) *opaque; export @symbol("munmap") fn munmap(addr: *opaque, length: size) int; export def SIGABRT: int = 6; export def SIGCHLD: int = 20; harec-0.24.2/rt/COPYING000066400000000000000000000405261464473277600143740ustar00rootroot00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. harec-0.24.2/rt/README000066400000000000000000000001401464473277600142050ustar00rootroot00000000000000This is a basic Hare runtime. It is capable of running the harec test suite, but not much else. harec-0.24.2/rt/abort.ha000066400000000000000000000026301464473277600147540ustar00rootroot00000000000000export @symbol("rt.abort") fn _abort( path: *str, line: u64, col: u64, msg: str, ) void = { const prefix = "Abort: "; const sep = ":"; const sepspace = ": "; write(2, constchar(prefix), len(prefix)); write(2, constchar(*path), len(path)); write(2, constchar(sep), len(sep)); let (line, z) = u64tos(line); write(2, line, z); write(2, constchar(sep), len(sep)); let (col, z) = u64tos(col); write(2, col, z); write(2, constchar(sepspace), len(sepspace)); write(2, constchar(msg), len(msg)); write(2, constchar("\n"), 1); kill(getpid(), SIGABRT); }; // See harec:include/gen.h const reasons: [_]str = [ "slice or array access out of bounds", // 0 "type assertion failed", // 1 "out of memory", // 2 "static append exceeds slice capacity", // 3 "unreachable code", // 4 "slice allocation capacity smaller than initializer", // 5 "assertion failed", // 6 "error occurred", // 7 ]; export fn abort_fixed(path: *str, line: u64, col: u64, i: u64) void = { _abort(path, line, col, reasons[i]); }; fn u64tos(u: u64) (*const u8, size) = { static let buf: [20]u8 = [0...]; // len("18446744073709551615") let sl = buf[..0]; if (u == 0) { static append(sl, '0'); }; for (u > 0) { static append(sl, (u % 10): u8 + '0'); u /= 10; }; for (let s = 0z, e = len(sl) - 1; s < e) { let tmp = sl[s]; sl[s] = sl[e]; sl[e] = tmp; s += 1; e -= 1; }; return (sl: *[*]u8: *const u8, len(sl)); }; harec-0.24.2/rt/compile.ha000066400000000000000000000046561464473277600153070ustar00rootroot00000000000000export type status = enum { SUCCESS, USER, LEX, PARSE, CHECK, ABNORMAL = 255, }; fn strstatus(status: int) str = { switch (status) { case status::SUCCESS => return "success (0)"; case status::USER => return "user (1)"; case status::LEX => return "lex (2)"; case status::PARSE => return "parse (3)"; case status::CHECK => return "check (4)"; case status::ABNORMAL => return "abnormal (255)"; case => return itos(status: int); }; }; export type error = !void; // Runs the Hare compiler and returns the exit status. export fn compile( expected: (status | void), src: str, flags: str... ) (void | error) = { let wstatus = 0; let pipefd = [-1, -1]; assert(pipe2(&pipefd, 0) == 0); const child = fork(); if (child == 0) { close(pipefd[1]); dup2(pipefd[0], 0); close(1); close(2); let argv: []nullable *const u8 = []; defer for (let i = 0z; i < len(argv); i += 1) { free(argv[i]); }; // FIXME use $BINOUT variable append(argv, alloc_constchar("./.bin/harec")); for (let i = 0z; i < len(flags); i += 1) { append(argv, alloc_constchar(flags[i])); }; append(argv, alloc_constchar("-o/dev/null")); append(argv, alloc_constchar("-")); append(argv, null); execve(constchar("./.bin/harec\0"), *(&argv: **[*]nullable *const u8), envp); abort(); } else { assert(child != -1, "fork(2) failed"); close(pipefd[0]); const buf = constchar(src): *const [*]u8; for (let n = 0z; n < len(src)) { let m = write(pipefd[1], &buf[n], len(src) - n): size; assert(m > 0, "write(2) failed"); n += m; }; close(pipefd[1]); wait4(child, &wstatus, 0, null); }; if (!wifexited(wstatus)) { assert(wifsignaled(wstatus)); let s = "signaled "; write(2, constchar(s), len(s)); s = itos(wtermsig(wstatus)); write(2, constchar(s), len(s)); write(2, constchar("\n"), 1); return error; }; match (expected) { case void => const status = wexitstatus(wstatus); if (status == status::SUCCESS) { let s = "expected any failure, got success\n"; write(2, constchar(s), len(s)); return error; }; case let expected: status => const status = wexitstatus(wstatus); if (status != expected) { let s = "expected "; write(2, constchar(s), len(s)); s = strstatus(expected); write(2, constchar(s), len(s)); s = ", got "; write(2, constchar(s), len(s)); s = strstatus(status); write(2, constchar(s), len(s)); write(2, constchar("\n"), 1); return error; }; }; }; harec-0.24.2/rt/cstrings.ha000066400000000000000000000005701464473277600155020ustar00rootroot00000000000000type string = struct { data: nullable *[*]u8, length: size, capacity: size, }; export fn toutf8(s: str) []u8 = *(&s: *[]u8); fn constchar(s: str) *const u8 = { let s = &s: *string; return s.data: *const u8; }; fn alloc_constchar(s: str) *const u8 = { let c: []u8 = alloc([], len(s) + 1); append(c, *(&s: *[]u8)...); append(c, 0); return constchar(*(&c: *str)); }; harec-0.24.2/rt/ensure.ha000066400000000000000000000014011464473277600151410ustar00rootroot00000000000000export type slice = struct { data: nullable *opaque, length: size, capacity: size, }; export fn ensure(s: *slice, membsz: size) void = { let cap = s.capacity; if (cap >= s.length) { return; }; for (cap < s.length) { assert(cap >= s.capacity, "slice out of memory (overflow)"); if (cap == 0) { cap = s.length; } else { cap *= 2; }; }; s.capacity = cap; const data = realloc(s.data, s.capacity * membsz); assert(data != null || s.capacity * membsz == 0); s.data = data; }; export fn unensure(s: *slice, membsz: size) void = { let cap = s.capacity; for (cap > s.length) { cap /= 2; }; cap *= 2; s.capacity = cap; const data = realloc(s.data, s.capacity * membsz); assert(data != null || s.capacity * membsz == 0); s.data = data; }; harec-0.24.2/rt/hare+netbsd.sc000066400000000000000000000014421464473277600160540ustar00rootroot00000000000000PHDRS { headers PT_PHDR PHDRS; text PT_LOAD FILEHDR PHDRS; data PT_LOAD; note PT_NOTE; } ENTRY(_start); SECTIONS { . = 0x8000000; .text : { KEEP (*(.text)) *(.text.*) } :text . = 0x80000000; .data : { KEEP (*(.data)) *(.data.*) } :data .init_array : { PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(.init_array)) PROVIDE_HIDDEN (__init_array_end = .); } :data .fini_array : { PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(.fini_array)) PROVIDE_HIDDEN (__fini_array_end = .); } :data .test_array : { PROVIDE_HIDDEN (__test_array_start = .); KEEP (*(.test_array)) PROVIDE_HIDDEN (__test_array_end = .); } :data .note.openbsd.ident : { KEEP (*(.note.netbsd.ident)) *(.note.netbsd.*) } :data :note .bss : { KEEP (*(.bss)) *(.bss.*) } :data } harec-0.24.2/rt/hare+openbsd.sc000066400000000000000000000000621464473277600162240ustar00rootroot00000000000000/* empty linker script; not needed for OpenBSD */ harec-0.24.2/rt/hare.sc000066400000000000000000000012671464473277600146060ustar00rootroot00000000000000PHDRS { headers PT_PHDR PHDRS; text PT_LOAD FILEHDR PHDRS; data PT_LOAD; } ENTRY(_start); SECTIONS { . = 0x8000000; .text : { KEEP (*(.text)) *(.text.*) } :text . = 0x80000000; .data : { KEEP (*(.data)) *(.data.*) } :data .init_array : { PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(.init_array)) PROVIDE_HIDDEN (__init_array_end = .); } :data .fini_array : { PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(.fini_array)) PROVIDE_HIDDEN (__fini_array_end = .); } :data .test_array : { PROVIDE_HIDDEN (__test_array_start = .); KEEP (*(.test_array)) PROVIDE_HIDDEN (__test_array_end = .); } :data .bss : { KEEP (*(.bss)) *(.bss.*) } :data } harec-0.24.2/rt/itos.ha000066400000000000000000000010241464473277600146170ustar00rootroot00000000000000// SPDX-License-Identifier: MPL-2.0 // (c) Hare authors fn itos(i: int) str = { static let buf: [len("-2147483648")]u8 = [0...]; let sl = buf[..0]; if (i == 0) { return "0"; } else if (i == -2147483648) { return "-2147483648"; }; if (i < 0) { static append(sl, '-'); i = -i; }; for (i > 0) { static append(sl, (i % 10): u8 + '0'); i /= 10; }; for (let s = 0z, e = len(sl) - 1; s < e) { let tmp = sl[s]; sl[s] = sl[e]; sl[e] = tmp; s += 1; e -= 1; }; return *(&sl: *str); }; harec-0.24.2/rt/malloc+libc.ha000066400000000000000000000021321464473277600160160ustar00rootroot00000000000000// SPDX-License-Identifier: MPL-2.0 // (c) Hare authors // Allocates n bytes of memory and returns a pointer to them, or null if there // is insufficient memory. export fn malloc(n: size) nullable *opaque = { return c_malloc(n); }; // Changes the allocation size of a pointer to n bytes. If n is smaller than // the prior allocation, it is truncated; otherwise the allocation is expanded // and the values of the new bytes are undefined. May return a different pointer // than the one given if there is insufficient space to expand the pointer // in-place. Returns null if there is insufficient memory to support the // request. export fn realloc(p: nullable *opaque, n: size) nullable *opaque = { if (n == 0) { free(p); return null; }; return c_realloc(p, n); }; // Frees a pointer previously allocated with [[malloc]]. export @symbol("rt.free") fn free_(p: nullable *opaque) void = { c_free(p); }; @symbol("malloc") fn c_malloc(size) nullable *opaque; @symbol("realloc") fn c_realloc(nullable *opaque, size) nullable *opaque; @symbol("free") fn c_free(nullable *opaque) void; harec-0.24.2/rt/malloc.ha000066400000000000000000000205001464473277600151100ustar00rootroot00000000000000// This is a simple memory allocator, based on // Appel, Andrew W., and David A. Naumann. "Verified sequential malloc/free" // but with logarithmic bin sizing and additional safety checks. Not thread-safe // A group of blocks that were allocated together. type chunk = union { padding: size, // TODO: track number of active allocations here data: [*]u8, }; // Metadata for a block. export type meta = struct { union { sz: size, next: uintptr, }, user: [*]u8, }; // Size of the header/footer for allocations. def META: size = size(size); // Alignment for pointers returned by malloc. // XXX: arch def ALIGN: size = 16; // Allocation granularity for large allocs. Only used to allow sanity-checking // large heap pointers, doesn't necessarily need to match system page size. def PAGESZ: size = 4096; // Amount of memory to allocate at a time for chunks (2MiB). def CHUNKSZ: size = 1 << 21; // Byte to fill allocations with while they're not in use. def POISON: u8 = 0x69; // Number of allocations currently in flight. let cur_allocs: size = 0; // Freelists for blocks up to 2048 bytes. let bins: [9]nullable *meta = [null...]; // The chunk to allocate from if there are no blocks available in the right // freelist. let cur_chunk: (*chunk, size) = (null: *chunk, CHUNKSZ); // Allocates n bytes of memory and returns a pointer to them, or null if there // is insufficient memory. export fn malloc(n: size) nullable *opaque = { if (n == 0) return null; if (size_islarge(n)) { // Round up to PAGESZ and just use mmap directly n = realsz(n); let m = match (segmalloc(n + ALIGN + META)) { case null => return null; case let p: *opaque => yield (p: uintptr + ALIGN - META): *meta; }; m.sz = n; *(&m.user[n]: *size) = n; // For out-of-bounds write detection cur_allocs += 1; return &m.user; }; let bin = size_getbin(n), sz = bin_getsize(bin); let m = match (bins[bin]) { case null => if (cur_chunk.1 + META + sz + META > CHUNKSZ) { // No space left in this chunk, allocate a new one match (segmalloc(CHUNKSZ)) { case null => return null; case let p: *opaque => cur_chunk = (p: *chunk, size(size)); }; }; // Allocate a new block from the currently-active chunk let m = &cur_chunk.0.data[cur_chunk.1]: *meta; cur_chunk.1 += META + sz; m.sz = sz; *(&m.user[sz]: *size) = sz; yield m; case let m: *meta => // Pop a block off the freelist bins[bin] = meta_next(m); checkpoison(m, sz); m.sz = sz; yield m; }; cur_allocs += 1; return &m.user; }; // Frees an allocation returned by [[malloc]]. Freeing any other pointer, or // freeing a pointer that's already been freed, will cause an abort. export @symbol("rt.free") fn free_(p: nullable *opaque) void = { let m = match (p) { case null => return; case let p: *opaque => yield getmeta(p); }; cur_allocs -= 1; if (size_islarge(m.sz)) { // Pass through to munmap segfree((p: uintptr - ALIGN): *opaque, m.sz + ALIGN + META); return; }; // Push onto freelist let bin = size_getbin(m.sz); m.user[..m.sz] = [POISON...]; m.next = bins[bin]: uintptr | 0b1; bins[bin] = m; }; // Changes the allocation size of a pointer to n bytes. If n is smaller than // the prior allocation, it is truncated; otherwise the allocation is expanded // and the values of the new bytes are undefined. May return a different pointer // than the one given if there is insufficient space to expand the pointer // in-place. Returns null if there is insufficient memory to support the // request. export fn realloc(p: nullable *opaque, n: size) nullable *opaque = { if (n == 0) { free(p); return null; }; let m = match (p) { case null => return malloc(n); case let p: *opaque => yield getmeta(p); }; if (realsz(n) == m.sz) return p; let new = match (malloc(n)) { case null => return null; case let new: *opaque => yield new; }; memcpy(new, &m.user, if (n < m.sz) n else m.sz); free(p); return new; }; // Gets the metadata for a given allocation. The provided pointer must have been // returned by [[malloc]] and must not have been freed. export fn getmeta(p: *opaque) *meta = { let m = (p: uintptr - META): *meta; validatemeta(m, false); assert(m.sz & 0b1 == 0, "tried to get metadata for already-freed pointer (double free?)"); return m; }; // Find the maximum allocation size for a given bin. fn bin_getsize(bin: size) size = { // Would need to have bin 0 be ALIGN rather than 0 in this case static assert(ALIGN != META); // Space bins logarithmically let sz = if (bin == 0) 0z else 1 << (bin - 1); // And make sure that (bin_getsize(n) + META) % ALIGN == 0, while erring on // the side of bin sizes slightly larger than powers of two return sz * ALIGN + ALIGN - META; }; // Find the bin for a given allocation size. fn size_getbin(sz: size) size = { // Undo alignment fudging. Equivalent to // ceil((sz - ALIGN + META) / ALIGN) sz = (sz + META - 1) / ALIGN; // Then undo exponentiation if (sz == 0) return 0; let ret = 0z; for (1 << ret < sz; ret += 1) void; return ret + 1; }; // Returns true if a given allocation size should use mmap directly. fn size_islarge(sz: size) bool = sz > bin_getsize(len(bins) - 1); // Gets the next block on the freelist. fn meta_next(m: *meta) nullable *meta = { assert(m.next & 0b1 == 0b1, "expected metadata on freelist to be marked as free (heap corruption?)"); return (m.next & ~0b1): nullable *meta; }; // Round a user-requested allocation size up to the next-smallest size we can // allocate. fn realsz(sz: size) size = { if (size_islarge(sz)) { sz += ALIGN + META; if (sz % PAGESZ != 0) sz += PAGESZ - sz % PAGESZ; return sz - ALIGN - META; }; return bin_getsize(size_getbin(sz)); }; // Check for memory errors related to a given block of memory. fn validatemeta(m: *meta, shallow: bool) void = { assert(&m.user: uintptr % ALIGN == 0, "invalid alignment for metadata pointer (heap corruption?)"); // If we were recursively called to check a next pointer, the block // needs to be marked as free, abort in meta_next() if it's not if (m.sz & 0b1 == 0b1 || shallow == true) { // Block is currently free, verify that it points to a valid // next block match (meta_next(m)) { case null => void; case let next: *meta => assert(next: uintptr % ALIGN == META, "invalid metadata for small allocation on freelist (heap corruption?)"); if (!shallow) validatemeta(next, true); }; return; }; // Block is currently allocated, verify that its size is valid let second = &m.user[m.sz]: *meta; if (size_islarge(m.sz)) { assert((&m.user: uintptr - ALIGN) % PAGESZ == 0, "invalid large allocation address (non-heap pointer?)"); assert((m.sz + ALIGN + META) % PAGESZ == 0, "invalid metadata for large allocation (non-heap pointer?)"); assert(second.sz == m.sz, "invalid secondary metadata for large allocation (out-of-bounds write?)"); return; }; assert(bin_getsize(size_getbin(m.sz)) == m.sz, "invalid metadata for small allocation (non-heap pointer?)"); if (second.sz & 0b1 == 0b1) { // Next block after it in the chunk is free, recursively verify // that it's valid validatemeta(second, false); return; }; // Note that we can't recurse here because the "next block" might // actually be the extra metadata at the end of the chunk (which is // never marked as being on the freelist assert(!size_islarge(second.sz), "invalid secondary metadata for small allocation (out-of-bounds write?)"); assert(bin_getsize(size_getbin(second.sz)) == second.sz, "invalid secondary metadata for small allocation (out-of-bounds write?)"); }; // Verify that a pointer on a free list hasn't been touched since it was added. fn checkpoison(m: *meta, sz: size) void = { match (meta_next(m)) { case null => void; case let next: *meta => validatemeta(next, false); }; for (let i = 0z; i < sz; i += 1) { assert(m.user[i] == POISON, "invalid poison data on freelist (use after free?)"); }; }; @fini fn checkleaks() void = { for (let i = 0z; i < len(bins); i += 1) { for (let m = bins[i]; m != null; m = meta_next(m as *meta)) { checkpoison(m as *meta, bin_getsize(i)); }; }; // TODO: Need a debugging malloc that tracks backtraces for // currently-active allocations in order to help with finding leaks // before we enable this by default. Also need to make sure that this is // run after the rest of @fini in order to guarantee that we see all // frees //assert(cur_allocs == 0, "memory leak"); }; harec-0.24.2/rt/memcpy.ha000066400000000000000000000002451464473277600151370ustar00rootroot00000000000000export fn memcpy(dest: *opaque, src: *opaque, amt: size) void = { let a = dest: *[*]u8, b = src: *[*]u8; for (let i = 0z; i < amt; i += 1) { a[i] = b[i]; }; }; harec-0.24.2/rt/memmove.ha000066400000000000000000000005141464473277600153110ustar00rootroot00000000000000export fn memmove(dest: *opaque, src: *opaque, n: size) void = { let d = dest: *[*]u8, s = src: *[*]u8; if (d: uintptr == s: uintptr) { return; }; if (d: uintptr < s: uintptr) { for (let i = 0z; i < n; i += 1) { d[i] = s[i]; }; } else { for (let i = 0z; i < n; i += 1) { d[n - i - 1] = s[n - i - 1]; }; }; }; harec-0.24.2/rt/memset.ha000066400000000000000000000002161464473277600151350ustar00rootroot00000000000000export fn memset(dest: *opaque, val: u8, amt: size) void = { let a = dest: *[*]u8; for (let i = 0z; i < amt; i += 1) { a[i] = val; }; }; harec-0.24.2/rt/strcmp.ha000066400000000000000000000004371464473277600151600ustar00rootroot00000000000000export fn strcmp(_a: str, _b: str) bool = { if (len(_a) != len(_b)) { return false; }; let a = (&_a: *string).data, b = (&_b: *string).data; let a = a: *[*]u8, b = b: *[*]u8; for (let i = 0z; i < len(_a); i += 1) { if (a[i] != b[i]) { return false; }; }; return true; }; harec-0.24.2/scripts/000077500000000000000000000000001464473277600143745ustar00rootroot00000000000000harec-0.24.2/scripts/version000077500000000000000000000006361464473277600160140ustar00rootroot00000000000000#!/bin/sh # Distro packagers may set the LOCALVER variable to add their distribution to # the version, e.g. 1.0-alpine. VERSION=${VERSION:0.24.2} ver=$(git describe 2>/dev/null) if [ $? -ne 0 ] then ver="dev+$(git log -1 --format='%h' 2>/dev/null)" if [ $? -ne 0 ] then # git presumed unavailable ver=$VERSION fi fi localver=${LOCALVER:-} if [ ${#localver} != 0 ] then ver="$ver-$localver" fi echo $ver harec-0.24.2/src/000077500000000000000000000000001464473277600134745ustar00rootroot00000000000000harec-0.24.2/src/check.c000066400000000000000000004267401464473277600147320ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "ast.h" #include "check.h" #include "eval.h" #include "expr.h" #include "identifier.h" #include "mod.h" #include "scope.h" #include "type_store.h" #include "typedef.h" #include "types.h" #include "util.h" void mkident(struct context *ctx, struct identifier *out, const struct identifier *in, const char *symbol) { if (symbol) { out->name = xstrdup(symbol); return; } identifier_dup(out, in); if (ctx->ns && !in->ns) { out->ns = xcalloc(1, sizeof(struct identifier)); identifier_dup(out->ns, ctx->ns); } } void mkstrliteral(struct expression *expr, const char *fmt, ...) { va_list ap; va_start(ap, fmt); size_t n = vsnprintf(NULL, 0, fmt, ap); va_end(ap); char *s = xcalloc(n + 1, n); va_start(ap, fmt); vsnprintf(s, n + 1, fmt, ap); va_end(ap); *expr = (struct expression) { .type = EXPR_LITERAL, .result = &builtin_type_const_str, }; expr->literal.string.value = s; expr->literal.string.len = n; } char * gen_typename(const struct type *type) { size_t sz = 0; char *ptr = NULL; FILE *f = open_memstream(&ptr, &sz); if (f == NULL) { xfprintf(stderr, "Unable to open memstream: %s\n", strerror(errno)); exit(EXIT_ABNORMAL); } emit_type(type, f); fclose(f); return ptr; } static void handle_errors(struct errors *errors) { struct errors *error = errors; while (error) { xfprintf(stderr, "%s:%d:%d: error: %s\n", sources[error->loc.file], error->loc.lineno, error->loc.colno, error->msg); errline(error->loc); free(error->msg); struct errors *next = error->next; free(error); error = next; } if (errors) { exit(EXIT_CHECK); } } static void mkerror(const struct location loc, struct expression *expr) { expr->type = EXPR_LITERAL; expr->result = &builtin_type_error; expr->literal.uval = 0; expr->loc = loc; } static void verror(struct context *ctx, const struct location loc, const char *fmt, va_list ap) { va_list copy; va_copy(copy, ap); size_t sz = vsnprintf(NULL, 0, fmt, copy); va_end(copy); char *msg = xcalloc(sz + 1, 1); vsnprintf(msg, sz + 1, fmt, ap); struct errors *next = *ctx->next = xcalloc(1, sizeof(struct errors)); next->loc = loc; next->msg = msg; ctx->next = &next->next; } void error(struct context *ctx, struct location loc, struct expression *expr, const char *fmt, ...) { if (expr) { mkerror(loc, expr); } va_list ap; va_start(ap, fmt); verror(ctx, loc, fmt, ap); va_end(ap); } static noreturn void error_norec(struct context *ctx, struct location loc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verror(ctx, loc, fmt, ap); va_end(ap); handle_errors(ctx->errors); abort(); } struct expression * lower_implicit_cast(struct context *ctx, const struct type *to, struct expression *expr) { if (to == expr->result || expr->result->storage == STORAGE_NEVER) { return expr; } if (type_dealias(ctx, to)->storage == STORAGE_TAGGED) { const struct type *interim = tagged_select_subtype(ctx, to, expr->result, true); if (interim) { expr = lower_implicit_cast(ctx, interim, expr); } } struct expression *cast = xcalloc(1, sizeof(struct expression)); cast->type = EXPR_CAST; cast->result = cast->cast.secondary = to; cast->cast.kind = C_CAST; cast->cast.value = expr; cast->cast.lowered = true; return cast; } static void resolve_decl(struct context *ctx, struct incomplete_declaration *idecl); static void check_expr_access(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_ACCESS; expr->access.type = aexpr->access.type; struct scope_object *obj = NULL; switch (expr->access.type) { case ACCESS_IDENTIFIER: obj = scope_lookup(ctx->scope, &aexpr->access.ident); if (!obj) { char buf[IDENT_BUFSIZ]; identifier_unparse_static(&aexpr->access.ident, buf); error(ctx, aexpr->loc, expr, "Unknown object '%s'", buf); return; } wrap_resolver(ctx, obj, resolve_decl); switch (obj->otype) { case O_CONST: // Lower flexible types *expr = *obj->value; flexible_reset_refs(expr->result); break; case O_BIND: case O_DECL: expr->result = obj->type; expr->access.object = obj; break; case O_TYPE: if (type_dealias(ctx, obj->type)->storage != STORAGE_VOID && type_dealias(ctx, obj->type)->storage != STORAGE_DONE) { char *ident = identifier_unparse(&obj->type->alias.ident); error(ctx, aexpr->loc, expr, "Cannot use non void or done type alias '%s' as literal", ident); free(ident); return; } expr->type = EXPR_LITERAL; expr->result = obj->type; break; case O_SCAN: assert(0); // handled above } break; case ACCESS_INDEX: expr->access.array = xcalloc(1, sizeof(struct expression)); expr->access.index = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->access.array, expr->access.array, NULL); check_expression(ctx, aexpr->access.index, expr->access.index, &builtin_type_size); const struct type *atype = type_dereference(ctx, expr->access.array->result); if (!atype) { error(ctx, aexpr->access.array->loc, expr, "Cannot dereference nullable pointer for indexing"); return; } atype = type_dealias(ctx, atype); if (atype->storage == STORAGE_ERROR) { mkerror(aexpr->access.array->loc, expr); return; } const struct type *itype = type_dealias(ctx, expr->access.index->result); if (atype->storage != STORAGE_ARRAY && atype->storage != STORAGE_SLICE) { error(ctx, aexpr->access.array->loc, expr, "Can only index into array or slice object, but got %s", type_storage_unparse(atype->storage)); return; } if (atype->storage == STORAGE_SLICE && atype->array.members->size == SIZE_UNDEFINED) { error(ctx, aexpr->access.array->loc, expr, "Cannot use index into slice whose member type has undefined size"); return; } if (!type_is_integer(ctx, itype)) { error(ctx, aexpr->access.index->loc, expr, "Cannot use non-integer %s type as slice/array index", type_storage_unparse(itype->storage)); return; } expr->access.index = lower_implicit_cast(ctx, &builtin_type_size, expr->access.index); expr->result = type_store_lookup_with_flags(ctx, atype->array.members, atype->flags | atype->array.members->flags); // Compile-time bounds check if (atype->storage == STORAGE_ARRAY && atype->array.length != SIZE_UNDEFINED) { struct expression *evaled = xcalloc(1, sizeof(struct expression)); if (eval_expr(ctx, expr->access.index, evaled)) { if (evaled->literal.uval >= atype->array.length) { error(ctx, aexpr->loc, expr, "Index must not be greater than array length"); free(evaled); return; } expr->access.bounds_checked = true; } free(evaled); } break; case ACCESS_FIELD: expr->access._struct = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->access._struct, expr->access._struct, NULL); const struct type *stype = type_dereference(ctx, expr->access._struct->result); if (!stype) { error(ctx, aexpr->access._struct->loc, expr, "Cannot dereference nullable pointer for field selection"); return; } stype = type_dealias(ctx, stype); if (stype->storage == STORAGE_ERROR) { mkerror(aexpr->access._struct->loc, expr); return; } if (stype->storage != STORAGE_STRUCT && stype->storage != STORAGE_UNION) { error(ctx, aexpr->access._struct->loc, expr, "Cannot select field from non-struct, non-union object"); return; } expr->access.field = type_get_field(ctx, stype, aexpr->access.field); if (!expr->access.field) { error(ctx, aexpr->access._struct->loc, expr, "No such struct field '%s'", aexpr->access.field); return; } expr->result = expr->access.field->type; break; case ACCESS_TUPLE: expr->access.tuple = xcalloc(1, sizeof(struct expression)); struct expression *value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->access.tuple, expr->access.tuple, NULL); check_expression(ctx, aexpr->access.value, value, NULL); assert(value->type == EXPR_LITERAL); const struct type *ttype = type_dereference(ctx, expr->access.tuple->result); if (!ttype) { error(ctx, aexpr->access.tuple->loc, expr, "Cannot dereference nullable pointer for value selection"); return; } ttype = type_dealias(ctx, ttype); if (ttype->storage == STORAGE_ERROR) { mkerror(aexpr->access.tuple->loc, expr); return; } if (ttype->storage != STORAGE_TUPLE) { error(ctx, aexpr->access.tuple->loc, expr, "Cannot select value from non-tuple object"); return; } if (!type_is_integer(ctx, value->result)) { error(ctx, aexpr->access.tuple->loc, expr, "Cannot use non-integer literal to select tuple value"); return; } expr->access.tvalue = type_get_value(ttype, aexpr->access.value->literal.uval); if (!expr->access.tvalue) { error(ctx, aexpr->access.tuple->loc, expr, "No such tuple value '%zu'", aexpr->access.value->literal.uval); return; } expr->access.tindex = aexpr->access.value->literal.uval; expr->result = expr->access.tvalue->type; break; } } static void check_expr_alloc_init(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { // alloc(initializer) case int ptrflags = 0; const struct type *inithint = NULL; if (hint) { const struct type *htype = type_dealias(ctx, hint); switch (htype->storage) { case STORAGE_POINTER: inithint = htype->pointer.referent; // TODO: Describe the use of pointer flags in the spec ptrflags = htype->pointer.flags; break; case STORAGE_SLICE: inithint = hint; break; case STORAGE_TAGGED: // TODO break; default: // The user's code is wrong here, but we'll let it fail // later. break; } } check_expression(ctx, aexpr->alloc.init, expr->alloc.init, inithint); const struct type *objtype = expr->alloc.init->result; if (type_dealias(ctx, objtype)->storage == STORAGE_ARRAY && type_dealias(ctx, objtype)->array.expandable) { const struct type *atype = type_dealias(ctx, objtype); if (!inithint) { error(ctx, aexpr->loc, expr, "Cannot infer expandable array length without type hint"); return; } const struct type *htype = type_dealias(ctx, inithint); if (htype->storage != STORAGE_ARRAY) { error(ctx, aexpr->loc, expr, "Cannot assign expandable array from non-array type"); return; } assert(htype->array.members == atype->array.members); objtype = inithint; } if (type_is_flexible(objtype) && inithint) { const struct type *promoted = promote_flexible(ctx, objtype, inithint); if (promoted) { objtype = promoted; } } else if (inithint) { // XXX: this is dumb, but we're gonna get rid of the const flag // anyway so it doesn't matter struct type stripped_objtype = *type_dealias(ctx, objtype); stripped_objtype.flags &= ~TYPE_CONST; stripped_objtype.id = type_hash(&stripped_objtype); struct type stripped_inithint = *type_dealias(ctx, inithint); stripped_inithint.flags &= ~TYPE_CONST; stripped_inithint.id = type_hash(&stripped_inithint); if (stripped_objtype.id == stripped_inithint.id) { objtype = inithint; } } expr->result = type_store_lookup_pointer(ctx, aexpr->loc, objtype, ptrflags); if (expr->alloc.init->result->size == SIZE_UNDEFINED) { error(ctx, aexpr->loc, expr, "Cannot allocate object of undefined size"); return; } } static void check_expr_alloc_slice(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { // alloc(init, capacity) case check_expression(ctx, aexpr->alloc.init, expr->alloc.init, hint); if (expr->alloc.init->result->storage == STORAGE_ERROR) { mkerror(aexpr->alloc.init->loc, expr); return; } const struct type *objtype = expr->alloc.init->result; if (type_dealias(ctx, objtype)->storage == STORAGE_ARRAY) { if (type_dealias(ctx, objtype)->array.length == SIZE_UNDEFINED) { error(ctx, aexpr->alloc.init->loc, expr, "Slice initializer must have defined length"); return; } } else if (type_dealias(ctx, objtype)->storage != STORAGE_SLICE) { error(ctx, aexpr->alloc.init->loc, expr, "Slice initializer must be of slice or array type, not %s", type_storage_unparse(type_dealias(ctx, objtype)->storage)); return; } const struct type *caphint = &builtin_type_size; expr->alloc.cap = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->alloc.cap, expr->alloc.cap, caphint); const struct type *captype = expr->alloc.cap->result; if (!type_is_assignable(ctx, &builtin_type_size, captype)) { error(ctx, aexpr->alloc.cap->loc, expr, "Slice capacity must be assignable to size"); return; } expr->alloc.cap = lower_implicit_cast(ctx, &builtin_type_size, expr->alloc.cap); struct expression cap = {0}; if (expr->alloc.init->type == EXPR_LITERAL && expr->alloc.cap->type == EXPR_LITERAL && eval_expr(ctx, expr->alloc.cap, &cap)) { uint64_t len = 0; for (struct array_literal *c = expr->alloc.init->literal.array; c != NULL; c = c->next) { len++; } if (cap.literal.uval < len) { error(ctx, aexpr->alloc.cap->loc, expr, "Slice capacity cannot be smaller than length of initializer"); return; } } const struct type *membtype = type_dealias(ctx, objtype)->array.members; expr->result = type_store_lookup_slice(ctx, aexpr->alloc.init->loc, membtype); if (objtype->storage == STORAGE_ARRAY && objtype->array.expandable) { expr->alloc.kind = ALLOC_LEN; } } static void check_expr_alloc_copy(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { // alloc(init...) case check_expression(ctx, aexpr->alloc.init, expr->alloc.init, hint); if (expr->alloc.init->result->storage == STORAGE_ERROR) { mkerror(aexpr->alloc.init->loc, expr); return; } const struct type *result = type_dealias(ctx, expr->alloc.init->result); if (result->storage != STORAGE_ARRAY && result->storage != STORAGE_SLICE) { error(ctx, aexpr->alloc.init->loc, expr, "Slice initializer must be of slice or array type, not %s", type_storage_unparse(result->storage)); return; } if (hint) { const struct type *htype = type_dealias(ctx, hint); if (htype->storage != STORAGE_SLICE && htype->storage != STORAGE_TAGGED) { error(ctx, aexpr->alloc.init->loc, expr, "Hint must be a slice type, not %s", type_storage_unparse(htype->storage)); return; } } check_expression(ctx, aexpr->alloc.init, expr->alloc.init, hint); result = type_dealias(ctx, expr->alloc.init->result); expr->result = type_store_lookup_slice(ctx, aexpr->alloc.init->loc, result->array.members); } static void check_expr_alloc(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { assert(aexpr->type == EXPR_ALLOC); expr->type = EXPR_ALLOC; expr->alloc.init = xcalloc(1, sizeof(struct expression)); expr->alloc.kind = aexpr->alloc.kind; switch (aexpr->alloc.kind) { case ALLOC_OBJECT: check_expr_alloc_init(ctx, aexpr, expr, hint); break; case ALLOC_CAP: check_expr_alloc_slice(ctx, aexpr, expr, hint); break; case ALLOC_COPY: check_expr_alloc_copy(ctx, aexpr, expr, hint); break; case ALLOC_LEN: abort(); // Not determined by parse } } static void check_expr_append_insert(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { assert(aexpr->type == EXPR_APPEND || aexpr->type == EXPR_INSERT); expr->type = aexpr->type; expr->result = &builtin_type_void; expr->append.is_static = aexpr->append.is_static; expr->append.is_multi = aexpr->append.is_multi; expr->append.object = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->append.object, expr->append.object, NULL); if (expr->append.object->result->storage == STORAGE_ERROR) { mkerror(aexpr->loc, expr); return; } if (expr->append.object->type != EXPR_ACCESS) { error(ctx, aexpr->append.object->loc, expr, "Expression must operate on an object"); return; } const struct type *sltype; const struct type *sltypename; const char *exprtype_name; struct expression *object = NULL; switch (expr->type) { case EXPR_APPEND: sltypename = expr->append.object->result; exprtype_name = "append"; object = expr->append.object; break; case EXPR_INSERT: assert(expr->append.object->type == EXPR_ACCESS); assert(expr->append.object->access.type == ACCESS_INDEX); sltypename = expr->append.object->access.array->result; exprtype_name = "insert"; object = expr->append.object->access.array; break; default: abort(); // Invariant } if (object->type == EXPR_ACCESS && object->access.type == ACCESS_IDENTIFIER && object->access.object->flags & SO_FOR_EACH_SUBJECT) { error(ctx, aexpr->append.object->loc, expr, "cannot %s to subject of for-each loop", exprtype_name); } sltype = type_dereference(ctx, sltypename); if (!sltype) { error(ctx, aexpr->append.object->loc, expr, "Cannot dereference nullable pointer for %s expression", exprtype_name); return; } sltype = type_dealias(ctx, sltype); if (sltype->storage != STORAGE_SLICE) { char *typename = gen_typename(sltypename); error(ctx, aexpr->append.object->loc, expr, "%s expression must operate on a slice, but got %s", exprtype_name, typename); free(typename); return; } if (sltype->flags & TYPE_CONST) { error(ctx, aexpr->append.object->loc, expr, "expression must operate on a mutable slice"); return; } if (sltype->array.members->size == SIZE_UNDEFINED) { error(ctx, aexpr->append.object->loc, expr, "Cannot %s %sto slice whose member type has undefined size", exprtype_name, expr->type == EXPR_APPEND ? "" : "in"); return; } expr->append.value = xcalloc(1, sizeof(struct expression)); if (!expr->append.is_multi && !aexpr->append.length) { check_expression(ctx, aexpr->append.value, expr->append.value, sltype->array.members); if (!type_is_assignable(ctx, sltype->array.members, expr->append.value->result)) { error(ctx, aexpr->append.value->loc, expr, "Value type must be assignable to object member type"); return; } expr->append.value = lower_implicit_cast(ctx, sltype->array.members, expr->append.value); return; } check_expression(ctx, aexpr->append.value, expr->append.value, sltype); const struct type *valtype = type_dealias(ctx, expr->append.value->result); if (aexpr->append.length) { if (valtype->storage != STORAGE_ARRAY || !valtype->array.expandable) { error(ctx, aexpr->append.value->loc, expr, "Value must be an expandable array in append with length"); return; } struct expression *len = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->append.length, len, &builtin_type_size); if (!type_is_assignable(ctx, &builtin_type_size, len->result)) { error(ctx, aexpr->append.length->loc, expr, "Length parameter must be assignable to size"); return; } len = lower_implicit_cast(ctx, &builtin_type_size, len); expr->append.length = len; } else if (valtype->storage != STORAGE_SLICE && valtype->storage != STORAGE_ARRAY && (valtype->storage != STORAGE_POINTER || valtype->pointer.referent->storage != STORAGE_ARRAY || valtype->pointer.flags & PTR_NULLABLE)) { error(ctx, aexpr->append.value->loc, expr, "Value must be an array, slice, or array pointer in multi-valued %s", exprtype_name); return; } else if (valtype->size == SIZE_UNDEFINED) { error(ctx, aexpr->loc, expr, "Value array must be bounded"); return; } if (valtype->storage == STORAGE_POINTER) { valtype = valtype->pointer.referent; const struct type *slice = type_store_lookup_slice(ctx, aexpr->loc, valtype->array.members); expr->append.value = lower_implicit_cast(ctx, slice, expr->append.value); } if (sltype->array.members != valtype->array.members) { error(ctx, aexpr->loc, expr, "Value member type must match object member type"); return; } } static void check_assert(struct context *ctx, struct ast_expression_assert e, struct location loc, struct expression *expr) { expr->result = &builtin_type_void; expr->type = EXPR_ASSERT; if (e.cond != NULL) { expr->assert.cond = xcalloc(1, sizeof(struct expression)); check_expression(ctx, e.cond, expr->assert.cond, &builtin_type_bool); loc = e.cond->loc; if (expr->assert.cond->result->storage == STORAGE_ERROR) { mkerror(loc, expr); return; } if (type_dealias(ctx, expr->assert.cond->result)->storage != STORAGE_BOOL) { error(ctx, loc, expr, "Assertion condition must be boolean"); return; } } else { if (!e.is_static) { expr->result = &builtin_type_never; } } if (e.message == NULL) { expr->assert.fixed_reason = ABORT_ANON_ASSERTION_FAILED; } else { expr->assert.message = xcalloc(1, sizeof(struct expression)); check_expression(ctx, e.message, expr->assert.message, &builtin_type_str); if (type_dealias(ctx, expr->assert.message->result)->storage != STORAGE_STRING) { error(ctx, e.message->loc, expr, "Assertion message must be string"); return; } } if (e.is_static) { expr->type = EXPR_LITERAL; bool cond = false; if (expr->assert.cond != NULL) { struct expression out = {0}, msgout = {0}; if (!eval_expr(ctx, expr->assert.cond, &out)) { error(ctx, e.cond->loc, expr, "Unable to evaluate static assertion condition at compile time"); return; } if (expr->assert.message) { if (!eval_expr(ctx, expr->assert.message, &msgout)) { error(ctx, e.message->loc, expr, "Unable to evaluate static assertion message at compile time"); return; } } assert(type_dealias(ctx, out.result)->storage == STORAGE_BOOL); cond = out.literal.bval; } // XXX: Should these abort immediately? if (!cond) { if (e.message != NULL) { error(ctx, loc, expr, "Static assertion failed: %.*s", expr->assert.message->literal.string.len, expr->assert.message->literal.string.value); } else { error(ctx, loc, expr, "Static assertion failed"); } } } } static void check_expr_assert(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { check_assert(ctx, aexpr->assert, aexpr->loc, expr); } static void check_binarithm_op(struct context *ctx, struct expression *expr, enum binarithm_operator op) { const struct type *dealiased = type_dealias(ctx, expr->result); switch (op) { // Numeric arithmetic case BIN_DIV: case BIN_MINUS: case BIN_PLUS: case BIN_TIMES: if (!type_is_numeric(ctx, dealiased)) { error(ctx, expr->loc, expr, "Cannot perform arithmetic on non-numeric %s type", type_storage_unparse(dealiased->storage)); } return; // Integer artithmetic case BIN_BAND: case BIN_BOR: case BIN_LSHIFT: case BIN_MODULO: case BIN_RSHIFT: case BIN_BXOR: if (!type_is_integer(ctx, dealiased)) { error(ctx, expr->loc, expr, "Cannot perform operation on non-integer %s type", type_storage_unparse(dealiased->storage)); } return; // Logical arithmetic case BIN_LAND: case BIN_LOR: case BIN_LXOR: expr->result = &builtin_type_bool; if (dealiased->storage != STORAGE_BOOL) { error(ctx, expr->loc, expr, "Cannot perform logical arithmetic on non-bool %s type", type_storage_unparse(dealiased->storage)); } return; case BIN_GREATER: case BIN_GREATEREQ: case BIN_LESS: case BIN_LESSEQ: expr->result = &builtin_type_bool; if (!type_is_numeric(ctx, dealiased)) { error(ctx, expr->loc, expr, "Cannot perform comparison on non-numeric %s type", type_storage_unparse(dealiased->storage)); } return; case BIN_LEQUAL: case BIN_NEQUAL: expr->result = &builtin_type_bool; if (!type_is_numeric(ctx, dealiased) && dealiased->storage != STORAGE_POINTER && dealiased->storage != STORAGE_STRING && dealiased->storage != STORAGE_BOOL && dealiased->storage != STORAGE_RCONST && dealiased->storage != STORAGE_RUNE) { error(ctx, expr->loc, expr, "Cannot perform equality test on %s type", type_storage_unparse(dealiased->storage)); } return; } } static void check_expr_assign(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_ASSIGN; expr->result = &builtin_type_void; expr->assign.op = aexpr->assign.op; struct expression *object = xcalloc(1, sizeof(struct expression)); struct expression *value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->assign.object, object, NULL); check_expression(ctx, aexpr->assign.value, value, object->result); if (object->type == EXPR_LITERAL && object->result != &builtin_type_error) { error(ctx, aexpr->assign.object->loc, expr, "Cannot assign to constant"); return; } if (object->result->size == SIZE_UNDEFINED) { error(ctx, aexpr->loc, expr, "Cannot assign to object with undefined size"); return; } if (!type_is_assignable(ctx, object->result, value->result)) { char *valtypename = gen_typename(value->result); char *objtypename = gen_typename(object->result); error(ctx, aexpr->loc, expr, "rvalue type (%s) is not assignable to lvalue (%s)", valtypename, objtypename); free(valtypename); free(objtypename); return; } if (expr->assign.op != BIN_LEQUAL) { check_binarithm_op(ctx, object, expr->assign.op); } if (object->type == EXPR_SLICE && value->result->storage == STORAGE_ARRAY && value->result->array.expandable) { expr->assign.value = value; } else { expr->assign.value = lower_implicit_cast(ctx, object->result, value); } expr->assign.object = object; } static const struct type * type_promote(struct context *ctx, const struct type *a, const struct type *b) { // Note: we must return either a, b, or NULL // TODO: There are likely some improperly handled edge cases around type // flags, both here and in the spec const struct type *da = type_store_lookup_with_flags(ctx, a, 0); const struct type *db = type_store_lookup_with_flags(ctx, b, 0); if (da == db) { const struct type *base = type_store_lookup_with_flags(ctx, a, a->flags | b->flags); assert(base == a || base == b); return base; } if (a->storage == STORAGE_ALIAS && b->storage == STORAGE_ALIAS) { return NULL; } da = type_dealias(ctx, da); db = type_dealias(ctx, db); if (da == db) { return a->storage == STORAGE_ALIAS ? a : b; } if (type_is_flexible(da) || type_is_flexible(db)) { return promote_flexible(ctx, a, b); } if (db->storage == STORAGE_ENUM && da->storage == db->alias.type->storage) { return b; } if (db->storage == STORAGE_ERROR) { return a; } switch (da->storage) { case STORAGE_ARRAY: if (da->array.length == SIZE_UNDEFINED && da->array.members) { return b; } if (db->array.length == SIZE_UNDEFINED && db->array.members) { return a; } return NULL; case STORAGE_ENUM: if (da->alias.type->storage == db->storage) { return a; } return NULL; case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: if (!type_is_integer(ctx, db) || !type_is_signed(ctx, db) || db->size == da->size) { return NULL; } return da->size > db->size ? a : b; case STORAGE_U32: case STORAGE_U16: case STORAGE_U64: case STORAGE_UINT: case STORAGE_SIZE: case STORAGE_U8: if (da->storage == STORAGE_SIZE && db->storage == STORAGE_UINTPTR) { return db; } if (!type_is_integer(ctx, db) || type_is_signed(ctx, db) || db->size == da->size) { return NULL; } return da->size > db->size ? a : b; case STORAGE_F32: case STORAGE_F64: if (!type_is_float(ctx, db) || db->size == da->size) { return NULL; } return da->size > db->size ? a : b; case STORAGE_POINTER: if (db->storage == STORAGE_NULL) { return a; } if (db->storage == STORAGE_UINTPTR) { return a; } if (db->storage != STORAGE_POINTER) { return NULL; } if (da->pointer.referent->storage == STORAGE_OPAQUE || db->pointer.referent->storage == STORAGE_OPAQUE) { return a; } const struct type *r = type_promote(ctx, da->pointer.referent, db->pointer.referent); if (r == da->pointer.referent) { return a; } if (r == db->pointer.referent) { return b; } assert(r == NULL); return NULL; case STORAGE_NULL: if (db->storage == STORAGE_POINTER || db->storage == STORAGE_UINTPTR) { return b; } return NULL; case STORAGE_ERROR: case STORAGE_NEVER: return b; case STORAGE_UINTPTR: if (db->storage == STORAGE_SIZE || db->storage == STORAGE_NULL) { return a; } if (db->storage == STORAGE_POINTER) { return b; } return NULL; // Cannot be promoted case STORAGE_BOOL: case STORAGE_FUNCTION: case STORAGE_OPAQUE: case STORAGE_RUNE: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: return NULL; // Handled above case STORAGE_ALIAS: case STORAGE_FCONST: case STORAGE_ICONST: case STORAGE_RCONST: assert(0); } assert(0); } static void resolve_enum_field(struct context *ctx, struct incomplete_declaration *idel); static bool type_has_default(struct context *ctx, const struct type *type) { switch (type->storage) { case STORAGE_VOID: case STORAGE_DONE: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_BOOL: case STORAGE_RUNE: case STORAGE_F32: case STORAGE_F64: case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: case STORAGE_SIZE: case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_ERROR: return true; case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_TAGGED: case STORAGE_VALIST: return false; case STORAGE_ARRAY: return type->array.length != SIZE_UNDEFINED && type_has_default(ctx, type->array.members); case STORAGE_ENUM: for (struct scope_object *obj = type->_enum.values->objects; obj != NULL; obj = obj->lnext) { if (obj->otype == O_DECL) { continue; } if (obj->otype == O_SCAN) { wrap_resolver(ctx, obj, resolve_enum_field); } assert(obj->otype == O_CONST); if (obj->value->literal.uval == 0) { return true; } } return false; case STORAGE_POINTER: return type->pointer.flags & PTR_NULLABLE; case STORAGE_STRUCT: case STORAGE_UNION: for (struct struct_field *sf = type->struct_union.fields; sf != NULL; sf = sf->next) { if (!type_has_default(ctx, sf->type)) { return false; } } return true; case STORAGE_TUPLE: for (const struct type_tuple *t = &type->tuple; t != NULL; t = t->next) { if (!type_has_default(ctx, t->type)) { return false; } } return true; case STORAGE_ALIAS: return type_has_default(ctx, type_dealias(ctx, type)); case STORAGE_FCONST: case STORAGE_ICONST: case STORAGE_NULL: case STORAGE_RCONST: abort(); // unreachable } abort(); // Unreachable } static void check_expr_binarithm(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_BINARITHM; expr->binarithm.op = aexpr->binarithm.op; struct expression *lvalue = xcalloc(1, sizeof(struct expression)), *rvalue = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->binarithm.lvalue, lvalue, NULL); check_expression(ctx, aexpr->binarithm.rvalue, rvalue, NULL); if (lvalue->result->storage == STORAGE_ERROR || rvalue->result->storage == STORAGE_ERROR) { mkerror(aexpr->loc, expr); return; } expr->result = type_promote(ctx, lvalue->result, rvalue->result); if (expr->result == NULL) { char *ltypename = gen_typename(lvalue->result); char *rtypename = gen_typename(rvalue->result); error(ctx, aexpr->loc, expr, "Cannot promote lvalue %s and rvalue %s", ltypename, rtypename); free(ltypename); free(rtypename); return; } expr->binarithm.lvalue = lower_implicit_cast(ctx, expr->result, lvalue); expr->binarithm.rvalue = lower_implicit_cast(ctx, expr->result, rvalue); check_binarithm_op(ctx, expr, expr->binarithm.op); } static void create_unpack_bindings(struct context *ctx, const struct type *type, const struct location loc, const struct ast_binding_unpack *aunpack, bool is_static, struct expression *expr) { type = type_dealias(ctx, type); if (type->storage != STORAGE_TUPLE) { error(ctx, loc, expr, "Cannot unpack non-tuple type"); return; } expr->binding.unpack = xcalloc(1, sizeof(struct binding_unpack)); struct binding_unpack *unpack = expr->binding.unpack; const struct type_tuple *type_tuple = &type->tuple; while (aunpack != NULL && type_tuple != NULL) { if (type_tuple->type->size == SIZE_UNDEFINED) { error(ctx, loc, expr, "Cannot create binding of undefined size"); return; } if (aunpack->name != NULL) { struct identifier ident = { .name = aunpack->name, }; if (unpack->object != NULL) { unpack->next = xcalloc(1, sizeof(struct binding_unpack)); unpack = unpack->next; } if (is_static) { struct identifier gen = {0}; // Generate a static declaration identifier gen.name = gen_name(&ctx->id, "static.%d"); unpack->object = scope_insert( ctx->scope, O_DECL, &gen, &ident, type_tuple->type, NULL); } else { unpack->object = scope_insert( ctx->scope, O_BIND, &ident, &ident, type_tuple->type, NULL); } unpack->offset = type_tuple->offset; } aunpack = aunpack->next; type_tuple = type_tuple->next; } if (expr->binding.unpack->object == NULL) { error(ctx, loc, expr, "Must have at least one non-underscore value when unpacking tuples"); return; } if (type_tuple != NULL) { error(ctx, loc, expr, "Fewer bindings than tuple elements were provided when unpacking"); return; } if (aunpack != NULL) { error(ctx, loc, expr, "More bindings than tuple elements were provided when unpacking"); return; } } static void check_expr_binding(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { assert(aexpr->type == EXPR_BINDING || aexpr->type == EXPR_DEFINE); expr->type = aexpr->type; expr->result = &builtin_type_void; struct expression_binding *binding = &expr->binding; struct expression_binding **next = &expr->binding.next; const struct ast_expression_binding *abinding = &aexpr->binding; while (abinding) { const struct type *type = NULL; if (abinding->type) { type = type_store_lookup_atype(ctx, abinding->type); type = type_store_lookup_with_flags(ctx, type, type->flags | abinding->flags); } struct expression *initializer = xcalloc(1, sizeof(struct expression)); check_expression(ctx, abinding->initializer, initializer, type); if (abinding->type && abinding->type->storage == STORAGE_ARRAY && abinding->type->array.contextual) { if (initializer->result->storage == STORAGE_ERROR) { // no-op } else if (initializer->result->storage != STORAGE_ARRAY) { error(ctx, aexpr->loc, expr, "Cannot infer array length from non-array type"); return; } else if (initializer->result->array.members != type->array.members) { char *inittype = gen_typename(initializer->result); char *bindingtype= gen_typename(type); error(ctx, aexpr->loc, expr, "Initializer of type %s is not assignable to binding type %s", inittype, bindingtype); free(inittype); free(bindingtype); return; } type = initializer->result; } struct identifier ident = { .name = abinding->name, }; if (expr->type == EXPR_DEFINE) { if (type) { initializer = lower_implicit_cast( ctx, type, initializer); } struct expression *value = xcalloc(1, sizeof(struct expression)); if (!eval_expr(ctx, initializer, value)) { error(ctx, initializer->loc, value, "Unable to evaluate constant init at compile time"); type = &builtin_type_error; } binding->initializer = value; binding->object = scope_insert(ctx->scope, O_CONST, &ident, &ident, NULL, value); goto done; } if (!type) { type = type_store_lookup_with_flags(ctx, initializer->result, abinding->flags); } if (abinding->unpack != NULL) { create_unpack_bindings(ctx, type, abinding->initializer->loc, abinding->unpack, abinding->is_static, expr); } else { if (abinding->is_static) { // Generate a static declaration identifier struct identifier gen = {0}; gen.name = gen_name(&ctx->id, "static.%d"); binding->object = scope_insert(ctx->scope, O_DECL, &gen, &ident, type, NULL); } else { binding->object = scope_insert(ctx->scope, O_BIND, &ident, &ident, type, NULL); } } if (type->storage == STORAGE_NULL) { error(ctx, aexpr->loc, expr, "Null is not a valid type for a binding"); return; } if (!type_is_assignable(ctx, type, initializer->result)) { char *inittype = gen_typename(initializer->result); char *bindingtype= gen_typename(type); error(ctx, aexpr->loc, expr, "Initializer of type %s is not assignable to binding type %s", inittype, bindingtype); free(inittype); free(bindingtype); return; } type = lower_flexible(ctx, type, NULL); if (type->size == SIZE_UNDEFINED) { error(ctx, aexpr->loc, expr, "Cannot create binding for type of undefined size"); return; } binding->initializer = lower_implicit_cast(ctx, type, initializer); if (abinding->is_static) { struct expression *value = xcalloc(1, sizeof(struct expression)); if (!eval_expr(ctx, binding->initializer, value)) { error(ctx, abinding->initializer->loc, expr, "Unable to evaluate static initializer at compile time"); return; } // TODO: Free initializer binding->initializer = value; } done: if (abinding->next) { binding = *next = xcalloc(1, sizeof(struct expression_binding)); next = &binding->next; } abinding = abinding->next; } } static void check_expr_call(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_CALL; struct expression *lvalue = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->call.lvalue, lvalue, NULL); expr->call.lvalue = lvalue; const struct type *fntype = type_dereference(ctx, lvalue->result); if (!fntype) { error(ctx, aexpr->loc, expr, "Cannot dereference nullable pointer type for function call"); return; } fntype = type_dealias(ctx, fntype); if (fntype->storage == STORAGE_ERROR) { mkerror(aexpr->loc, expr); return; } if (fntype->storage != STORAGE_FUNCTION) { error(ctx, aexpr->loc, expr, "Cannot call non-function type"); return; } if (fntype->func.variadism != VARIADISM_HARE && aexpr->call.variadic) { error(ctx, aexpr->loc, expr, "Function type does not permit variadic argument list"); } expr->result = fntype->func.result; struct call_argument *arg, **next = &expr->call.args; struct ast_expression_list *aarg = aexpr->call.args; struct type_func_param *param = fntype->func.params; while (param && aarg) { arg = *next = xcalloc(1, sizeof(struct call_argument)); arg->value = xcalloc(1, sizeof(struct expression)); struct ast_expression val; if (!param->next && fntype->func.variadism == VARIADISM_HARE && !aexpr->call.variadic) { // lower the rest to an array val = (struct ast_expression){ .loc = aarg->expr->loc, .type = EXPR_LITERAL, .literal = { .storage = STORAGE_ARRAY, .array.exprs = aarg, }, }; } else { val = *aarg->expr; } check_expression(ctx, &val, arg->value, param->type); if (!type_is_assignable(ctx, param->type, arg->value->result)) { char *argtypename = gen_typename(arg->value->result); char *paramtypename = gen_typename(param->type); error(ctx, val.loc, expr, "Argument type %s is not assignable to parameter type %s", argtypename, paramtypename); free(argtypename); free(paramtypename); return; } arg->value = lower_implicit_cast(ctx, param->type, arg->value); if (!param->next && fntype->func.variadism == VARIADISM_HARE) { return; } aarg = aarg->next; next = &arg->next; param = param->next; } while (param && param->default_value) { arg = *next = xcalloc(1, sizeof(struct call_argument)); arg->value = param->default_value; next = &arg->next; param = param->next; } if (param) { if (fntype->func.variadism == VARIADISM_HARE && !param->next) { // No variadic arguments, lower to empty slice arg = *next = xcalloc(1, sizeof(struct call_argument)); arg->value = xcalloc(1, sizeof(struct expression)); *arg->value = (struct expression){ .type = EXPR_LITERAL, .result = param->type, .literal.array = NULL, }; return; } else if (param->default_value == NULL) { error(ctx, aexpr->loc, expr, "Not enough arguments for function call"); return; } } else if (aarg) { if (fntype->func.variadism != VARIADISM_C) { error(ctx, aexpr->loc, expr, "Too many arguments for function call"); return; } while (aarg) { arg = *next = xcalloc(1, sizeof(struct call_argument)); arg->value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aarg->expr, arg->value, NULL); aarg = aarg->next; next = &arg->next; } } } static void check_expr_cast(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_CAST; expr->cast.kind = aexpr->cast.kind; struct expression *value = expr->cast.value = xcalloc(1, sizeof(struct expression)); const struct type *secondary = expr->cast.secondary = type_store_lookup_atype(ctx, aexpr->cast.type); // TODO: Instead of allowing errors on casts to void, we should use a // different nonterminal check_expression(ctx, aexpr->cast.value, value, secondary == &builtin_type_void ? NULL : secondary); const struct type *primary = type_dealias(ctx, expr->cast.value->result); if (primary->storage == STORAGE_ERROR) { mkerror(aexpr->cast.value->loc, expr); return; } switch (aexpr->cast.kind) { case C_ASSERTION: case C_TEST: if (primary->storage == STORAGE_POINTER) { if (!(primary->pointer.flags & PTR_NULLABLE)) { error(ctx, aexpr->cast.value->loc, expr, "Expected a tagged union type or " "a nullable pointer"); return; } if (secondary->storage != STORAGE_NULL && (secondary->storage != STORAGE_POINTER || primary->pointer.referent != secondary->pointer.referent || (secondary->pointer.flags & PTR_NULLABLE))) { error(ctx, aexpr->cast.type->loc, expr, "Can only type assert nullable pointer into non-nullable pointer of the same type or null"); return; } break; } if (primary->storage != STORAGE_TAGGED) { error(ctx, aexpr->cast.value->loc, expr, "Expected a tagged union type or " "a nullable pointer"); return; } // secondary type must be a strict subset or a // member of the primary type if (!((tagged_subset_compat(ctx, primary, secondary) || tagged_select_subtype(ctx, primary, secondary, true)) && !tagged_subset_compat(ctx, secondary, primary))) { error(ctx, aexpr->cast.type->loc, expr, "Type is not a valid member of " "the tagged union type"); return; } break; case C_CAST:; const struct type *intermediary = type_is_castable(ctx, secondary, value->result); if (intermediary == NULL) { char *primarytypename = gen_typename(value->result); char *secondarytypename = gen_typename(secondary); error(ctx, aexpr->cast.type->loc, expr, "Invalid cast from %s to %s", primarytypename, secondarytypename); free(primarytypename); free(secondarytypename); return; } // intermediary type is required when casting to tagged union // whose member is an alias of primary type, since gen.c asserts // that the primary type is a direct member of the tagged union. // The value is first cast to an intermediary type which is a // direct member of the tagged union, before being cast to the // tagged union itself. expr->cast.value = lower_implicit_cast(ctx, intermediary, value); break; } expr->result = aexpr->cast.kind == C_TEST? &builtin_type_bool : secondary; } static void check_expr_array_literal(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { size_t len = 0; struct ast_expression_list *item = aexpr->literal.array.exprs; struct array_literal *cur, **next = &expr->literal.array; const struct type *type = NULL; if (hint) { hint = type_dealias(ctx, hint); size_t narray = 0; switch (hint->storage) { case STORAGE_ARRAY: case STORAGE_SLICE: type = hint->array.members; break; case STORAGE_TAGGED: for (const struct type_tagged_union *tu = &hint->tagged; tu; tu = tu->next) { const struct type *t = type_dealias(ctx, tu->type); if (t->storage == STORAGE_ARRAY || t->storage == STORAGE_SLICE) { hint = t; type = hint->array.members; ++narray; } } if (narray != 1) { type = hint = NULL; } break; default: hint = NULL; break; } } while (item) { struct expression *value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, item->expr, value, type); cur = *next = xcalloc(1, sizeof(struct array_literal)); cur->value = value; if (!type) { type = value->result; } else { if (!type_is_assignable(ctx, type, value->result)) { char *typename1 = gen_typename(type); char *typename2 = gen_typename(value->result); error(ctx, item->expr->loc, expr, "Array members must be of a uniform type, previously seen %s, but now see %s", typename1, typename2); free(typename1); free(typename2); return; } if (!hint) { // The promote_flexible in // type_is_assignable might've caused the // type to change out from under our feet type = expr->literal.array->value->result; } cur->value = lower_implicit_cast(ctx, type, cur->value); } item = item->next; next = &cur->next; ++len; } if (type == NULL) { error(ctx, aexpr->loc, expr, "Cannot infer array type from context, try casting it to the desired type"); return; } expr->result = type_store_lookup_array(ctx, aexpr->loc, type, len, aexpr->literal.array.expand); } static void check_expr_compound(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_COMPOUND; struct scope *scope = scope_push(&ctx->scope, SCOPE_COMPOUND); scope->hint = hint; expr->compound.scope = scope; if (aexpr->compound.label) { expr->compound.label = xstrdup(aexpr->compound.label); scope->label = xstrdup(aexpr->compound.label); } struct expressions *list = &expr->compound.exprs; struct expressions **next = &list->next; const struct ast_expression_list *alist = &aexpr->compound.list; struct expression *lexpr = NULL; while (alist) { lexpr = xcalloc(1, sizeof(struct expression)); check_expression(ctx, alist->expr, lexpr, NULL); if (type_has_error(ctx, lexpr->result)) { error(ctx, alist->expr->loc, lexpr, "Cannot ignore error here"); } list->expr = lexpr; alist = alist->next; if (alist) { *next = xcalloc(1, sizeof(struct expressions)); list = *next; next = &list->next; } if (alist && lexpr->result->storage == STORAGE_NEVER) { error(ctx, alist->expr->loc, expr, "Expression with result 'never' may not be followed by additional expressions"); } } if (lexpr->result->storage != STORAGE_NEVER) { // Add implicit `yield void` if control reaches end of compound // expression. struct type_tagged_union *result = xcalloc(1, sizeof(struct type_tagged_union)); result->type = &builtin_type_void; result->next = scope->results; scope->results = result; list->next = xcalloc(1, sizeof(struct expressions)); struct ast_expression *yexpr = xcalloc(1, sizeof(struct ast_expression)); yexpr->type = EXPR_YIELD; lexpr = xcalloc(1, sizeof(struct expression)); check_expression(ctx, yexpr, lexpr, NULL); list->next->expr = lexpr; } expr->result = type_store_reduce_result(ctx, aexpr->loc, scope->results); for (struct yield *yield = scope->yields; yield;) { *yield->expression = lower_implicit_cast(ctx, expr->result, *yield->expression); struct yield *next = yield->next; free(yield); yield = next; } assert(expr->result); scope_pop(&ctx->scope); } static void check_expr_literal(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_LITERAL; enum type_storage storage = aexpr->literal.storage; expr->result = builtin_type_for_storage(storage, false); switch (aexpr->literal.storage) { case STORAGE_ICONST: expr->result = type_create_flexible(storage, aexpr->literal.ival, aexpr->literal.ival); /* fallthrough */ case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: expr->literal.ival = aexpr->literal.ival; break; case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_SIZE: expr->literal.uval = aexpr->literal.uval; break; case STORAGE_RCONST: expr->result = type_create_flexible(storage, aexpr->literal.rune, aexpr->literal.rune); expr->literal.rune = aexpr->literal.rune; break; case STORAGE_BOOL: expr->literal.bval = aexpr->literal.bval; break; case STORAGE_NULL: case STORAGE_VOID: case STORAGE_DONE: // No storage break; case STORAGE_ARRAY: check_expr_array_literal(ctx, aexpr, expr, hint); break; case STORAGE_STRING: expr->literal.string.len = aexpr->literal.string.len; expr->literal.string.value = xcalloc(1, aexpr->literal.string.len); memcpy(expr->literal.string.value, aexpr->literal.string.value, aexpr->literal.string.len); break; case STORAGE_FCONST: expr->result = type_create_flexible(storage, aexpr->literal.fval, aexpr->literal.fval); // fallthrough case STORAGE_F32: case STORAGE_F64: expr->literal.fval = aexpr->literal.fval; break; case STORAGE_ENUM: case STORAGE_ERROR: case STORAGE_UINTPTR: case STORAGE_ALIAS: case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_POINTER: case STORAGE_RUNE: case STORAGE_SLICE: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_STRUCT: case STORAGE_UNION: case STORAGE_VALIST: assert(0); // Invariant } } static void check_expr_defer(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_DEFER; expr->result = &builtin_type_void; expr->defer.deferred = xcalloc(1, sizeof(struct expression)); expr->defer.scope = scope_push(&ctx->scope, SCOPE_DEFER); check_expression(ctx, aexpr->defer.deferred, expr->defer.deferred, NULL); if (type_has_error(ctx, expr->defer.deferred->result)) { error(ctx, aexpr->defer.deferred->loc, expr->defer.deferred, "Cannot ignore error here"); } scope_pop(&ctx->scope); } static void check_expr_delete(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_DELETE; expr->delete.is_static = aexpr->delete.is_static; expr->result = &builtin_type_void; struct expression *dexpr = expr->delete.expr = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->delete.expr, expr->delete.expr, NULL); const struct type *otype = NULL; switch (dexpr->type) { case EXPR_SLICE: otype = dexpr->slice.object->result; break; case EXPR_ACCESS: if (dexpr->access.type != ACCESS_INDEX) { error(ctx, aexpr->delete.expr->loc, expr, "Deleted expression must be slicing or indexing expression"); return; } struct expression *array = dexpr->access.array; if (array->type == EXPR_ACCESS && array->access.type == ACCESS_IDENTIFIER && array->access.object->flags & SO_FOR_EACH_SUBJECT) { error(ctx, aexpr->delete.expr->loc, expr, "cannot delete to subject of for-each loop"); } otype = dexpr->access.array->result; break; default: error(ctx, aexpr->delete.expr->loc, expr, "Deleted expression must be slicing or indexing expression"); return; } otype = type_dereference(ctx, otype); if (!otype) { error(ctx, aexpr->loc, expr, "Cannot dereference nullable pointer for delete expression"); return; } otype = type_dealias(ctx, otype); if (otype->storage != STORAGE_SLICE) { error(ctx, aexpr->delete.expr->loc, expr, "delete must operate on a slice"); return; } if (otype->flags & TYPE_CONST) { error(ctx, aexpr->delete.expr->loc, expr, "delete must operate on a mutable slice"); return; } } static void check_expr_control(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = aexpr->type; expr->result = &builtin_type_never; expr->control.label = aexpr->control.label; enum scope_class want; switch (expr->type) { case EXPR_BREAK: case EXPR_CONTINUE: want = SCOPE_LOOP; break; case EXPR_YIELD: want = SCOPE_COMPOUND; break; default: abort(); // Invariant } struct scope *scope = NULL; if (aexpr->control.label) { scope = scope_lookup_label(ctx->scope, aexpr->control.label); if (scope && scope->class != want) { error(ctx, aexpr->loc, expr, "Selected expression must%s be a loop", want == SCOPE_COMPOUND ? " not" : ""); } } else { scope = scope_lookup_class(ctx->scope, want); } if (!scope) { const char *msg; switch (expr->type) { case EXPR_BREAK: msg = "No eligible loop to break from"; break; case EXPR_CONTINUE: msg = "No eligible loop to continue to"; break; case EXPR_YIELD: msg = "No eligible expression to yield from"; break; default: assert(0); // Invariant } error(ctx, aexpr->loc, expr, msg); return; } struct scope *defer_scope = scope_lookup_class(ctx->scope, SCOPE_DEFER); if (defer_scope) { defer_scope = aexpr->control.label ? scope_lookup_label(defer_scope, aexpr->control.label) : scope_lookup_class(defer_scope, want); if (scope == defer_scope) { error(ctx, aexpr->loc, expr, "Cannot jump out of defer expression"); return; } } expr->control.scope = scope; if (expr->type == EXPR_BREAK) { scope->has_break = true; } if (expr->type != EXPR_YIELD) { return; } expr->control.value = xcalloc(1, sizeof(struct expression)); if (aexpr->control.value) { check_expression(ctx, aexpr->control.value, expr->control.value, scope->hint); } else { expr->control.value->type = EXPR_LITERAL; expr->control.value->result = &builtin_type_void; } struct type_tagged_union *result = xcalloc(1, sizeof(struct type_tagged_union)); result->type = expr->control.value->result; result->next = scope->results; scope->results = result; struct yield *yield = xcalloc(1, sizeof(struct yield)); yield->expression = &expr->control.value; yield->next = scope->yields; scope->yields = yield; } static void check_expr_for_accumulator(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint, struct scope *scope) { struct expression *bindings = NULL, *cond = NULL, *afterthought = NULL; if (aexpr->_for.bindings) { bindings = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->_for.bindings, bindings, NULL); if (bindings->result->storage == STORAGE_ERROR) { return; } assert(bindings->result->storage == STORAGE_VOID); expr->_for.bindings = bindings; } cond = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->_for.cond, cond, &builtin_type_bool); expr->_for.cond = cond; if (type_dealias(ctx, cond->result)->storage != STORAGE_BOOL && cond->result->storage != STORAGE_ERROR) { error(ctx, aexpr->_for.cond->loc, expr, "Expected for condition to be boolean"); return; } if (aexpr->_for.afterthought) { afterthought = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->_for.afterthought, afterthought, &builtin_type_void); if (type_has_error(ctx, afterthought->result)) { error(ctx, aexpr->_for.afterthought->loc, afterthought, "Cannot ignore error here"); } expr->_for.afterthought = afterthought; } struct expression *body = xcalloc(1, sizeof(struct expression)); expr->_for.body = body; check_expression(ctx, aexpr->_for.body, body, NULL); if (type_has_error(ctx, body->result)) { error(ctx, aexpr->_for.body->loc, body, "Cannot ignore error here"); } expr->_for.body = body; struct expression evaled; if (eval_expr(ctx, expr->_for.cond, &evaled)) { if (evaled.literal.bval && !scope->has_break) { expr->result = &builtin_type_never; } } } static void check_expr_for_each(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { struct expression *binding = xcalloc(1, sizeof(struct expression)); struct expression *initializer = xcalloc(1, sizeof(struct expression)); expr->_for.bindings = binding; binding->type = EXPR_BINDING; binding->result = &builtin_type_void; binding->binding.initializer = initializer; struct ast_expression_binding *abinding = &aexpr->_for.bindings->binding; const struct type *binding_type = NULL, *init_type_hint = NULL; if (abinding->type != NULL) { binding_type = type_store_lookup_atype(ctx, abinding->type); binding_type = type_store_lookup_with_flags(ctx, binding_type, binding_type->flags | abinding->flags); // Construct a type hint for the init expression. For example, // if the type hint is *int and we are in a &.., we would have // to do: *int -> int -> [_]int init_type_hint = binding_type; switch (expr->_for.kind) { case FOR_EACH_POINTER: init_type_hint = type_dealias(ctx, init_type_hint); if (init_type_hint->storage != STORAGE_POINTER) { error(ctx, aexpr->loc, expr, "Expected pointer type"); return; } init_type_hint = init_type_hint->pointer.referent; // fallthrough case FOR_EACH_VALUE: init_type_hint = type_store_lookup_array(ctx, aexpr->loc, init_type_hint, SIZE_UNDEFINED, false); break; case FOR_EACH_ITERATOR: { struct type_tagged_union *tags; struct type_tagged_union *done_tagged = xcalloc(1, sizeof(struct type_tagged_union)); done_tagged->type = &builtin_type_done; if (init_type_hint->storage == STORAGE_TAGGED) { tags = tagged_dup_tags(&init_type_hint->tagged); } else { tags = xcalloc(1, sizeof(struct type_tagged_union)); tags->type = binding_type; } tags->next = done_tagged; init_type_hint = type_store_lookup_tagged(ctx, aexpr->loc, tags); break; } default: break; } } check_expression(ctx, abinding->initializer, initializer, init_type_hint); const struct type *initializer_type = type_dealias(ctx, initializer->result); const struct type *var_type = binding_type; const struct type *initializer_result; switch (expr->_for.kind) { case FOR_EACH_POINTER: if (abinding->unpack) { error(ctx, abinding->initializer->loc, expr, "Cannot unpack tuple by pointer in for-each loop"); return; } // fallthrough case FOR_EACH_VALUE: initializer_type = type_dealias(ctx, type_dereference(ctx, initializer_type)); if (initializer_type->storage != STORAGE_ARRAY && initializer_type->storage != STORAGE_SLICE) { error(ctx, abinding->initializer->loc, initializer, "Expected array or slice"); return; } if (initializer_type->storage == STORAGE_ARRAY && initializer_type->size == SIZE_UNDEFINED) { error(ctx, abinding->initializer->loc, initializer, "Cannot iterate over array of undefined size"); return; } if (expr->_for.kind == FOR_EACH_VALUE) { initializer_result = initializer_type->array.members; } else { initializer_result = type_store_lookup_pointer(ctx, aexpr->loc, initializer_type->array.members, 0); } break; case FOR_EACH_ITERATOR: if (initializer_type->storage != STORAGE_TAGGED) { error(ctx, abinding->initializer->loc, initializer, "Expected tagged union"); return; } struct type_tagged_union *tags = tagged_dup_tags( &initializer_type->tagged); struct type_tagged_union *prev_tag = NULL; int done_tags_found = 0; // Reomve all done tags and aliases of it from the tagged // union. for (struct type_tagged_union *tu = tags; tu; tu = tu->next) { if (type_dealias(ctx, tu->type)->storage == STORAGE_DONE) { if (prev_tag != NULL) { prev_tag->next = tu->next; } else { tags = tu->next; } done_tags_found++; } prev_tag = tu; } if (done_tags_found != 1) { error(ctx, abinding->initializer->loc, initializer, "Tagged union must contain exactly one done type"); return; } initializer_result = type_store_reduce_result(ctx, abinding->initializer->loc, tags); break; default: abort(); } if (var_type == NULL) { var_type = initializer_result; } if (abinding->unpack != NULL) { create_unpack_bindings(ctx, var_type, initializer->loc, abinding->unpack, abinding->is_static, binding); } else { struct identifier ident = { .name = abinding->name, }; if (var_type->size == SIZE_UNDEFINED) { error(ctx, abinding->initializer->loc, binding, "Cannot create binding of undefined size"); return; } binding->binding.object = scope_insert(ctx->scope, O_BIND, &ident, &ident, var_type, NULL); } if (binding_type != NULL && !type_is_assignable(ctx, var_type, initializer_result)) { error(ctx, aexpr->loc, expr, "Initializer is not assignable to binding type"); return; } struct expression *body = xcalloc(1, sizeof(struct expression)); expr->_for.body = body; if (expr->_for.kind != FOR_EACH_ITERATOR && initializer->type == EXPR_ACCESS && initializer->access.type == ACCESS_IDENTIFIER) { initializer->access.object->flags |= SO_FOR_EACH_SUBJECT; check_expression(ctx, aexpr->_for.body, body, NULL); initializer->access.object->flags &= ~(SO_FOR_EACH_SUBJECT); } else { check_expression(ctx, aexpr->_for.body, body, NULL); } if (type_has_error(ctx, body->result)) { error(ctx, aexpr->_for.body->loc, body, "Cannot ignore error here"); } } static void check_expr_for(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_FOR; expr->result = &builtin_type_void; expr->_for.kind = aexpr->_for.kind; struct scope *scope = scope_push(&ctx->scope, SCOPE_LOOP); expr->_for.scope = scope; if (aexpr->_for.label) { expr->_for.label = xstrdup(aexpr->_for.label); scope->label = xstrdup(aexpr->_for.label); } switch (expr->_for.kind) { case FOR_ACCUMULATOR: check_expr_for_accumulator(ctx, aexpr, expr, hint, scope); break; case FOR_EACH_VALUE: case FOR_EACH_POINTER: case FOR_EACH_ITERATOR: check_expr_for_each(ctx, aexpr, expr, hint); break; } scope_pop(&ctx->scope); } static void check_expr_free(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { assert(aexpr->type == EXPR_FREE); expr->type = EXPR_FREE; expr->free.expr = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->free.expr, expr->free.expr, NULL); if (expr->free.expr->type == EXPR_ACCESS && expr->free.expr->access.type == ACCESS_IDENTIFIER && expr->free.expr->access.object->flags & SO_FOR_EACH_SUBJECT) { error(ctx, aexpr->free.expr->loc, expr, "cannot free to subject of for-each loop"); } enum type_storage storage = type_dealias(ctx, expr->free.expr->result)->storage; if (storage == STORAGE_ERROR) { mkerror(aexpr->loc, expr); return; } if (storage != STORAGE_SLICE && storage != STORAGE_STRING && storage != STORAGE_POINTER && storage != STORAGE_NULL) { error(ctx, aexpr->free.expr->loc, expr, "free must operate on slice, string, pointer, or null"); return; } expr->result = &builtin_type_void; } static void check_expr_if(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_IF; struct expression *cond, *true_branch, *false_branch = NULL; cond = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->_if.cond, cond, &builtin_type_bool); true_branch = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->_if.true_branch, true_branch, hint); const struct type *fresult = &builtin_type_void; if (aexpr->_if.false_branch) { false_branch = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->_if.false_branch, false_branch, hint); fresult = false_branch->result; } if (hint && type_is_assignable(ctx, hint, true_branch->result) && type_is_assignable(ctx, hint, fresult)) { expr->result = hint; } else { struct type_tagged_union _tags = { .type = fresult, .next = NULL, }, tags = { .type = true_branch->result, .next = &_tags, }; expr->result = type_store_reduce_result(ctx, aexpr->loc, &tags); if (expr->result == NULL) { error(ctx, aexpr->loc, expr, "Invalid result type (dangling or ambiguous null)"); return; } } true_branch = lower_implicit_cast(ctx, expr->result, true_branch); if (false_branch != NULL) { false_branch = lower_implicit_cast(ctx, expr->result, false_branch); } if (cond->result->storage == STORAGE_ERROR) { mkerror(aexpr->match.value->loc, expr); return; } if (type_dealias(ctx, cond->result)->storage != STORAGE_BOOL) { error(ctx, aexpr->_if.cond->loc, expr, "Expected if condition to be boolean"); return; } expr->_if.cond = cond; expr->_if.true_branch = true_branch; expr->_if.false_branch = false_branch; } static void check_expr_match(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_MATCH; struct expression *value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->match.value, value, NULL); expr->match.value = value; const struct type *type = type_dealias(ctx, value->result); if (type->storage == STORAGE_ERROR) { mkerror(aexpr->match.value->loc, expr); return; } bool is_ptr = type->storage == STORAGE_POINTER && type->pointer.flags & PTR_NULLABLE; if (type->storage != STORAGE_TAGGED && !is_ptr) { error(ctx, aexpr->match.value->loc, expr, "match value must be tagged union or nullable pointer type"); return; } struct type_tagged_union result_type = {0}; struct type_tagged_union *tagged = &result_type, **next_tag = &tagged->next; struct match_case **next = &expr->match.cases, *_case = NULL; for (struct ast_match_case *acase = aexpr->match.cases; acase; acase = acase->next) { _case = *next = xcalloc(1, sizeof(struct match_case)); next = &_case->next; const struct type *ctype = NULL; if (acase->type) { ctype = type_store_lookup_atype(ctx, acase->type); if (is_ptr) { switch (ctype->storage) { case STORAGE_NULL: break; case STORAGE_POINTER: if (type->pointer.referent != ctype->pointer.referent) { error(ctx, acase->type->loc, expr, "Match case on incompatible pointer type"); return; } break; default: error(ctx, acase->type->loc, expr, "Invalid type for match case (expected null or pointer type)"); return; } } else { // TODO: Assign a score to tagged compatibility // and choose the branch with the highest score. if (!type_is_assignable(ctx, type, ctype)) { error(ctx, acase->type->loc, expr, "Invalid type for match case (match is not assignable to this type)"); return; } } } if (acase->name) { assert(ctype); if (ctype->size == SIZE_UNDEFINED) { error(ctx, acase->type->loc, expr, "Cannot create binding for type of undefined size"); return; } if (ctype->storage == STORAGE_NULL) { error(ctx, aexpr->loc, expr, "Null is not a valid type for a binding"); return; } struct identifier ident = { .name = acase->name, }; struct scope *scope = scope_push( &ctx->scope, SCOPE_MATCH); _case->object = scope_insert(scope, O_BIND, &ident, &ident, ctype, NULL); } _case->value = xcalloc(1, sizeof(struct expression)); _case->type = ctype; // Lower to compound // TODO: This should probably be done in a more first-class way struct ast_expression compound = { .type = EXPR_COMPOUND, .compound = { .label = aexpr->match.label, .list = acase->exprs, }, }; check_expression(ctx, &compound, _case->value, hint); if (acase->name) { scope_pop(&ctx->scope); } if (expr->result == NULL) { expr->result = _case->value->result; tagged->type = expr->result; } else if (expr->result != _case->value->result) { tagged = *next_tag = xcalloc(1, sizeof(struct type_tagged_union)); next_tag = &tagged->next; tagged->type = _case->value->result; } } if (result_type.next) { if (hint) { expr->result = hint; } else { expr->result = type_store_reduce_result( ctx, aexpr->loc, &result_type); if (expr->result == NULL) { error(ctx, aexpr->loc, expr, "Invalid result type (dangling or ambiguous null)"); return; } } struct match_case *_case = expr->match.cases; struct ast_match_case *acase = aexpr->match.cases; while (_case) { if (!type_is_assignable(ctx, expr->result, _case->value->result)) { error(ctx, acase->exprs.expr->loc, expr, "Match case is not assignable to result type"); return; } _case->value = lower_implicit_cast(ctx, expr->result, _case->value); _case = _case->next; acase = acase->next; } struct type_tagged_union *tu = result_type.next; while (tu) { struct type_tagged_union *next = tu->next; free(tu); tu = next; } } } static void check_expr_measure(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->result = &builtin_type_size; switch (aexpr->measure.op) { case M_ALIGN: case M_SIZE: break; case M_LEN: expr->len.value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->measure.value, expr->len.value, NULL); const struct type *type = type_dereference(ctx, expr->len.value->result); if (!type) { error(ctx, aexpr->measure.value->loc, expr, "Cannot dereference nullable pointer for len"); return; } type = type_dealias(ctx, type); enum type_storage vstor = type->storage; bool valid = vstor == STORAGE_ARRAY || vstor == STORAGE_SLICE || vstor == STORAGE_STRING || vstor == STORAGE_ERROR; if (!valid) { char *typename = gen_typename(expr->len.value->result); error(ctx, aexpr->measure.value->loc, expr, "len argument must be of an array, slice, or str type, but got %s", typename); free(typename); return; } if (vstor == STORAGE_ARRAY) { if (type->array.length == SIZE_UNDEFINED) { error(ctx, aexpr->measure.value->loc, expr, "Cannot take length of unbounded array type"); return; } expr->type = EXPR_LITERAL; expr->result = &builtin_type_size; expr->literal.object = NULL; expr->literal.uval = type->array.length; return; } expr->type = EXPR_LEN; return; case M_OFFSET: expr->type = EXPR_LITERAL; if (aexpr->measure.value->type != EXPR_ACCESS) { error(ctx, aexpr->measure.value->loc, expr, "offset argument must be a field or tuple access"); return; } if (aexpr->measure.value->access.type != ACCESS_FIELD && aexpr->measure.value->access.type != ACCESS_TUPLE) { error(ctx, aexpr->measure.value->loc, expr, "offset argument must be a field or tuple access"); return; } struct expression *value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->measure.value, value, NULL); if (value->result->storage == STORAGE_ERROR) { return; } if (value->access.type == ACCESS_FIELD) { expr->literal.uval = value->access.field->offset; } else { assert(value->access.type == ACCESS_TUPLE); expr->literal.uval = value->access.tvalue->offset; } return; } expr->type = EXPR_LITERAL; struct errors **cur_err = ctx->next; struct dimensions dim = type_store_lookup_dimensions( ctx, aexpr->measure.type); if (ctx->next != cur_err) { mkerror(aexpr->measure.type->loc, expr); return; } struct ast_types *next = ctx->unresolved; ctx->unresolved = xcalloc(1, sizeof(struct ast_types)); ctx->unresolved->type = aexpr->measure.type; ctx->unresolved->next = next; if (aexpr->measure.op == M_ALIGN) { if (dim.align == ALIGN_UNDEFINED) { error(ctx, aexpr->measure.type->loc, expr, "Cannot take alignment of a type with undefined alignment"); return; } expr->literal.uval = dim.align; } else { if (dim.size == SIZE_UNDEFINED) { error(ctx, aexpr->measure.type->loc, expr, "Cannot take size of a type with undefined size"); return; } expr->literal.uval = dim.size; } } static void check_expr_propagate(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { struct expression *lvalue = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->propagate.value, lvalue, hint == &builtin_type_void ? NULL : hint); const struct type *intype = lvalue->result; if (intype->storage == STORAGE_ERROR) { mkerror(aexpr->loc, expr); return; } if (type_dealias(ctx, intype)->storage != STORAGE_TAGGED) { char *typename = gen_typename(intype); error(ctx, aexpr->loc, expr, "Cannot use error propagation on non-tagged type %s", typename); free(typename); return; } if (!aexpr->propagate.abort) { struct scope *defer = scope_lookup_class(ctx->scope, SCOPE_DEFER); if (defer) { error(ctx, aexpr->loc, expr, "Cannot use error propagation in a defer expression"); return; } } struct type_tagged_union result_tagged = {0}; struct type_tagged_union *tagged = &result_tagged, **next_tag = &tagged->next; struct type_tagged_union return_tagged = {0}; struct type_tagged_union *rtagged = &return_tagged, **next_rtag = &rtagged->next; const struct type_tagged_union *intu = &type_dealias(ctx, intype)->tagged; for (; intu; intu = intu->next) { if (intu->type->flags & TYPE_ERROR) { if (rtagged->type) { rtagged = *next_rtag = xcalloc(1, sizeof(struct type_tagged_union)); next_rtag = &rtagged->next; rtagged->type = intu->type; } else { rtagged->type = intu->type; } } else { if (tagged->type) { tagged = *next_tag = xcalloc(1, sizeof(struct type_tagged_union)); next_tag = &tagged->next; tagged->type = intu->type; } else { tagged->type = intu->type; } } } if (!return_tagged.type) { error(ctx, aexpr->loc, expr, "No error can occur here, cannot propagate"); return; } const struct type *return_type; if (return_tagged.next) { return_type = type_store_lookup_tagged( ctx, aexpr->loc, &return_tagged); } else { return_type = return_tagged.type; } const struct type *result_type; if (!result_tagged.type) { result_type = &builtin_type_never; } else if (result_tagged.next) { result_type = type_store_lookup_tagged( ctx, aexpr->loc, &result_tagged); } else { result_type = result_tagged.type; } // Lower to a match expression expr->type = EXPR_MATCH; expr->match.value = lvalue; struct scope *scope = scope_push(&ctx->scope, SCOPE_MATCH); struct match_case *case_ok = xcalloc(1, sizeof(struct match_case)); struct match_case *case_err = xcalloc(1, sizeof(struct match_case)); struct scope_object *ok_obj = NULL, *err_obj = NULL; if (result_type->size != SIZE_UNDEFINED) { struct identifier ok_name = { .name = gen_name(&ctx->id, "ok.%d"), }; ok_obj = scope_insert(scope, O_BIND, &ok_name, &ok_name, result_type, NULL); } case_ok->type = result_type; case_ok->object = ok_obj; case_ok->value = xcalloc(1, sizeof(struct expression)); case_ok->value->result = result_type; if (ok_obj) { case_ok->value->type = EXPR_ACCESS; case_ok->value->access.type = ACCESS_IDENTIFIER; case_ok->value->access.object = ok_obj; } else { case_ok->value->type = EXPR_LITERAL; } case_err->value = xcalloc(1, sizeof(struct expression)); if (aexpr->propagate.abort) { case_err->value->loc = expr->loc; case_err->value->type = EXPR_ASSERT; case_err->value->assert = (struct expression_assert){ .cond = NULL, .message = NULL, .fixed_reason = ABORT_PROPAGATE_ERROR_OCCURRED, }; } else { if (return_type->size != SIZE_UNDEFINED) { struct identifier err_name = { .name = gen_name(&ctx->id, "err.%d"), }; err_obj = scope_insert(scope, O_BIND, &err_name, &err_name, return_type, NULL); } case_err->type = return_type; case_err->object = err_obj; if (!type_is_assignable(ctx, ctx->fntype->func.result, return_type)) { char *res = gen_typename(ctx->fntype->func.result); char *ret = gen_typename(return_type); error(ctx, aexpr->loc, expr, "Error type %s is not assignable to function result type %s", ret, res); free(res); free(ret); return; } case_err->value->type = EXPR_RETURN; struct expression *rval = xcalloc(1, sizeof(struct expression)); rval->result = return_type; if (err_obj != NULL) { rval->type = EXPR_ACCESS; rval->access.type = ACCESS_IDENTIFIER; rval->access.object = err_obj; } else { rval->type = EXPR_LITERAL; } case_err->value->_return.value = lower_implicit_cast(ctx, ctx->fntype->func.result, rval); } case_err->value->result = &builtin_type_never; expr->match.cases = case_ok; case_ok->next = case_err; scope_pop(&ctx->scope); expr->result = result_type; } static void check_expr_return(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { struct scope *defer = scope_lookup_class(ctx->scope, SCOPE_DEFER); if (defer) { error(ctx, aexpr->loc, expr, "Cannot return inside a defer expression"); return; } if (ctx->fntype == NULL) { error(ctx, aexpr->loc, expr, "Cannot return outside a function body"); return; } expr->type = EXPR_RETURN; expr->result = &builtin_type_never; struct expression *rval = xcalloc(1, sizeof(struct expression)); if (aexpr->_return.value) { check_expression(ctx, aexpr->_return.value, rval, ctx->fntype->func.result); } else { rval->type = EXPR_LITERAL; rval->result = &builtin_type_void; } if (!type_is_assignable(ctx, ctx->fntype->func.result, rval->result)) { char *rettypename = gen_typename(rval->result); char *fntypename = gen_typename(ctx->fntype->func.result); error(ctx, aexpr->loc, expr, "Return value %s is not assignable to function result type %s", rettypename, fntypename); free(rettypename); free(fntypename); return; } expr->_return.value = lower_implicit_cast(ctx, ctx->fntype->func.result, rval); } static void slice_bounds_check(struct context *ctx, struct expression *expr) { const struct type *atype = type_dereference(ctx, expr->slice.object->result); const struct type *dtype = type_dealias(ctx, atype); struct expression start, end; enum { START = 1, END = 1 << 1, LENGTH = 1 << 2 } bounds = 0; if (expr->slice.start && eval_expr(ctx, expr->slice.start, &start)) { bounds |= START; } if (expr->slice.end && eval_expr(ctx, expr->slice.end, &end)) { bounds |= END; } if (dtype->storage == STORAGE_ARRAY && dtype->array.length != SIZE_UNDEFINED) { bounds |= LENGTH; } if ((bounds & (START | LENGTH)) == (START | LENGTH) && start.literal.uval > dtype->array.length) { error(ctx, expr->loc, expr, "Start index must not be greater than array length"); } if ((bounds & (START | END)) == (START | END) && start.literal.uval > end.literal.uval) { error(ctx, expr->loc, expr, "Start index must not be greater than end index"); } if ((bounds & (END | LENGTH)) == (END | LENGTH) && end.literal.uval > dtype->array.length) { error(ctx, expr->loc, expr, "End index must not be greater than array length"); } } static void check_expr_slice(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_SLICE; expr->slice.object = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->slice.object, expr->slice.object, NULL); if (expr->slice.object->result->storage == STORAGE_ERROR) { mkerror(aexpr->loc, expr); return; } const struct type *atype = type_dereference(ctx, expr->slice.object->result); if (!atype) { error(ctx, aexpr->slice.object->loc, expr, "Cannot dereference nullable pointer for slicing"); return; } const struct type *dtype = type_dealias(ctx, atype); if (dtype->storage != STORAGE_SLICE && dtype->storage != STORAGE_ARRAY) { error(ctx, aexpr->slice.object->loc, expr, "Cannot slice non-array, non-slice object"); return; } const struct type *itype; if (aexpr->slice.start) { expr->slice.start = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->slice.start, expr->slice.start, &builtin_type_size); itype = type_dealias(ctx, expr->slice.start->result); if (!type_is_integer(ctx, itype)) { error(ctx, aexpr->slice.start->loc, expr, "Cannot use non-integer %s type as slicing operand", type_storage_unparse(itype->storage)); return; } expr->slice.start = lower_implicit_cast(ctx, &builtin_type_size, expr->slice.start); } if (aexpr->slice.end) { expr->slice.end = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->slice.end, expr->slice.end, &builtin_type_size); itype = type_dealias(ctx, expr->slice.end->result); if (!type_is_integer(ctx, itype)) { error(ctx, aexpr->slice.end->loc, expr, "Cannot use non-integer %s type as slicing operand", type_storage_unparse(itype->storage)); return; } expr->slice.end = lower_implicit_cast(ctx, &builtin_type_size, expr->slice.end); } else if (dtype->storage == STORAGE_ARRAY && dtype->array.length == SIZE_UNDEFINED) { error(ctx, aexpr->loc, expr, "Must have end index on array of undefined length"); return; } slice_bounds_check(ctx, expr); if (dtype->storage == STORAGE_SLICE) { expr->result = atype; } else { expr->result = type_store_lookup_slice(ctx, aexpr->loc, dtype->array.members); } } static void check_struct_exhaustive(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *stype) { stype = type_dealias(ctx, stype); if (stype->storage == STORAGE_UNION) { return; } assert(stype->storage == STORAGE_STRUCT); struct struct_field *sf = stype->struct_union.fields; struct ast_field_value *af = aexpr->_struct.fields; // XXX: O(n^2)? while (sf) { bool found = false; for (struct ast_field_value *f = af; f; f = f->next) { if (!sf->name) { check_struct_exhaustive(ctx, aexpr, expr, sf->type); found = true; continue; } if (strcmp(f->name, sf->name) == 0) { if (found) { error(ctx, aexpr->loc, expr, "Field '%s' is initialized multiple times", sf->name); } found = true; } } if (!found && (!aexpr->_struct.autofill || !type_has_default(ctx, sf->type))) { error(ctx, aexpr->loc, expr, "Field '%s' is uninitialized", sf->name); } sf = sf->next; } } static void check_expr_struct(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_STRUCT; const struct type *stype = NULL; if (aexpr->_struct.type.name) { struct scope_object *obj = scope_lookup(ctx->scope, &aexpr->_struct.type); // resolve the unknown type wrap_resolver(ctx, obj, resolve_type); if (!obj) { error(ctx, aexpr->loc, expr, "Unknown type alias"); return; } if (obj->otype != O_TYPE) { error(ctx, aexpr->loc, expr, "Identifier does not refer to a type"); return; } stype = obj->type; enum type_storage storage = type_dealias(ctx, stype)->storage; if (storage != STORAGE_STRUCT && storage != STORAGE_UNION) { error(ctx, aexpr->loc, expr, "Object named is not a struct or union type"); return; } } struct ast_type satype = { .storage = STORAGE_STRUCT, .flags = TYPE_CONST, }; struct ast_struct_union_field *tfield = &satype.struct_union.fields; struct ast_struct_union_field **tnext = &tfield->next; struct expr_struct_field *sexpr, **snext = &expr->_struct.fields; expr->_struct.autofill = aexpr->_struct.autofill; if (stype == NULL && expr->_struct.autofill) { error(ctx, aexpr->loc, expr, "Autofill is only permitted for named struct initializers"); return; } struct ast_field_value *afield = aexpr->_struct.fields; while (afield) { const struct type *ftype; *snext = sexpr = xcalloc(1, sizeof(struct expr_struct_field)); snext = &sexpr->next; sexpr->value = xcalloc(1, sizeof(struct expression)); if (!stype) { assert(afield->name); // TODO if (!afield->type) { error(ctx, aexpr->loc, expr, "Unnamed struct must specify field type"); return; } tfield->name = afield->name; tfield->type = afield->type; ftype = type_store_lookup_atype(ctx, tfield->type); check_expression(ctx, afield->initializer, sexpr->value, ftype); if (afield->next) { *tnext = tfield = xcalloc( 1, sizeof(struct ast_struct_union_type)); tnext = &tfield->next; } } else { if (!afield->name) { error(ctx, afield->initializer->loc, expr, "Cannot embed a struct literal into " "a named struct literal"); return; } sexpr->field = type_get_field(ctx, type_dealias(ctx, stype), afield->name); if (!sexpr->field) { error(ctx, afield->initializer->loc, expr, "No field by this name exists for this type"); return; } ftype = sexpr->field->type; check_expression(ctx, afield->initializer, sexpr->value, ftype); if (!type_is_assignable(ctx, sexpr->field->type, sexpr->value->result)) { error(ctx, afield->initializer->loc, expr, "Initializer is not assignable to struct field"); return; } sexpr->value = lower_implicit_cast(ctx, sexpr->field->type, sexpr->value); } afield = afield->next; } if (stype) { expr->result = stype; check_struct_exhaustive(ctx, aexpr, expr, stype); } else { expr->result = type_store_lookup_atype(ctx, &satype); tfield = &satype.struct_union.fields; sexpr = expr->_struct.fields; while (tfield) { const struct struct_field *field = type_get_field(ctx, expr->result, tfield->name); if (!field) { // TODO: Use more specific error location error(ctx, aexpr->loc, expr, "No field by this name exists for this type"); return; } if (!type_is_assignable(ctx, field->type, sexpr->value->result)) { error(ctx, aexpr->loc, expr, "Cannot initialize struct field '%s' from value of this type", field->name); return; } sexpr->field = field; sexpr->value = lower_implicit_cast(ctx, field->type, sexpr->value); struct ast_struct_union_field *next = tfield->next; if (tfield != &satype.struct_union.fields) { free(tfield); } tfield = next; sexpr = sexpr->next; } } } static int casecmp(const void *_a, const void *_b) { const struct expression *a = *(const struct expression **)_a; const struct expression *b = *(const struct expression **)_b; assert(a->type == EXPR_LITERAL && b->type == EXPR_LITERAL); if (a->result->storage == STORAGE_ERROR) { return b->result->storage == STORAGE_ERROR ? 0 : 1; } else if (b->result->storage == STORAGE_ERROR) { return -1; } assert(type_dealias(NULL, a->result)->storage == type_dealias(NULL, b->result)->storage); if (type_is_signed(NULL, a->result)) { return a->literal.ival < b->literal.ival ? -1 : a->literal.ival > b->literal.ival ? 1 : 0; } else if (type_is_integer(NULL, a->result)) { return a->literal.uval < b->literal.uval ? -1 : a->literal.uval > b->literal.uval ? 1 : 0; } else if (type_dealias(NULL, a->result)->storage == STORAGE_POINTER) { const struct scope_object *obja = a->literal.object; const struct scope_object *objb = b->literal.object; if (obja != objb) { if (obja == NULL) { return -1; } else if (objb == NULL) { return 1; } uint32_t a = identifier_hash(FNV1A_INIT, &obja->name); uint32_t b = identifier_hash(FNV1A_INIT, &objb->name); assert(a != b); return a < b ? -1 : 1; } else { return a->literal.uval < b->literal.uval ? -1 : a->literal.uval > b->literal.uval ? 1 : 0; } } else if (type_dealias(NULL, a->result)->storage == STORAGE_STRING) { size_t len = a->literal.string.len < b->literal.string.len ? a->literal.string.len : b->literal.string.len; int ret = memcmp(a->literal.string.value, b->literal.string.value, len); if (ret != 0) { return ret; } return a->literal.string.len < b->literal.string.len ? -1 : a->literal.string.len > b->literal.string.len ? 1 : 0; } else if (type_dealias(NULL, a->result)->storage == STORAGE_BOOL) { return (int)a->literal.bval - (int)b->literal.bval; } else { assert(type_dealias(NULL, a->result)->storage == STORAGE_RCONST || type_dealias(NULL, a->result)->storage == STORAGE_RUNE); return a->literal.rune < b->literal.rune ? -1 : a->literal.rune > b->literal.rune ? 1 : 0; } } static size_t num_cases(struct context *ctx, const struct type *type) { type = type_dealias(ctx, type); switch (type->storage) { case STORAGE_BOOL: return 2; case STORAGE_STRING: return -1; case STORAGE_ENUM:; // XXX: O(n^2) size_t n = 0; struct scope_object *obj = type->_enum.values->objects; assert(obj != NULL); if (obj->otype == O_SCAN) { wrap_resolver(ctx, obj, resolve_enum_field); } for (; obj != NULL; obj = obj->lnext) { if (obj->otype == O_DECL) { continue; } assert(obj->otype == O_CONST); bool should_count = true; for (struct scope_object *other = obj->lnext; other != NULL; other = other->lnext) { if (other->otype == O_DECL) { continue; } if (other->otype == O_SCAN) { wrap_resolver(ctx, other, resolve_enum_field); } assert(other->otype == O_CONST); if (obj->value->literal.uval == other->value->literal.uval) { should_count = false; break; } } if (should_count) { n++; } } return n; default: assert(type_is_integer(ctx, type) || type->storage == STORAGE_POINTER || type->storage == STORAGE_RUNE); assert(!type_is_flexible(type)); if (type->size >= sizeof(size_t)) { return -1; } return (size_t)1 << (type->size * 8); } } static void check_expr_switch(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_SWITCH; struct expression *value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->_switch.value, value, NULL); const struct type *type = lower_flexible(ctx, value->result, NULL); expr->_switch.value = value; if (!type_is_integer(ctx, type) && type_dealias(ctx, type)->storage != STORAGE_POINTER && type_dealias(ctx, type)->storage != STORAGE_STRING && type_dealias(ctx, type)->storage != STORAGE_BOOL && type_dealias(ctx, type)->storage != STORAGE_RUNE) { error(ctx, aexpr->loc, expr, "Cannot switch on %s type", type_storage_unparse(type_dealias(ctx, type)->storage)); return; } struct type_tagged_union result_type = {0}; struct type_tagged_union *tagged = &result_type, **next_tag = &tagged->next; struct switch_case **next = &expr->_switch.cases, *_case = NULL; size_t n = 0; bool has_default_case = false; struct ast_switch_case *acase; for (acase = aexpr->_switch.cases; acase; acase = acase->next) { _case = *next = xcalloc(1, sizeof(struct switch_case)); next = &_case->next; _case->value = xcalloc(1, sizeof(struct expression)); if (acase->options == NULL) { if (has_default_case) { error(ctx, acase->exprs.expr->loc, _case->value, "Duplicate default case"); } has_default_case = true; } struct case_option *opt, **next_opt = &_case->options; for (const struct ast_case_option *aopt = acase->options; aopt; aopt = aopt->next) { opt = *next_opt = xcalloc(1, sizeof(struct case_option)); struct expression *value = xcalloc(1, sizeof(struct expression)); struct expression *evaled = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aopt->value, value, type); if (!type_is_assignable(ctx, type, value->result)) { error(ctx, aopt->value->loc, expr, "Invalid type for switch case"); return; } value = lower_implicit_cast(ctx, type, value); if (!eval_expr(ctx, value, evaled)) { error(ctx, aopt->value->loc, expr, "Unable to evaluate case at compile time"); return; } opt->value = evaled; next_opt = &opt->next; n++; } // Lower to compound // TODO: This should probably be done in a more first-class way struct ast_expression compound = { .type = EXPR_COMPOUND, .compound = { .label = aexpr->_switch.label, .list = acase->exprs, }, }; check_expression(ctx, &compound, _case->value, hint); if (expr->result == NULL) { expr->result = _case->value->result; tagged->type = expr->result; } else if (expr->result != _case->value->result) { tagged = *next_tag = xcalloc(1, sizeof(struct type_tagged_union)); next_tag = &tagged->next; tagged->type = _case->value->result; } } struct expression **cases_array = xcalloc(n, sizeof(struct expression *)); size_t i = 0; for (_case = expr->_switch.cases; _case; _case = _case->next) { for (const struct case_option *opt = _case->options; opt; opt = opt->next) { assert(i < n); cases_array[i] = opt->value; i++; } } assert(i == n); qsort(cases_array, n, sizeof(struct expression *), &casecmp); bool has_duplicate = false; for (size_t i = 1; i < n; i++) { if (cases_array[i]->result->storage == STORAGE_ERROR) { break; } const struct expression_literal *a = &cases_array[i - 1]->literal; const struct expression_literal *b = &cases_array[i]->literal; bool equal; if (type_is_integer(ctx, value->result)) { equal = a->uval == b->uval; } else if (type_dealias(ctx, value->result)->storage == STORAGE_POINTER) { equal = a->object == b->object && a->uval == b->uval; } else if (type_dealias(ctx, value->result)->storage == STORAGE_STRING) { equal = a->string.len == b->string.len && memcmp(a->string.value, b->string.value, a->string.len) == 0; } else if (type_dealias(ctx, value->result)->storage == STORAGE_BOOL) { equal = a->bval == b->bval; } else { assert(type_dealias(ctx, value->result)->storage == STORAGE_RCONST || type_dealias(ctx, value->result)->storage == STORAGE_RUNE); equal = a->rune == b->rune; } if (equal) { error(ctx, cases_array[i]->loc, cases_array[i], "Duplicate switch case"); has_duplicate = true; } } free(cases_array); if (!has_default_case && !has_duplicate && value->result->storage != STORAGE_ERROR && (n == (size_t)-1 || n != num_cases(ctx, value->result))) { error(ctx, aexpr->loc, value, "Switch expression isn't exhaustive"); } if (result_type.next) { if (hint) { expr->result = hint; } else { expr->result = type_store_reduce_result( ctx, aexpr->loc, &result_type); if (expr->result == NULL) { error(ctx, aexpr->loc, expr, "Invalid result type (dangling or ambiguous null)"); return; } } _case = expr->_switch.cases; acase = aexpr->_switch.cases; while (_case) { if (!type_is_assignable(ctx, expr->result, _case->value->result)) { error(ctx, acase->exprs.expr->loc, expr, "Switch case is not assignable to result type"); return; } _case->value = lower_implicit_cast(ctx, expr->result, _case->value); _case = _case->next; acase = acase->next; } struct type_tagged_union *tu = result_type.next; while (tu) { struct type_tagged_union *next = tu->next; free(tu); tu = next; } } } static void check_expr_tuple(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_TUPLE; const struct type_tuple *ttuple = NULL; if (hint && type_dealias(ctx, hint)->storage == STORAGE_TUPLE) { ttuple = &type_dealias(ctx, hint)->tuple; } struct type_tuple result = {0}; struct type_tuple *rtype = &result; struct expression_tuple *tuple = &expr->tuple; for (const struct ast_expression_tuple *atuple = &aexpr->tuple; atuple; atuple = atuple->next) { tuple->value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, atuple->expr, tuple->value, ttuple ? ttuple->type : NULL); rtype->type = tuple->value->result; if (atuple->next) { rtype->next = xcalloc(1, sizeof(struct type_tuple)); rtype = rtype->next; tuple->next = xcalloc(1, sizeof(struct expression_tuple)); tuple = tuple->next; } if (ttuple) { ttuple = ttuple->next; } } if (hint && type_dealias(ctx, hint)->storage == STORAGE_TUPLE) { expr->result = hint; } else if (hint && type_dealias(ctx, hint)->storage == STORAGE_TAGGED) { for (const struct type_tagged_union *tu = &type_dealias(ctx, hint)->tagged; tu; tu = tu->next) { if (type_dealias(ctx, tu->type)->storage != STORAGE_TUPLE) { continue; } const struct type_tuple *ttuple = &type_dealias(ctx, tu->type)->tuple; const struct expression_tuple *etuple = &expr->tuple; bool valid = true; while (etuple) { if (!ttuple || !type_is_assignable(ctx, ttuple->type, etuple->value->result)) { valid = false; break; } ttuple = ttuple->next; etuple = etuple->next; } if (!ttuple && valid) { expr->result = type_dealias(ctx, tu->type); break; } } if (!expr->result) { error(ctx, aexpr->loc, expr, "Tuple value is not assignable to tagged union hint"); return; } } else { expr->result = type_store_lookup_tuple(ctx, aexpr->loc, &result); if (expr->result == &builtin_type_error) { // an error occurred return; } } ttuple = &type_dealias(ctx, expr->result)->tuple; struct expression_tuple *etuple = &expr->tuple; const struct ast_expression_tuple *atuple = &aexpr->tuple; while (etuple) { if (!ttuple) { error(ctx, atuple->expr->loc, expr, "Too many values for tuple type"); return; } if (!type_is_assignable(ctx, ttuple->type, etuple->value->result)) { error(ctx, atuple->expr->loc, expr, "Value is not assignable to tuple value type"); return; } etuple->value = lower_implicit_cast(ctx, ttuple->type, etuple->value); etuple = etuple->next; atuple = atuple->next; ttuple = ttuple->next; } if (ttuple) { error(ctx, aexpr->loc, expr, "Too few values for tuple type"); return; } } static void check_expr_unarithm(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_UNARITHM; struct expression *operand = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->unarithm.operand, operand, NULL); expr->unarithm.operand = operand; expr->unarithm.op = aexpr->unarithm.op; if (operand->result->storage == STORAGE_ERROR) { mkerror(expr->unarithm.operand->loc, expr); return; } switch (expr->unarithm.op) { case UN_LNOT: if (type_dealias(ctx, operand->result)->storage != STORAGE_BOOL) { error(ctx, aexpr->unarithm.operand->loc, expr, "Cannot perform logical NOT (!) on non-boolean type"); return; } expr->result = &builtin_type_bool; break; case UN_BNOT: if (!type_is_integer(ctx, operand->result)) { error(ctx, aexpr->unarithm.operand->loc, expr, "Cannot perform binary NOT (~) on non-integer type"); return; } expr->result = operand->result; break; case UN_MINUS: if (!type_is_numeric(ctx, operand->result)) { error(ctx, aexpr->unarithm.operand->loc, expr, "Cannot perform operation on non-numeric type"); return; } if (operand->result->storage == STORAGE_ICONST) { // Not technically quite right, but we need // operand->result to be lowered with expr->result, and // this is correct enough const struct type *old = operand->result; const struct type *new = type_create_flexible( STORAGE_ICONST, -old->flexible.min, -old->flexible.max); lower_flexible(ctx, old, new); } expr->result = operand->result; break; case UN_ADDRESS:; const struct type *ptrhint = NULL; if (hint && type_dealias(ctx, hint)->storage == STORAGE_POINTER) { ptrhint = type_dealias(ctx, hint)->pointer.referent; if (type_dealias(ctx, ptrhint)->storage == STORAGE_OPAQUE) { ptrhint = NULL; } } if (type_is_flexible(operand->result) && ptrhint) { const struct type *promoted = promote_flexible(ctx, operand->result, ptrhint); if (promoted) { operand->result = promoted; } } else if (ptrhint) { // XXX: this is dumb, but we're gonna get rid of the // const flag anyway so it doesn't matter struct type stripped_result = *type_dealias(ctx, operand->result); stripped_result.flags &= ~TYPE_CONST; stripped_result.id = type_hash(&stripped_result); struct type stripped_ptrhint = *type_dealias(ctx, ptrhint); stripped_ptrhint.flags &= ~TYPE_CONST; stripped_ptrhint.id = type_hash(&stripped_ptrhint); if (stripped_result.id == stripped_ptrhint.id) { operand->result = ptrhint; } } expr->result = type_store_lookup_pointer( ctx, aexpr->loc, operand->result, 0); break; case UN_DEREF: if (type_dealias(ctx, operand->result)->storage != STORAGE_POINTER) { error(ctx, aexpr->unarithm.operand->loc, expr, "Cannot de-reference non-pointer type"); return; } if (type_dealias(ctx, operand->result)->pointer.flags & PTR_NULLABLE) { error(ctx, aexpr->unarithm.operand->loc, expr, "Cannot dereference nullable pointer type"); return; } if (type_dealias(ctx, operand->result)->pointer.referent->size == SIZE_UNDEFINED) { error(ctx, aexpr->unarithm.operand->loc, expr, "Cannot dereference pointer to type of undefined size"); return; } expr->result = type_dealias(ctx, operand->result)->pointer.referent; break; } } static void check_expr_vastart(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { if (ctx->fntype->func.variadism != VARIADISM_C) { error(ctx, aexpr->loc, expr, "Cannot use vastart within function which does not use C-style variadism"); return; } expr->type = EXPR_VASTART; expr->result = &builtin_type_valist; } static void check_expr_vaarg(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_VAARG; if (hint == NULL) { error(ctx, aexpr->loc, expr, "Cannot infer type of vaarg without hint"); return; } expr->vaarg.ap = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->vaarg.ap, expr->vaarg.ap, &builtin_type_valist); if (type_dealias(ctx, expr->vaarg.ap->result)->storage != STORAGE_VALIST) { error(ctx, aexpr->loc, expr, "Expected vaarg operand to be valist"); return; } expr->result = hint; } static void check_expr_vaend(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->type = EXPR_VAEND; expr->vaarg.ap = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aexpr->vaarg.ap, expr->vaarg.ap, &builtin_type_valist); if (type_dealias(ctx, expr->vaarg.ap->result)->storage != STORAGE_VALIST) { error(ctx, aexpr->loc, expr, "Expected vaend operand to be valist"); return; } expr->result = &builtin_type_void; } void check_expression(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, const struct type *hint) { expr->loc = aexpr->loc; switch (aexpr->type) { case EXPR_ACCESS: check_expr_access(ctx, aexpr, expr, hint); break; case EXPR_ALLOC: check_expr_alloc(ctx, aexpr, expr, hint); break; case EXPR_APPEND: check_expr_append_insert(ctx, aexpr, expr, hint); break; case EXPR_ASSERT: check_expr_assert(ctx, aexpr, expr, hint); break; case EXPR_ASSIGN: check_expr_assign(ctx, aexpr, expr, hint); break; case EXPR_BINARITHM: check_expr_binarithm(ctx, aexpr, expr, hint); break; case EXPR_BINDING: case EXPR_DEFINE: check_expr_binding(ctx, aexpr, expr, hint); break; case EXPR_BREAK: case EXPR_CONTINUE: case EXPR_YIELD: check_expr_control(ctx, aexpr, expr, hint); break; case EXPR_CALL: check_expr_call(ctx, aexpr, expr, hint); break; case EXPR_CAST: check_expr_cast(ctx, aexpr, expr, hint); break; case EXPR_COMPOUND: check_expr_compound(ctx, aexpr, expr, hint); break; case EXPR_LITERAL: check_expr_literal(ctx, aexpr, expr, hint); break; case EXPR_DEFER: check_expr_defer(ctx, aexpr, expr, hint); break; case EXPR_DELETE: check_expr_delete(ctx, aexpr, expr, hint); break; case EXPR_FOR: check_expr_for(ctx, aexpr, expr, hint); break; case EXPR_FREE: check_expr_free(ctx, aexpr, expr, hint); break; case EXPR_IF: check_expr_if(ctx, aexpr, expr, hint); break; case EXPR_INSERT: check_expr_append_insert(ctx, aexpr, expr, hint); break; case EXPR_MATCH: check_expr_match(ctx, aexpr, expr, hint); break; case EXPR_MEASURE: check_expr_measure(ctx, aexpr, expr, hint); break; case EXPR_PROPAGATE: check_expr_propagate(ctx, aexpr, expr, hint); break; case EXPR_RETURN: check_expr_return(ctx, aexpr, expr, hint); break; case EXPR_SLICE: check_expr_slice(ctx, aexpr, expr, hint); break; case EXPR_STRUCT: check_expr_struct(ctx, aexpr, expr, hint); break; case EXPR_SWITCH: check_expr_switch(ctx, aexpr, expr, hint); break; case EXPR_TUPLE: check_expr_tuple(ctx, aexpr, expr, hint); break; case EXPR_UNARITHM: check_expr_unarithm(ctx, aexpr, expr, hint); break; case EXPR_VAARG: check_expr_vaarg(ctx, aexpr, expr, hint); break; case EXPR_VAEND: check_expr_vaend(ctx, aexpr, expr, hint); break; case EXPR_VASTART: check_expr_vastart(ctx, aexpr, expr, hint); break; } assert(expr->result); flexible_refer(expr->result, &expr->result); } static void append_decl(struct context *ctx, struct declaration *decl) { struct declarations *decls = xcalloc(1, sizeof(struct declarations)); decls->decl = *decl; decls->next = ctx->decls; ctx->decls = decls; } static void resolve_unresolved(struct context *ctx) { while (ctx->unresolved) { struct ast_types *unresolved = ctx->unresolved; ctx->unresolved = unresolved->next; type_store_lookup_atype(ctx, unresolved->type); free(unresolved); } } void check_function(struct context *ctx, const struct scope_object *obj, const struct ast_decl *adecl) { const struct ast_function_decl *afndecl = &adecl->function; ctx->fntype = obj->type; if (ctx->fntype->storage == STORAGE_ERROR) { return; } struct declaration _decl, *decl = &_decl; decl->decl_type = DECL_FUNC; decl->func.type = obj->type; decl->func.flags = afndecl->flags; decl->exported = adecl->exported; decl->file = adecl->loc.file; decl->symbol = ident_to_sym(&obj->ident); mkident(ctx, &decl->ident, &afndecl->ident, NULL); if (!adecl->function.body) { if (decl->func.flags != 0) { error(ctx, adecl->loc, NULL, "Function attributes cannot be used on prototypes"); return; } decl->func.body = NULL; goto end; // Prototype } if (afndecl->symbol != NULL && decl->func.flags != 0) { error(ctx, adecl->loc, NULL, "@symbol cannot be used alongside other function attributes"); } decl->func.scope = scope_push(&ctx->scope, SCOPE_FUNC); struct ast_function_parameters *params = afndecl->prototype.params; while (params) { if (!params->name) { error(ctx, params->loc, NULL, "Function parameters must be named"); return; } struct identifier ident = { .name = params->name, }; const struct type *type = type_store_lookup_atype( ctx, params->type); if (obj->type->func.variadism == VARIADISM_HARE && !params->next) { type = type_store_lookup_slice(ctx, params->loc, type); } scope_insert(decl->func.scope, O_BIND, &ident, &ident, type, NULL); params = params->next; } // TODO: Add function name to errors if (decl->func.flags != 0) { const char *flag = NULL; switch (decl->func.flags) { case FN_INIT: flag = "@init"; break; case FN_FINI: flag = "@fini"; break; case FN_TEST: flag = "@test"; break; default: error(ctx, adecl->loc, NULL, "Only one of @init, @fini, or @test may be used in a function declaration"); break; } if (obj->type->func.result != &builtin_type_void) { error(ctx, adecl->loc, NULL, "%s function must return void", flag); } if (decl->exported) { error(ctx, adecl->loc, NULL, "%s function cannot be exported", flag); } if (afndecl->prototype.params) { error(ctx, adecl->loc, NULL, "%s function cannot have parameters", flag); } } struct expression *body = xcalloc(1, sizeof(struct expression)); check_expression(ctx, afndecl->body, body, obj->type->func.result); resolve_unresolved(ctx); if (!type_is_assignable(ctx, obj->type->func.result, body->result)) { char *restypename = gen_typename(body->result); char *fntypename = gen_typename(obj->type->func.result); error(ctx, afndecl->body->loc, body, "Result value %s is not assignable to function result type %s", restypename, fntypename); free(restypename); free(fntypename); return; } decl->func.body = lower_implicit_cast(ctx, obj->type->func.result, body); scope_pop(&ctx->scope); ctx->fntype = NULL; end: if ((adecl->function.flags & FN_TEST) && !ctx->is_test) { return; } append_decl(ctx, decl); } static struct incomplete_declaration * incomplete_declaration_create(struct context *ctx, struct location loc, struct scope *scope, const struct identifier *ident, const struct identifier *name) { struct scope *subunit = ctx->unit->parent; ctx->unit->parent = NULL; struct incomplete_declaration *idecl = (struct incomplete_declaration *)scope_lookup(scope, name); ctx->unit->parent = subunit; if (idecl) { error_norec(ctx, loc, "Duplicate global identifier '%s'", identifier_unparse(ident)); } idecl = xcalloc(1, sizeof(struct incomplete_declaration)); scope_object_init((struct scope_object *)idecl, O_SCAN, ident, name, NULL, NULL); scope_insert_from_object(scope, (struct scope_object *)idecl); return idecl; } static void scan_enum_field(struct context *ctx, struct scope *imports, struct scope *enum_scope, const struct type *etype, struct ast_enum_field *f) { // We have to process the last field first // This way, objects in enum_scope will have lnext pointing to // the previous element, which is important for implicit enum values. if (f->next) { scan_enum_field(ctx, imports, enum_scope, etype, f->next); } assert(etype->storage == STORAGE_ENUM); struct incomplete_enum_field *field = xcalloc(1, sizeof(struct incomplete_enum_field)); *field = (struct incomplete_enum_field){ .field = f, .enum_scope = enum_scope, }; struct identifier localname = { .name = (char *)f->name, }; struct identifier name = { .name = (char *)f->name, .ns = (struct identifier *)&etype->alias.name, }; struct incomplete_declaration *fld = incomplete_declaration_create(ctx, f->loc, enum_scope, &name, &localname); fld->type = IDECL_ENUM_FLD; fld->imports = imports; fld->obj.type = etype, fld->field = field; } static void check_hosted_main(struct context *ctx, struct location loc, const struct ast_decl *decl, struct identifier ident, const char *symbol) { if (*ctx->mainsym == '\0' || ctx->is_test) { return; } if (symbol != NULL) { if (strcmp(symbol, ctx->mainsym) != 0) { return; } } else { if (strcmp(ident.name, "main") != 0 || ident.ns != NULL) { return; } } const struct ast_function_decl *func; if (decl && decl->decl_type == ADECL_FUNC) { func = &decl->function; if (func->flags != 0) { return; } } else { error(ctx, loc, NULL, "main must be a function in hosted environment"); return; } if (func->body != NULL && !decl->exported) { error(ctx, loc, NULL, "main must be exported in hosted environment"); return; } if (func->prototype.params != NULL) { error(ctx, loc, NULL, "main must not have parameters in hosted environment"); return; } if (func->prototype.result->storage != STORAGE_VOID) { error(ctx, loc, NULL, "main must return void in hosted environment"); return; } } static void scan_types(struct context *ctx, struct scope *imp, const struct ast_decl *decl) { for (const struct ast_type_decl *t = &decl->type; t; t = t->next) { struct identifier with_ns = {0}; mkident(ctx, &with_ns, &t->ident, NULL); check_hosted_main(ctx, decl->loc, NULL, with_ns, NULL); struct incomplete_declaration *idecl = incomplete_declaration_create(ctx, decl->loc, ctx->scope, &with_ns, &t->ident); idecl->decl = (struct ast_decl){ .decl_type = ADECL_TYPE, .loc = decl->loc, .type = *t, .exported = decl->exported, }; idecl->imports = imp; if (t->type->storage == STORAGE_ENUM) { bool exported = idecl->decl.exported; const struct type *type = type_store_lookup_enum( ctx, t->type, exported); if (type->storage == STORAGE_ERROR) { return; // error occured } scope_push((struct scope **)&type->_enum.values, SCOPE_ENUM); scan_enum_field(ctx, imp, type->_enum.values, type, t->type->_enum.values); type->_enum.values->parent = ctx->defines; idecl->obj.otype = O_TYPE; idecl->obj.type = type; append_decl(ctx, &(struct declaration){ .decl_type = DECL_TYPE, .file = decl->loc.file, .ident = idecl->obj.ident, .exported = exported, .type = type, }); } else { idecl->type = IDECL_DECL; } } } static void unexported_type_error(struct context *ctx, struct location loc, const struct type *type) { char *s = gen_typename(type); error(ctx, loc, NULL, "Can't use unexported type %s in exported declaration", s); free(s); } static void check_exported_type(struct context *ctx, struct location loc, const struct type *type) { switch (type->storage) { case STORAGE_ALIAS: case STORAGE_ENUM: if (!type->alias.exported) { unexported_type_error(ctx, loc, type); } break; case STORAGE_ARRAY: case STORAGE_SLICE: check_exported_type(ctx, loc, type->array.members); break; case STORAGE_FUNCTION: for (const struct type_func_param *param = type->func.params; param; param = param->next) { check_exported_type(ctx, loc, param->type); } check_exported_type(ctx, loc, type->func.result); break; case STORAGE_POINTER: check_exported_type(ctx, loc, type->pointer.referent); break; case STORAGE_STRUCT: case STORAGE_UNION: for (const struct struct_field *field = type->struct_union.fields; field; field = field->next) { check_exported_type(ctx, loc, field->type); } break; case STORAGE_TAGGED: for (const struct type_tagged_union *t = &type->tagged; t; t = t->next) { check_exported_type(ctx, loc, t->type); } break; case STORAGE_TUPLE: for (const struct type_tuple *t = &type->tuple; t; t = t->next) { check_exported_type(ctx, loc, t->type); } break; case STORAGE_BOOL: case STORAGE_DONE: case STORAGE_F32: case STORAGE_F64: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_I8: case STORAGE_INT: case STORAGE_NEVER: case STORAGE_NULL: case STORAGE_OPAQUE: case STORAGE_RUNE: case STORAGE_SIZE: case STORAGE_STRING: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_U8: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_VOID: case STORAGE_VALIST: case STORAGE_FCONST: case STORAGE_ICONST: case STORAGE_RCONST: case STORAGE_ERROR: break; } } static void resolve_const(struct context *ctx, struct incomplete_declaration *idecl) { const struct ast_global_decl *decl = &idecl->decl.constant; assert(!decl->symbol); // Invariant const struct type *type = NULL; if (decl->type) { type = type_store_lookup_atype(ctx, decl->type); } struct expression *init = xcalloc(1, sizeof(struct expression)), *value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, decl->init, init, type); if (!decl->type) { type = init->result; if (type->storage == STORAGE_NULL) { error(ctx, decl->init->loc, value, "Null is not a valid type for a constant"); type = &builtin_type_error; goto end; } } if (idecl->decl.exported) { struct location loc = decl->type ? decl->type->loc : decl->init->loc; check_exported_type(ctx, loc, type); } if (!type_is_assignable(ctx, type, init->result)) { char *typename1 = gen_typename(init->result); char *typename2 = gen_typename(type); error(ctx, decl->init->loc, value, "Initializer type %s is not assignable to constant type %s", typename1, typename2); free(typename1); free(typename2); type = &builtin_type_error; goto end; } if (decl->type) { if (decl->type->storage == STORAGE_ARRAY && decl->type->array.contextual) { type = lower_flexible(ctx, init->result, NULL); } else { init = lower_implicit_cast(ctx, type, init); } } if (!eval_expr(ctx, init, value)) { error(ctx, decl->init->loc, value, "Unable to evaluate initializer at compile time"); type = &builtin_type_error; goto end; } end: idecl->obj.otype = O_CONST; idecl->obj.value = value; if (!ctx->defines || ctx->errors) { return; } struct scope_object *shadow_obj = scope_lookup(ctx->defines, &idecl->obj.ident); if (shadow_obj && &idecl->obj != shadow_obj) { // Shadowed by define if (type_is_flexible(value->result) || type_is_flexible(shadow_obj->value->result)) { const struct type *promoted = promote_flexible(ctx, value->result, shadow_obj->value->result); if (promoted == NULL) { const char *msg; char *typename = NULL; if (!type_is_flexible(value->result)) { msg = "Constant of type %s is shadowed by define of incompatible flexible type"; typename = gen_typename(value->result); } else if (!type_is_flexible(shadow_obj->value->result)) { msg = "Constant of flexible type is shadowed by define of incompatible type %s"; typename = gen_typename(shadow_obj->value->result); } else { msg = "Constant of flexible type is shadowed by define of incompatible flexible type"; } error(ctx, idecl->decl.loc, NULL, msg, typename); free(typename); } else { shadow_obj->value = lower_implicit_cast(ctx, promoted, shadow_obj->value); } } else if (value->result != shadow_obj->value->result) { char *typename = gen_typename(value->result); char *shadow_typename = gen_typename(shadow_obj->value->result); error(ctx, idecl->decl.loc, NULL, "Constant of type %s is shadowed by define of incompatible type %s", typename, shadow_typename); free(typename); free(shadow_typename); } idecl->obj.value = shadow_obj->value; } append_decl(ctx, &(struct declaration){ .decl_type = DECL_CONST, .file = idecl->decl.loc.file, .ident = idecl->obj.ident, .exported = idecl->decl.exported, .constant = { .type = type, .value = value, } }); } void resolve_function(struct context *ctx, struct incomplete_declaration *idecl) { const struct ast_function_decl *decl = &idecl->decl.function; const struct ast_type fn_atype = { .loc = idecl->decl.loc, .storage = STORAGE_FUNCTION, .flags = 0, .func = decl->prototype, }; const struct type *fntype = type_store_lookup_atype(ctx, &fn_atype); if (idecl->decl.exported) { check_exported_type(ctx, idecl->decl.loc, fntype); } idecl->obj.otype = O_DECL; idecl->obj.type = fntype; } void resolve_global(struct context *ctx, struct incomplete_declaration *idecl) { const struct ast_global_decl *decl = &idecl->decl.global; const struct type *type = NULL; struct identifier name = {0}; bool context = false; struct expression *init, *value = NULL; if (decl->type) { type = type_store_lookup_atype(ctx, decl->type); context = decl->type->storage == STORAGE_ARRAY && decl->type->array.contextual; if (context && !decl->init) { error(ctx, decl->type->loc, NULL, "Cannot infer array length without an init"); type = &builtin_type_error; goto end; } } if (decl->init) { init = xcalloc(1, sizeof(struct expression)); value = xcalloc(1, sizeof(struct expression)); check_expression(ctx, decl->init, init, type); if (type) { if (!type_is_assignable(ctx, type, init->result)) { char *typename1 = gen_typename(init->result); char *typename2 = gen_typename(type); error(ctx, decl->init->loc, value, "Initializer type %s is not assignable to global type %s", typename1, typename2); free(typename1); free(typename2); type = &builtin_type_error; goto end; } } else { type = lower_flexible(ctx, init->result, NULL); } if (context) { type = init->result; } else { init = lower_implicit_cast(ctx, type, init); } if (type->size == SIZE_UNDEFINED) { error(ctx, decl->init->loc, NULL, "Cannot initialize object with undefined size"); type = &builtin_type_error; goto end; } assert(type->size != SIZE_UNDEFINED); if (type->storage == STORAGE_NULL) { error(ctx, decl->init->loc, NULL, "Null is not a valid type for a global"); type = &builtin_type_error; goto end; } if (!eval_expr(ctx, init, value)) { error(ctx, decl->init->loc, value, "Unable to evaluate initializer at compile time"); type = &builtin_type_error; goto end; } } if (idecl->decl.exported) { struct location loc = decl->type ? decl->type->loc : decl->init->loc; check_exported_type(ctx, loc, type); } mkident(ctx, &name, &idecl->obj.name, NULL); end: idecl->obj.otype = O_DECL; idecl->obj.type = type; if (decl->threadlocal) { idecl->obj.flags |= SO_THREADLOCAL; } append_decl(ctx, &(struct declaration){ .decl_type = DECL_GLOBAL, .file = idecl->decl.loc.file, .ident = name, .symbol = ident_to_sym(&idecl->obj.ident), .exported = idecl->decl.exported, .global = { .type = type, .value = value, .threadlocal = idecl->decl.global.threadlocal, } }); } static void resolve_enum_field(struct context *ctx, struct incomplete_declaration *idecl) { assert(idecl->type == IDECL_ENUM_FLD); const struct type *type = idecl->obj.type; struct identifier localname = { .name = idecl->obj.ident.name }; struct scope_object *new = scope_lookup(idecl->field->enum_scope, &localname); if (new != &idecl->obj) { wrap_resolver(ctx, new, resolve_enum_field); assert(new->otype == O_CONST); idecl->obj.otype = O_CONST; idecl->obj.value = new->value; return; } ctx->scope = idecl->field->enum_scope; struct expression *value = xcalloc(1, sizeof(struct expression)); value->result = type; if (idecl->field->field->value) { // explicit value struct expression *initializer = xcalloc(1, sizeof(struct expression)); check_expression(ctx, idecl->field->field->value, initializer, type->alias.type); if (!type_is_assignable(ctx, type->alias.type, initializer->result)) { char *inittypename = gen_typename(initializer->result); char *builtintypename = gen_typename(type->alias.type); error_norec(ctx, idecl->field->field->value->loc, "Enum value type (%s) is not assignable from initializer type (%s) for value %s", builtintypename, inittypename, idecl->obj.ident.name); } initializer = lower_implicit_cast(ctx, type, initializer); if (!eval_expr(ctx, initializer, value)) { error_norec(ctx, idecl->field->field->value->loc, "Unable to evaluate constant initializer at compile time"); } } else { // implicit value struct scope_object *obj = idecl->obj.lnext; // find previous enum value wrap_resolver(ctx, obj, resolve_enum_field); value->type = EXPR_LITERAL; if (type_is_signed(ctx, type_dealias(ctx, type))) { if (obj == NULL) { value->literal.ival = 0; } else { value->literal.ival = obj->value->literal.ival + 1; } } else { if (obj == NULL) { value->literal.uval = 0; } else { value->literal.uval = obj->value->literal.uval + 1; } } } idecl->obj.otype = O_CONST; idecl->obj.value = value; } const struct type * lookup_enum_type(struct context *ctx, const struct scope_object *obj) { const struct type *enum_type = NULL; switch (obj->otype) { case O_SCAN: { struct incomplete_declaration *idecl = (struct incomplete_declaration *)obj; if (idecl->in_progress) { // Type alias cycle will be handled in check return NULL; } if (idecl->type != IDECL_DECL || idecl->decl.decl_type != ADECL_TYPE) { return NULL; } if (idecl->decl.type.type->storage == STORAGE_ENUM) { assert(false); } else if (idecl->decl.type.type->storage == STORAGE_ALIAS) { ctx->scope->parent = idecl->imports; const struct identifier *alias = &idecl->decl.type.type->alias; const struct scope_object *new = scope_lookup(ctx->scope, alias); if (new) { idecl->in_progress = true; enum_type = lookup_enum_type(ctx, new); idecl->in_progress = false; } } break; } case O_TYPE: enum_type = obj->type; break; default: return NULL; } if (!enum_type) { return NULL; } enum_type = type_dealias(ctx, enum_type); if (enum_type->storage != STORAGE_ENUM) { return NULL; } return enum_type; } static void scan_enum_field_aliases(struct context *ctx, struct scope_object *obj) { const struct type *enum_type = lookup_enum_type(ctx, obj); if (!enum_type) { return; } // orig->type is (perhaps transitively) an alias of a resolved enum // type, which means its dependency graph is a linear chain of // resolved types ending with that enum, so we can immediately resolve it wrap_resolver(ctx, obj, resolve_type); for (const struct scope_object *val = enum_type->_enum.values->objects; val; val = val->lnext) { struct identifier name = { .name = val->name.name, .ns = (struct identifier *)&obj->name, }; struct identifier ident = { .name = val->name.name, .ns = (struct identifier *)&obj->ident, }; struct ast_enum_field *afield = xcalloc(1, sizeof(struct ast_enum_field)); *afield = (struct ast_enum_field){ .loc = (struct location){0}, // XXX: what to put here? .name = xstrdup(val->name.name), }; struct incomplete_enum_field *field = xcalloc(1, sizeof(struct incomplete_enum_field)); struct incomplete_declaration *idecl = (struct incomplete_declaration *)val; *field = (struct incomplete_enum_field){ .field = afield, .enum_scope = idecl->field->enum_scope, }; idecl = incomplete_declaration_create(ctx, (struct location){0}, ctx->scope, &ident, &name); idecl->type = IDECL_ENUM_FLD; idecl->obj.type = obj->type; idecl->field = field; } } void resolve_dimensions(struct context *ctx, struct incomplete_declaration *idecl) { if (idecl->type != IDECL_DECL || idecl->decl.decl_type != ADECL_TYPE) { struct location loc; if (idecl->type == IDECL_ENUM_FLD) { loc = idecl->field->field->loc; } else { loc = idecl->decl.loc; } char *ident = identifier_unparse(&idecl->obj.name); error(ctx, loc, NULL, "'%s' is not a type", ident); free(ident); idecl->obj.type = &builtin_type_error; return; } struct dimensions dim = type_store_lookup_dimensions(ctx, idecl->decl.type.type); idecl->obj.type = xcalloc(1, sizeof(struct type)); *(struct type *)idecl->obj.type = (struct type){ .size = dim.size, .align = dim.align, }; } void resolve_type(struct context *ctx, struct incomplete_declaration *idecl) { struct location loc; if (idecl->type == IDECL_ENUM_FLD) { loc = idecl->field->field->loc; } else { loc = idecl->decl.loc; } if (idecl->type != IDECL_DECL || idecl->decl.decl_type != ADECL_TYPE) { error_norec(ctx, loc, "'%s' is not a type", identifier_unparse(&idecl->obj.name)); } // 1. compute type dimensions struct errors **cur_err = ctx->next; struct dimensions dim = type_store_lookup_dimensions( ctx, idecl->decl.type.type); idecl->in_progress = false; // 2. compute type representation and store it struct type _alias = { .storage = STORAGE_ALIAS, .alias = { .ident = idecl->obj.ident, .name = idecl->obj.name, .type = NULL, .exported = idecl->decl.exported, }, .size = dim.size, .align = dim.align, .flags = idecl->decl.type.type->flags, }; struct type *alias = (struct type *)type_store_lookup_alias( ctx, &_alias, &dim); idecl->obj.otype = O_TYPE; idecl->obj.type = alias; if (ctx->next == cur_err) { alias->alias.type = type_store_lookup_atype( ctx, idecl->decl.type.type); } else { alias->alias.type = &builtin_type_error; } assert(alias->alias.type != NULL); if (idecl->decl.exported) { check_exported_type(ctx, idecl->decl.type.type->loc, alias->alias.type); } if (alias->alias.type->storage == STORAGE_NEVER) { error(ctx, loc, NULL, "Can't declare type alias of never"); alias->alias.type = &builtin_type_error; } if (alias->alias.type->storage == STORAGE_DONE && (alias->alias.type->flags & TYPE_ERROR) == TYPE_ERROR) { error(ctx, loc, NULL, "Built-in type done cannot be an error type"); alias->alias.type = &builtin_type_error; } append_decl(ctx, &(struct declaration){ .decl_type = DECL_TYPE, .file = idecl->decl.loc.file, .ident = idecl->obj.ident, .exported = idecl->decl.exported, .type = alias, }); } static struct incomplete_declaration * scan_const(struct context *ctx, struct scope *imports, bool exported, struct location loc, const struct ast_global_decl *decl) { struct identifier with_ns = {0}; mkident(ctx, &with_ns, &decl->ident, NULL); check_hosted_main(ctx, loc, NULL, with_ns, NULL); struct incomplete_declaration *idecl = incomplete_declaration_create(ctx, loc, ctx->scope, &with_ns, &decl->ident); idecl->type = IDECL_DECL; idecl->decl = (struct ast_decl){ .decl_type = ADECL_CONST, .loc = loc, .constant = *decl, .exported = exported, }; idecl->imports = imports; return idecl; } static void scan_decl(struct context *ctx, struct scope *imports, const struct ast_decl *decl) { struct incomplete_declaration *idecl = {0}; struct identifier ident = {0}; switch (decl->decl_type) { case ADECL_CONST: for (const struct ast_global_decl *g = &decl->constant; g; g = g->next) { scan_const(ctx, imports, decl->exported, decl->loc, g); } break; case ADECL_GLOBAL: for (const struct ast_global_decl *g = &decl->global; g; g = g->next) { mkident(ctx, &ident, &g->ident, g->symbol); check_hosted_main(ctx, decl->loc, NULL, ident, g->symbol); idecl = incomplete_declaration_create(ctx, decl->loc, ctx->scope, &ident, &g->ident); idecl->type = IDECL_DECL; idecl->decl = (struct ast_decl){ .decl_type = ADECL_GLOBAL, .loc = decl->loc, .global = *g, .exported = decl->exported, }; idecl->imports = imports; } break; case ADECL_FUNC:; const struct ast_function_decl *func = &decl->function; const struct identifier *name = NULL; if (func->flags) { const char *template = NULL; if (func->flags & FN_TEST) { template = "testfunc.%d"; } else if (func->flags & FN_INIT) { template = "initfunc.%d"; } else if (func->flags & FN_FINI) { template = "finifunc.%d"; } assert(template); ident.name = gen_name(&ctx->id, template); ++ctx->id; name = &ident; } else { mkident(ctx, &ident, &func->ident, func->symbol); name = &func->ident; } idecl = incomplete_declaration_create(ctx, decl->loc, ctx->scope, &ident, name); check_hosted_main(ctx, decl->loc, decl, ident, func->symbol); idecl->type = IDECL_DECL; idecl->decl = (struct ast_decl){ .decl_type = ADECL_FUNC, .loc = decl->loc, .function = *func, .exported = decl->exported, }; idecl->imports = imports; break; case ADECL_TYPE: scan_types(ctx, imports, decl); break; case ADECL_ASSERT:; static uint64_t num = 0; int n = snprintf(NULL, 0, "static assert %" PRIu64, num); ident.name = xcalloc(n + 1, sizeof(char)); snprintf(ident.name, n + 1, "static assert %" PRIu64, num); ++num; idecl = incomplete_declaration_create(ctx, decl->loc, ctx->scope, &ident, &ident); idecl->type = IDECL_DECL; idecl->decl = (struct ast_decl){ .decl_type = ADECL_ASSERT, .loc = decl->loc, .assert = decl->assert, .exported = decl->exported, }; idecl->imports = imports; break; } } static void resolve_decl(struct context *ctx, struct incomplete_declaration *idecl) { switch (idecl->type) { case IDECL_ENUM_FLD: resolve_enum_field(ctx, idecl); return; case IDECL_DECL: break; } switch (idecl->decl.decl_type) { case ADECL_CONST: resolve_const(ctx, idecl); return; case ADECL_GLOBAL: resolve_global(ctx, idecl); return; case ADECL_FUNC: resolve_function(ctx, idecl); return; case ADECL_TYPE: resolve_type(ctx, idecl); return; case ADECL_ASSERT:; struct expression expr = {0}; check_assert(ctx, idecl->decl.assert, idecl->decl.loc, &expr); return; } abort(); } void wrap_resolver(struct context *ctx, struct scope_object *obj, resolvefn resolver) { // ensure this declaration wasn't already scanned if (!obj || obj->otype != O_SCAN) { return; } // save current subunit and enum context struct scope *scope = ctx->scope; struct scope *subunit = ctx->unit->parent; ctx->unit->parent = NULL; const struct type *fntype = ctx->fntype; ctx->fntype = NULL; struct ast_types *unresolved = ctx->unresolved; ctx->unresolved = NULL; struct incomplete_declaration *idecl = (struct incomplete_declaration *)obj; // load this declaration's subunit context ctx->scope = ctx->defines; ctx->unit->parent = idecl->imports; // resolving a declaration that is already in progress -> cycle if (idecl->in_progress) { struct location loc; if (idecl->type == IDECL_ENUM_FLD) { loc = idecl->field->field->loc; } else { loc = idecl->decl.loc; } error_norec(ctx, loc, "Circular dependency for '%s'", identifier_unparse(&idecl->obj.name)); } idecl->in_progress = true; resolver(ctx, idecl); idecl->in_progress = false; resolve_unresolved(ctx); // load stored context ctx->unresolved = unresolved; ctx->fntype = fntype; ctx->unit->parent = subunit; ctx->scope = scope; } static void load_import(struct context *ctx, const struct ast_global_decl *defines, struct ast_imports *import, struct scope *scope) { struct scope *mod = module_resolve(ctx, defines, &import->ident); if (import->mode == IMPORT_MEMBERS) { for (const struct ast_import_members *member = import->members; member; member = member->next) { struct identifier name = { .name = member->name, }; struct identifier ident = { .name = member->name, .ns = &import->ident, }; const struct scope_object *obj = scope_lookup(mod, &ident); if (!obj) { error_norec(ctx, member->loc, "Unknown object '%s'", identifier_unparse(&ident)); } assert(obj->otype != O_SCAN); // obj->type and obj->value are a union, so it doesn't // matter which is passed into scope_insert struct scope_object *new = scope_insert( scope, obj->otype, &obj->ident, &name, obj->type, NULL); new->flags = obj->flags; if (obj->otype != O_TYPE || type_dealias(ctx, obj->type)->storage != STORAGE_ENUM) { continue; } const struct scope *enum_scope = type_dealias(ctx, obj->type)->_enum.values; for (const struct scope_object *o = enum_scope->objects; o; o = o->lnext) { struct identifier value_ident = { .name = o->name.name, .ns = &ident, }; struct identifier value_name = { .name = o->name.name, .ns = &name, }; scope_insert(scope, o->otype, &value_ident, &value_name, NULL, o->value); } } return; } struct identifier _ident = {0}; struct identifier *prefix = &_ident; switch (import->mode) { case IMPORT_NORMAL: prefix->name = import->ident.name; break; case IMPORT_ALIAS: prefix->name = import->alias; break; case IMPORT_WILDCARD: prefix = NULL; break; case IMPORT_MEMBERS: assert(0); // Unreachable } for (const struct scope_object *obj = mod->objects; obj; obj = obj->lnext) { assert(obj->otype != O_SCAN); struct scope_object *new; if (import->mode == IMPORT_NORMAL) { // obj->type and obj->value are a union, so it doesn't // matter which is passed into scope_insert new = scope_insert(scope, obj->otype, &obj->ident, &obj->name, obj->type, NULL); new->flags = obj->flags; } struct identifier ns, name = { .name = obj->name.name, .ns = prefix, }; if (obj->name.ns == NULL) { // this is only possible if an invalid .td file is used. // this check is necessary since the scope_lookup below // will segfault if obj->name.ns is NULL error_norec(ctx, (struct location){0}, "Invalid typedefs for %s", identifier_unparse(&import->ident)); } const struct scope_object *_enum = scope_lookup(mod, obj->name.ns); if (_enum != NULL && _enum->otype == O_TYPE && type_dealias(NULL, _enum->type)->storage == STORAGE_ENUM) { // include enum type in identifier if object is an enum // constant ns = (struct identifier){ .name = obj->name.ns->name, .ns = prefix, }; name.ns = &ns; } // obj->type and obj->value are a union, so it doesn't matter // which is passed into scope_insert new = scope_insert(scope, obj->otype, &obj->ident, &name, obj->type, NULL); new->flags = obj->flags; } } static const struct location defineloc = { .file = 0, .lineno = 1, .colno = 1, }; struct scope * check_internal(type_store *ts, struct modcache **cache, bool is_test, const char *mainsym, const struct ast_global_decl *defines, const struct ast_unit *aunit, struct unit *unit, bool scan_only) { struct context ctx = {0}; ctx.ns = unit->ns; ctx.is_test = is_test; ctx.mainsym = mainsym; ctx.store = ts; ctx.next = &ctx.errors; ctx.modcache = cache; // Top-level scope management involves: // // - Creating a top-level scope for the whole unit, to which // declarations are added. // - Creating a scope for each sub-unit, and populating it with imports. // // Further down the call frame, subsequent functions will create // sub-scopes for each declaration, expression-list, etc. // Put defines into a temporary scope (-D on the command line) sources[0] = "-D"; ctx.scope = NULL; ctx.unit = scope_push(&ctx.scope, SCOPE_DEFINES); for (const struct ast_global_decl *def = defines; def; def = def->next) { struct incomplete_declaration *idecl = scan_const(&ctx, NULL, false , defineloc, def); resolve_const(&ctx, idecl); } ctx.defines = ctx.scope; ctx.scope = NULL; ctx.defines->parent = ctx.unit = scope_push(&ctx.scope, SCOPE_UNIT); sources[0] = ""; // Populate the imports and put declarations into a scope. // Each declaration holds a reference to its subunit's imports // A scope gets us: // a) duplicate detection for free // b) a way to find declaration's definition when it's refered to struct scopes *subunit_scopes = NULL, **next = &subunit_scopes; struct scope *su_scope = NULL; struct identifiers **inext = &unit->imports; for (const struct ast_subunit *su = &aunit->subunits; su; su = su->next) { su_scope = NULL; scope_push(&su_scope, SCOPE_SUBUNIT); for (struct ast_imports *imports = su->imports; imports; imports = imports->next) { load_import(&ctx, defines, imports, su_scope); bool found = false; for (struct identifiers *uimports = unit->imports; uimports; uimports = uimports->next) { if (identifier_eq(&uimports->ident, &imports->ident)) { found = true; break; } } if (!found) { struct identifiers *uimport = *inext = xcalloc(1, sizeof(struct identifiers)); identifier_dup(&uimport->ident, &imports->ident); inext = &uimport->next; } } for (struct ast_decls *d = su->decls; d; d = d->next) { scan_decl(&ctx, su_scope, &d->decl); } *next = xcalloc(1, sizeof(struct scopes)); (*next)->scope = su_scope; next = &(*next)->next; } // Find enum aliases and store them in incomplete enum value declarations for (struct scope_object *obj = ctx.scope->objects; obj; obj = obj->lnext) { scan_enum_field_aliases(&ctx, obj); } // XXX: shadowed declarations are not checked for consistency ctx.scope = ctx.defines; for (const struct scope_object *obj = ctx.scope->objects; obj; obj = obj->lnext) { const struct scope_object *shadowed_obj = scope_lookup(ctx.unit, &obj->name); if (!shadowed_obj) { continue; } if (shadowed_obj->otype == O_CONST) { continue; } if (shadowed_obj->otype == O_SCAN) { const struct incomplete_declaration *idecl = (struct incomplete_declaration *)shadowed_obj; if (idecl->type == IDECL_DECL && idecl->decl.decl_type == ADECL_CONST) { continue; } } error(&ctx, defineloc, NULL, "Define shadows a non-define object"); } // Perform actual declaration resolution for (struct scope_object *obj = ctx.unit->objects; obj; obj = obj->lnext) { wrap_resolver(&ctx, obj, resolve_decl); // populate the expression graph struct incomplete_declaration *idecl = (struct incomplete_declaration *)obj; if (idecl->type == IDECL_DECL && idecl->decl.decl_type == ADECL_FUNC) { ctx.unit->parent = idecl->imports; check_function(&ctx, &idecl->obj, &idecl->decl); } } assert(ctx.unresolved == NULL); handle_errors(ctx.errors); unit->declarations = ctx.decls; if (!(scan_only || unit->declarations)) { xfprintf(stderr, "Error: module contains no declarations\n"); exit(EXIT_CHECK); } ctx.unit->parent = NULL; return ctx.unit; } struct scope * check(type_store *ts, bool is_test, const char *mainsym, const struct ast_global_decl *defines, const struct ast_unit *aunit, struct unit *unit) { struct modcache *modcache[MODCACHE_BUCKETS] = {0}; return check_internal(ts, modcache, is_test, mainsym, defines, aunit, unit, false); } harec-0.24.2/src/emit.c000066400000000000000000000176771464473277600146200ustar00rootroot00000000000000#include #include #include #include #include #include "check.h" #include "emit.h" #include "qbe.h" #include "types.h" #include "util.h" static void emit_qtype(const struct qbe_type *type, bool aggr, FILE *out) { assert(type); switch (type->stype) { case Q_BYTE: case Q_HALF: case Q_WORD: case Q_LONG: case Q_SINGLE: case Q_DOUBLE: xfprintf(out, "%c", (char)type->stype); break; case Q__AGGREGATE: case Q__UNION: if (aggr) { xfprintf(out, ":%s", type->name); } else { xfprintf(out, "l"); } break; case Q__VOID: break; // no-op } } static void qemit_type(const struct qbe_def *def, FILE *out) { assert(def->kind == Q_TYPE); const struct qbe_type *qtype = &def->type; const struct type *base = qtype->base; if (base) { char *tn = gen_typename(base); xfprintf(out, "# %s [id: %" PRIu32 "; size: ", tn, base->id); free(tn); if (base->size != SIZE_UNDEFINED) { xfprintf(out, "%zu]\n", base->size); } else { xfprintf(out, "undefined]\n"); } xfprintf(out, "type :%s =", def->name); if (base->align != ALIGN_UNDEFINED) { xfprintf(out, " align %zu", base->align); } } else { xfprintf(out, "type :%s =", def->name); } xfprintf(out, " {"); const struct qbe_field *field = &qtype->fields; while (field) { if (qtype->stype == Q__UNION) { xfprintf(out, " {"); } if (field->type) { xfprintf(out, " "); emit_qtype(field->type, true, out); } if (field->count) { xfprintf(out, " %zu", field->count); } if (qtype->stype == Q__UNION) { xfprintf(out, " }"); } else if (field->next) { xfprintf(out, ","); } field = field->next; } xfprintf(out, " }\n\n"); } static void emit_const(const struct qbe_value *val, FILE *out) { switch (val->type->stype) { case Q_BYTE: case Q_HALF: case Q_WORD: case Q_SINGLE: xfprintf(out, "%" PRIu32, val->wval); break; case Q_LONG: case Q_DOUBLE: xfprintf(out, "%" PRIu64, val->lval); break; case Q__VOID: case Q__AGGREGATE: case Q__UNION: assert(0); // Invariant } } static void emit_value(const struct qbe_value *val, FILE *out) { switch (val->kind) { case QV_CONST: emit_const(val, out); break; case QV_GLOBAL: if (val->threadlocal) { xfprintf(out, "thread "); } xfprintf(out, "$%s", val->name); break; case QV_LABEL: xfprintf(out, "@%s", val->name); break; case QV_TEMPORARY: xfprintf(out, "%%%s", val->name); break; case QV_VARIADIC: xfprintf(out, "..."); break; } } static void emit_call(const struct qbe_statement *stmt, FILE *out) { xfprintf(out, "%s ", qbe_instr[stmt->instr]); const struct qbe_arguments *arg = stmt->args; assert(arg); emit_value(&arg->value, out); xfprintf(out, "("); arg = arg->next; bool comma = false; while (arg) { xfprintf(out, "%s", comma ? ", " : ""); if (arg->value.kind != QV_VARIADIC) { emit_qtype(arg->value.type, true, out); xfprintf(out, " "); } emit_value(&arg->value, out); arg = arg->next; comma = true; } xfprintf(out, ")\n"); } static void emit_stmt(const struct qbe_statement *stmt, FILE *out) { switch (stmt->type) { case Q_COMMENT: xfprintf(out, "\t# %s\n", stmt->comment); break; case Q_INSTR: xfprintf(out, "\t"); if (stmt->instr == Q_CALL) { if (stmt->out != NULL) { emit_value(stmt->out, out); xfprintf(out, " ="); emit_qtype(stmt->out->type, true, out); xfprintf(out, " "); } emit_call(stmt, out); break; } if (stmt->out != NULL) { emit_value(stmt->out, out); xfprintf(out, " ="); emit_qtype(stmt->out->type, false, out); xfprintf(out, " "); } xfprintf(out, "%s%s", qbe_instr[stmt->instr], stmt->args ? " " : ""); const struct qbe_arguments *arg = stmt->args; while (arg) { xfprintf(out, "%s", arg == stmt->args ? "" : ", "); emit_value(&arg->value, out); arg = arg->next; } xfprintf(out, "\n"); break; case Q_LABEL: xfprintf(out, "@%s\n", stmt->label); break; } } static void emit_func(const struct qbe_def *def, FILE *out) { assert(def->kind == Q_FUNC); xfprintf(out, "section \".text.%s\" \"ax\"%s\nfunction", def->name, def->exported ? " export" : ""); if (def->func.returns->stype != Q__VOID) { xfprintf(out, " "); emit_qtype(def->func.returns, true, out); } xfprintf(out, " $%s(", def->name); const struct qbe_func_param *param = def->func.params; while (param) { emit_qtype(param->type, true, out); xfprintf(out, " %%%s", param->name); if (param->next || def->func.variadic) { xfprintf(out, ", "); } param = param->next; } if (def->func.variadic) { xfprintf(out, "..."); } xfprintf(out, ") {\n"); for (size_t i = 0; i < def->func.prelude.ln; ++i) { const struct qbe_statement *stmt = &def->func.prelude.stmts[i]; emit_stmt(stmt, out); } for (size_t i = 0; i < def->func.body.ln; ++i) { const struct qbe_statement *stmt = &def->func.body.stmts[i]; emit_stmt(stmt, out); } xfprintf(out, "}\n\n"); } static void emit_data_string(const char *str, size_t sz, FILE *out) { bool q = false; for (size_t i = 0; i < sz; ++i) { /* XXX: We could stand to emit less conservatively */ if (!isprint((unsigned char)(str[i])) || str[i] == '"' || str[i] == '\\') { if (q) { q = false; xfprintf(out, "\", "); } xfprintf(out, "b %d%s", str[i], i + 1 < sz ? ", " : ""); } else { if (!q) { q = true; xfprintf(out, "b \""); } xfprintf(out, "%c", str[i]); } } if (q) { xfprintf(out, "\""); } } static bool is_zeroes(const struct qbe_data_item *data) { for (const struct qbe_data_item *cur = data; cur; cur = cur->next) { switch (cur->type) { case QD_ZEROED: break; case QD_VALUE: switch (cur->value.kind) { case QV_CONST: if (cur->value.type->size < sizeof(uint64_t)) { if (cur->value.wval != 0) { return false; } } else { if (cur->value.lval != 0) { return false; } } break; case QV_GLOBAL: case QV_LABEL: case QV_TEMPORARY: return false; case QV_VARIADIC: abort(); } break; case QD_STRING: for (size_t i = 0; i < cur->sz; ++i) { if (cur->str[i] != 0) { return false; } } break; case QD_SYMOFFS: return false; } } return true; } static void emit_data(const struct qbe_def *def, FILE *out) { assert(def->kind == Q_DATA); if (def->data.section && def->data.secflags) { xfprintf(out, "section \"%s\" \"%s\"", def->data.section, def->data.secflags); } else if (def->data.section) { xfprintf(out, "section \"%s\"", def->data.section); } else if (def->data.threadlocal) { if (is_zeroes(&def->data.items)) { xfprintf(out, "section \".tbss\" \"awT\""); } else { xfprintf(out, "section \".tdata\" \"awT\""); } } else if (is_zeroes(&def->data.items)) { xfprintf(out, "section \".bss.%s\"", def->name); } else { xfprintf(out, "section \".data.%s\"", def->name); } xfprintf(out, "%s\ndata $%s = ", def->exported ? " export" : "", def->name); if (def->data.align != ALIGN_UNDEFINED) { xfprintf(out, "align %zu ", def->data.align); } xfprintf(out, "{ "); const struct qbe_data_item *item = &def->data.items; while (item) { switch (item->type) { case QD_VALUE: emit_qtype(item->value.type, true, out); xfprintf(out, " "); emit_value(&item->value, out); break; case QD_ZEROED: xfprintf(out, "z %zu", item->zeroed); break; case QD_STRING: emit_data_string(item->str, item->sz, out); break; case QD_SYMOFFS: // XXX: ARCH xfprintf(out, "l $%s + %" PRIi64, item->sym, item->offset); break; } xfprintf(out, item->next ? ", " : " "); item = item->next; } xfprintf(out, "}\n\n"); } static void emit_def(const struct qbe_def *def, FILE *out) { xfprintf(out, "dbgfile \"%s\"\n", sources[def->file]); switch (def->kind) { case Q_TYPE: qemit_type(def, out); break; case Q_FUNC: emit_func(def, out); break; case Q_DATA: emit_data(def, out); break; } } void emit(const struct qbe_program *program, FILE *out) { const struct qbe_def *def = program->defs; while (def) { emit_def(def, out); def = def->next; } } harec-0.24.2/src/eval.c000066400000000000000000001012021464473277600145630ustar00rootroot00000000000000#include #include #include #include #include #include "check.h" #include "eval.h" #include "expr.h" #include "scope.h" #include "type_store.h" #include "types.h" #include "util.h" static bool eval_access(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { struct expression tmp = {0}; switch (in->access.type) { case ACCESS_IDENTIFIER: return false; // &ident handled in eval_unarithm case ACCESS_INDEX: if (!eval_expr(ctx, in->access.array, &tmp)) { return false; } const struct array_literal *array = tmp.literal.array; if (!eval_expr(ctx, in->access.index, &tmp)) { return false; } for (size_t i = tmp.literal.uval; i > 0; --i) { if (array == NULL) { error(ctx, in->loc, NULL, "slice or array access out of bounds"); return false; } array = array->next; } return eval_expr(ctx, array->value, out); case ACCESS_FIELD: if (!eval_expr(ctx, in->access._struct, &tmp)) { return false; } const struct struct_literal *fields = tmp.literal._struct; for (; fields != NULL; fields = fields->next) { if (!strcmp(fields->field->name, in->access.field->name)) { break; } } if (fields == NULL) { return false; } return eval_expr(ctx, fields->value, out); case ACCESS_TUPLE: if (!eval_expr(ctx, in->access.tuple, &tmp)) { return false; } const struct tuple_literal *tuple = tmp.literal.tuple; for (size_t i = in->access.tindex; i > 0; --i) { if (tuple == NULL) { // out of bounds return false; } tuple = tuple->next; } return eval_expr(ctx, tuple->value, out); } return true; } static uint64_t itrunc(struct context *ctx, const struct type *type, uint64_t val) { switch (type->storage) { case STORAGE_U8: return (uint8_t)val; case STORAGE_U16: return (uint16_t)val; case STORAGE_U32: case STORAGE_RCONST: case STORAGE_RUNE: return (uint32_t)val; case STORAGE_U64: return (uint64_t)val; case STORAGE_I8: return (int8_t)val; case STORAGE_I16: return (int16_t)val; case STORAGE_I32: return (int32_t)val; case STORAGE_I64: return (int64_t)val; case STORAGE_INT: return (int)val; case STORAGE_UINT: return (unsigned int)val; case STORAGE_ARRAY: case STORAGE_ICONST: case STORAGE_POINTER: case STORAGE_SIZE: case STORAGE_UINTPTR: return val; case STORAGE_BOOL: return (bool)val; case STORAGE_NULL: return (uintptr_t)NULL; case STORAGE_ALIAS: return itrunc(ctx, type_dealias(ctx, type), val); case STORAGE_ENUM: return itrunc(ctx, type->alias.type, val); case STORAGE_ERROR: return val; case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: assert(0); } assert(0); } static double ftrunc(struct context *ctx, const struct type *type, double val) { if (type->storage == STORAGE_F32) { return (float)val; } assert(type_is_float(ctx, type)); return val; } static bool eval_binarithm(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { struct expression lvalue = {0}, rvalue = {0}; if (!eval_expr(ctx, in->binarithm.lvalue, &lvalue)) { return false; } if (!eval_expr(ctx, in->binarithm.rvalue, &rvalue)) { return false; } bool blval = false, brval = false, bval = false; int64_t ilval = 0, irval = 0, ival = 0; uint64_t ulval = 0, urval = 0, uval = 0; double flval = 0, frval = 0, fval = 0; if (type_is_float(ctx, lvalue.result)) { flval = lvalue.literal.fval, frval = rvalue.literal.fval; } else if (type_is_signed(ctx, lvalue.result)) { ilval = lvalue.literal.ival, irval = rvalue.literal.ival; } else if (type_is_integer(ctx, lvalue.result)) { ulval = lvalue.literal.uval, urval = rvalue.literal.uval; } else if (type_dealias(ctx, lvalue.result)->storage == STORAGE_BOOL) { blval = lvalue.literal.bval, brval = rvalue.literal.bval; } // Type promotion is lowered in check assert(lvalue.result->storage == rvalue.result->storage); bool neg = false; switch (in->binarithm.op) { case BIN_BAND: assert(type_is_integer(ctx, lvalue.result)); if (type_is_signed(ctx, lvalue.result)) { ival = itrunc(ctx, lvalue.result, ilval) & itrunc(ctx, rvalue.result, irval); } else { uval = itrunc(ctx, lvalue.result, ulval) & itrunc(ctx, rvalue.result, urval); } break; case BIN_BOR: assert(type_is_integer(ctx, lvalue.result)); if (type_is_signed(ctx, lvalue.result)) { ival = itrunc(ctx, lvalue.result, ilval) | itrunc(ctx, rvalue.result, irval); } else { uval = itrunc(ctx, lvalue.result, ulval) | itrunc(ctx, rvalue.result, urval); } break; case BIN_DIV: if (type_is_float(ctx, lvalue.result)) { fval = ftrunc(ctx, lvalue.result, flval) / ftrunc(ctx, rvalue.result, frval); } else if (type_is_signed(ctx, lvalue.result)) { int64_t l = itrunc(ctx, lvalue.result, ilval); int64_t r = itrunc(ctx, rvalue.result, irval); if (r == 0) { error(ctx, in->loc, NULL, "division by zero"); return false; } else if (r == -1) { uint64_t bit = lvalue.result->size * 8 - 1; uint64_t min = -((uint64_t)1 << bit); if (l == (int64_t)min) { error(ctx, in->loc, NULL, "division overflow"); return false; } } ival = l / r; } else { assert(type_is_integer(ctx, lvalue.result)); uint64_t r = itrunc(ctx, rvalue.result, urval); if (r == 0) { error(ctx, in->loc, NULL, "division by zero"); return false; } uval = itrunc(ctx, lvalue.result, ulval) / r; } break; case BIN_LSHIFT: assert(type_is_integer(ctx, lvalue.result)); assert(type_is_integer(ctx, rvalue.result)); assert(!type_is_signed(ctx, rvalue.result)); uval = itrunc(ctx, lvalue.result, ulval) << itrunc(ctx, rvalue.result, urval); break; case BIN_MINUS: if (type_is_float(ctx, lvalue.result)) { fval = ftrunc(ctx, lvalue.result, flval) - ftrunc(ctx, rvalue.result, frval); } else if (type_is_signed(ctx, lvalue.result)) { ival = itrunc(ctx, lvalue.result, ilval) - itrunc(ctx, rvalue.result, irval); } else { assert(type_is_integer(ctx, lvalue.result)); uval = itrunc(ctx, lvalue.result, ulval) - itrunc(ctx, rvalue.result, urval); } break; case BIN_MODULO: assert(type_is_integer(ctx, lvalue.result)); if (type_is_signed(ctx, lvalue.result)) { int64_t l = itrunc(ctx, lvalue.result, ilval); int64_t r = itrunc(ctx, rvalue.result, irval); if (r == 0) { error(ctx, in->loc, NULL, "division by zero"); return false; } else if (r == -1) { uint64_t bit = lvalue.result->size * 8 - 1; uint64_t min = -((uint64_t)1 << bit); if (l == (int64_t)min) { error(ctx, in->loc, NULL, "division overflow"); return false; } } ival = l % r; } else { uint64_t r = itrunc(ctx, rvalue.result, urval); if (r == 0) { error(ctx, in->loc, NULL, "division by zero"); return false; } uval = itrunc(ctx, lvalue.result, ulval) % r; } break; case BIN_PLUS: if (type_is_float(ctx, lvalue.result)) { fval = ftrunc(ctx, lvalue.result, flval) + ftrunc(ctx, rvalue.result, frval); } else if (type_is_signed(ctx, lvalue.result)) { ival = itrunc(ctx, lvalue.result, ilval) + itrunc(ctx, rvalue.result, irval); } else { assert(type_is_integer(ctx, lvalue.result)); uval = itrunc(ctx, lvalue.result, ulval) + itrunc(ctx, rvalue.result, urval); } break; case BIN_RSHIFT: assert(type_is_integer(ctx, lvalue.result)); assert(type_is_integer(ctx, rvalue.result)); assert(!type_is_signed(ctx, rvalue.result)); uval = itrunc(ctx, lvalue.result, ulval) >> itrunc(ctx, rvalue.result, urval); break; case BIN_TIMES: if (type_is_float(ctx, lvalue.result)) { fval = ftrunc(ctx, lvalue.result, flval) * ftrunc(ctx, rvalue.result, frval); } else if (type_is_signed(ctx, lvalue.result)) { ival = (int64_t)itrunc(ctx, lvalue.result, ilval) * (int64_t)itrunc(ctx, rvalue.result, irval); } else { assert(type_is_integer(ctx, lvalue.result)); uval = itrunc(ctx, lvalue.result, ulval) * itrunc(ctx, rvalue.result, urval); } break; case BIN_BXOR: assert(type_is_integer(ctx, lvalue.result)); if (type_is_signed(ctx, lvalue.result)) { ival = itrunc(ctx, lvalue.result, ilval) ^ itrunc(ctx, rvalue.result, irval); } else { uval = itrunc(ctx, lvalue.result, ulval) ^ itrunc(ctx, rvalue.result, urval); } break; // Logical arithmetic case BIN_GREATER: if (type_is_float(ctx, lvalue.result)) { bval = ftrunc(ctx, lvalue.result, flval) > ftrunc(ctx, rvalue.result, frval); } else if (type_is_signed(ctx, lvalue.result)) { bval = (int64_t)itrunc(ctx, lvalue.result, ilval) > (int64_t)itrunc(ctx, rvalue.result, irval); } else { assert(type_is_integer(ctx, lvalue.result)); bval = itrunc(ctx, lvalue.result, ulval) > itrunc(ctx, rvalue.result, urval); } break; case BIN_GREATEREQ: if (type_is_float(ctx, lvalue.result)) { bval = ftrunc(ctx, lvalue.result, flval) >= ftrunc(ctx, rvalue.result, frval); } else if (type_is_signed(ctx, lvalue.result)) { bval = (int64_t)itrunc(ctx, lvalue.result, ilval) >= (int64_t)itrunc(ctx, rvalue.result, irval); } else { assert(type_is_integer(ctx, lvalue.result)); bval = itrunc(ctx, lvalue.result, ulval) >= itrunc(ctx, rvalue.result, urval); } break; case BIN_LAND: assert(type_dealias(ctx, lvalue.result)->storage == STORAGE_BOOL && type_dealias(ctx, rvalue.result)->storage == STORAGE_BOOL); bval = blval && brval; break; case BIN_NEQUAL: neg = true; /* fallthrough */ case BIN_LEQUAL: if (type_is_float(ctx, lvalue.result)) { bval = ftrunc(ctx, lvalue.result, flval) == ftrunc(ctx, rvalue.result, frval); } else if (type_is_signed(ctx, lvalue.result)) { bval = itrunc(ctx, lvalue.result, ilval) == itrunc(ctx, rvalue.result, irval); } else if (type_is_integer(ctx, lvalue.result) || type_dealias(ctx, lvalue.result)->storage == STORAGE_POINTER) { bval = itrunc(ctx, lvalue.result, ulval) == itrunc(ctx, rvalue.result, urval); } else if (type_dealias(ctx, lvalue.result)->storage == STORAGE_BOOL) { bval = lvalue.literal.bval == rvalue.literal.bval; } else if (type_dealias(ctx, lvalue.result)->storage == STORAGE_RCONST || type_dealias(ctx, lvalue.result)->storage == STORAGE_RUNE) { bval = lvalue.literal.rune == rvalue.literal.rune; } else { assert(type_dealias(ctx, lvalue.result)->storage == STORAGE_STRING); if (lvalue.literal.string.len != rvalue.literal.string.len) { bval = false; } else { bval = memcmp(lvalue.literal.string.value, rvalue.literal.string.value, lvalue.literal.string.len) == 0; } } bval = bval != neg; break; case BIN_LESS: if (type_is_float(ctx, lvalue.result)) { bval = ftrunc(ctx, lvalue.result, flval) < ftrunc(ctx, rvalue.result, frval); } else if (type_is_signed(ctx, lvalue.result)) { bval = (int64_t)itrunc(ctx, lvalue.result, ilval) < (int64_t)itrunc(ctx, rvalue.result, irval); } else { assert(type_is_integer(ctx, lvalue.result)); bval = itrunc(ctx, lvalue.result, ulval) < itrunc(ctx, rvalue.result, urval); } break; case BIN_LESSEQ: if (type_is_float(ctx, lvalue.result)) { bval = ftrunc(ctx, lvalue.result, flval) <= ftrunc(ctx, rvalue.result, frval); } else if (type_is_signed(ctx, lvalue.result)) { bval = (int64_t)itrunc(ctx, lvalue.result, ilval) <= (int64_t)itrunc(ctx, rvalue.result, irval); } else { assert(type_is_integer(ctx, lvalue.result)); bval = itrunc(ctx, lvalue.result, ulval) <= itrunc(ctx, rvalue.result, urval); } break; case BIN_LOR: assert(type_dealias(ctx, lvalue.result)->storage == STORAGE_BOOL && type_dealias(ctx, rvalue.result)->storage == STORAGE_BOOL); bval = blval || brval; break; case BIN_LXOR: assert(type_dealias(ctx, lvalue.result)->storage == STORAGE_BOOL && type_dealias(ctx, rvalue.result)->storage == STORAGE_BOOL); bval = blval != brval; break; } if (type_is_float(ctx, in->result)) { out->literal.fval = ftrunc(ctx, in->result, fval); } else if (type_is_signed(ctx, in->result)) { out->literal.ival = itrunc(ctx, in->result, ival); } else if (type_dealias(ctx, in->result)->storage == STORAGE_BOOL || type_dealias(ctx, in->result)->storage == STORAGE_STRING) { out->literal.bval = bval; } else { assert(type_is_integer(ctx, in->result) || type_dealias(ctx, in->result)->storage == STORAGE_POINTER); out->literal.uval = itrunc(ctx, in->result, uval); } return true; } static bool eval_literal(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { enum type_storage storage = type_dealias(ctx, out->result)->storage; if (storage == STORAGE_ENUM) { storage = type_dealias(ctx, out->result)->alias.type->storage; } switch (storage) { case STORAGE_ALIAS: case STORAGE_ENUM: assert(0); // Handled above case STORAGE_ARRAY:; struct array_literal **anext = &out->literal.array; for (struct array_literal *arr = in->literal.array; arr; arr = arr->next) { struct array_literal *alit = *anext = xcalloc(1, sizeof(struct array_literal)); alit->value = xcalloc(1, sizeof(struct expression)); if (!eval_expr(ctx, arr->value, alit->value)) { return false; } anext = &alit->next; } break; case STORAGE_STRING: out->literal.string.len = in->literal.string.len; out->literal.string.value = xcalloc(1, in->literal.string.len); memcpy(out->literal.string.value, in->literal.string.value, in->literal.string.len); break; case STORAGE_TAGGED: out->literal.tagged.tag = in->literal.tagged.tag; out->literal.tagged.value = xcalloc(sizeof(struct expression), 1); return eval_expr(ctx, in->literal.tagged.value, out->literal.tagged.value); case STORAGE_STRUCT:; struct struct_literal **next = &out->literal._struct; for (struct struct_literal *_struct = in->literal._struct; _struct; _struct = _struct->next) { struct struct_literal *cur = *next = xcalloc(sizeof(struct struct_literal), 1); cur->field = _struct->field; cur->value = xcalloc(sizeof(struct expression), 1); if (!eval_expr(ctx, _struct->value, cur->value)) { return false; } next = &cur->next; } break; case STORAGE_UNION: assert(0); // TODO case STORAGE_TUPLE:; struct tuple_literal **tnext = &out->literal.tuple; for (struct tuple_literal *tuple = in->literal.tuple; tuple; tuple = tuple->next) { struct tuple_literal *tconst = *tnext = xcalloc(1, sizeof(struct tuple_literal)); tconst->field = tuple->field; tconst->value = xcalloc(1, sizeof(struct expression)); if (!eval_expr(ctx, tuple->value, tconst->value)) { return false; } tnext = &tconst->next; } break; case STORAGE_BOOL: case STORAGE_ERROR: case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_I8: case STORAGE_ICONST: case STORAGE_INT: case STORAGE_NULL: case STORAGE_POINTER: case STORAGE_RCONST: case STORAGE_RUNE: case STORAGE_SIZE: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_U8: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_VOID: case STORAGE_DONE: case STORAGE_SLICE: out->literal = in->literal; break; case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_VALIST: abort(); // Invariant } return true; } static void eval_expand_array(struct context *ctx, const struct type *intype, const struct type *outtype, const struct expression *restrict in, struct expression *restrict out) { assert(in->type == EXPR_LITERAL); assert(out->type == EXPR_LITERAL); assert(intype->storage == STORAGE_ARRAY); assert(outtype->storage == STORAGE_ARRAY); struct array_literal *array_in = in->literal.array; struct array_literal **next = &out->literal.array; for (size_t i = 0; i < outtype->array.length; i++) { struct array_literal *item = *next = xcalloc(1, sizeof(struct array_literal)); item->value = array_in->value; next = &item->next; if (array_in->next) { array_in = array_in->next; } } } static bool eval_type_assertion(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { struct expression val = {0}; if (!eval_expr(ctx, in->cast.value, &val)) { return false; } const struct type *from = type_dealias(ctx, in->cast.value->result); assert(from->storage == STORAGE_TAGGED); if (val.literal.tagged.tag == in->cast.secondary) { out->literal = val.literal.tagged.value->literal; return true; } else { error(ctx, in->loc, NULL, "type assertion failed"); return false; } } static bool eval_type_test(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { struct expression val = {0}; if (!eval_expr(ctx, in->cast.value, &val)) { return false; } const struct type *from = type_dealias(ctx, in->cast.value->result); assert(from->storage == STORAGE_TAGGED); out->literal.bval = val.literal.tagged.tag == in->cast.secondary; return true; } static bool eval_cast(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { struct expression val = {0}; if (!eval_expr(ctx, in->cast.value, &val)) { return false; } const struct type *to = type_dealias(ctx, in->result), *from = type_dealias(ctx, val.result); // The STORAGE_ARRAY exception is to make sure we handle expandable // arrays at this point. if (to->storage == from->storage && to->storage != STORAGE_ARRAY) { out->literal = val.literal; return true; } if (from->storage == STORAGE_ERROR) { return true; } else if (from->storage == STORAGE_TAGGED) { out->literal = val.literal.tagged.value->literal; return true; } // XXX: We should also be able to handle expressions which use // symbols/identifiers const struct type *subtype; switch (to->storage) { case STORAGE_POINTER: if (from->storage == STORAGE_NULL) { out->literal.uval = 0; return true; } assert(from->storage == STORAGE_POINTER || from->storage == STORAGE_UINTPTR); out->literal.uval = val.literal.uval; return true; case STORAGE_ENUM: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_I8: case STORAGE_ICONST: case STORAGE_INT: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_U8: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_SIZE: case STORAGE_RCONST: case STORAGE_RUNE: if (type_is_float(ctx, val.result)) { out->literal.ival = itrunc(ctx, to, (int64_t)val.literal.fval); } else if (type_is_signed(ctx, val.result)) { out->literal.ival = itrunc(ctx, to, val.literal.ival); } else { out->literal.ival = itrunc(ctx, to, val.literal.uval); } return true; case STORAGE_ARRAY: assert(from->storage == STORAGE_ARRAY); if (from->array.expandable) { eval_expand_array(ctx, from, to, &val, out); } else { out->literal = val.literal; } return true; case STORAGE_SLICE: assert(from->storage == STORAGE_ARRAY); out->literal.slice.array = val.literal.array; out->literal.slice.start = 0; out->literal.slice.len = out->literal.slice.cap = from->array.length; return true; case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: if (type_is_float(ctx, val.result)) { out->literal.fval = ftrunc(ctx, to, val.literal.fval); } else if (type_is_signed(ctx, val.result)) { out->literal.fval = ftrunc(ctx, to, (double)val.literal.ival); } else { out->literal.fval = ftrunc(ctx, to, (double)val.literal.uval); } return true; case STORAGE_TAGGED: subtype = tagged_select_subtype(ctx, to, val.result, true); out->literal.tagged.value = xcalloc(1, sizeof(struct expression)); if (subtype) { out->literal.tagged.tag = subtype; *out->literal.tagged.value = val; } else { out->literal.tagged.tag = from; *out->literal.tagged.value = val; } return true; case STORAGE_NULL: case STORAGE_ALIAS: assert(0); // Handled above case STORAGE_BOOL: case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: assert(0); // Invariant case STORAGE_ERROR: case STORAGE_VOID: case STORAGE_DONE: return true; } assert(0); // Unreachable } static bool eval_len(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { assert(in->type == EXPR_LEN); const struct type *expr_type = type_dereference(ctx, in->len.value->result); assert(expr_type != NULL); expr_type = type_dealias(ctx, expr_type); struct expression obj = {0}; if (!eval_expr(ctx, in->len.value, &obj)) { return false; } switch (obj.result->storage) { case STORAGE_SLICE: out->literal.uval = obj.literal.slice.len; return true; case STORAGE_STRING: out->literal.uval = obj.literal.string.len; return true; case STORAGE_ERROR: out->literal.uval = 0; return true; case STORAGE_ARRAY: default: abort(); // Invariant } uint64_t len = 0; for (struct array_literal *c = obj.literal.array; c != NULL; c = c->next) { len++; } out->literal.uval = len; return true; } static bool literal_default(struct context *ctx, struct expression *v) { struct expression b = {0}; switch (type_dealias(ctx, v->result)->storage) { case STORAGE_ERROR: case STORAGE_POINTER: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_I8: case STORAGE_ICONST: case STORAGE_INT: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_U8: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_SIZE: case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: case STORAGE_ENUM: case STORAGE_NULL: case STORAGE_RCONST: case STORAGE_RUNE: case STORAGE_SLICE: case STORAGE_BOOL: break; // calloc does this for us case STORAGE_STRUCT: case STORAGE_UNION: b.type = EXPR_STRUCT; b.result = v->result; b._struct.autofill = true; bool r = eval_expr(ctx, &b, v); assert(r); break; case STORAGE_STRING: v->literal.string.value = NULL; v->literal.string.len = 0; break; case STORAGE_ARRAY: v->literal.array = xcalloc(1, sizeof(struct array_literal)); v->literal.array->value = xcalloc(1, sizeof(struct expression)); v->literal.array->value->type = EXPR_LITERAL; v->literal.array->value->result = type_dealias(ctx, v->result)->array.members; return literal_default(ctx, v->literal.array->value); break; case STORAGE_TAGGED: return false; case STORAGE_TUPLE:; struct tuple_literal **c = &v->literal.tuple; for (const struct type_tuple *t = &type_dealias(ctx, v->result)->tuple; t != NULL; t = t->next) { *c = xcalloc(1, sizeof(struct tuple_literal)); (*c)->field = t; (*c)->value = xcalloc(1, sizeof(struct expression)); (*c)->value->type = EXPR_LITERAL; (*c)->value->result = t->type; if (!literal_default(ctx, (*c)->value)) { return false; } c = &(*c)->next; } break; case STORAGE_ALIAS: case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_VALIST: assert(0); // Invariant case STORAGE_VOID: case STORAGE_DONE: break; // no-op } return true; } static int field_compar(const void *_a, const void *_b) { const struct struct_literal **a = (const struct struct_literal **)_a; const struct struct_literal **b = (const struct struct_literal **)_b; return (*a)->field->offset - (*b)->field->offset; } static size_t count_struct_fields(struct context *ctx, const struct type *type) { size_t n = 0; assert(type->storage == STORAGE_STRUCT || type->storage == STORAGE_UNION); for (const struct struct_field *field = type->struct_union.fields; field; field = field->next) { if (!field->name) { n += count_struct_fields(ctx, type_dealias(ctx, field->type)); } else { ++n; } } return n; } static bool autofill_struct(struct context *ctx, const struct type *type, struct struct_literal **fields) { assert(type->storage == STORAGE_STRUCT || type->storage == STORAGE_UNION); for (const struct struct_field *field = type->struct_union.fields; field; field = field->next) { if (!field->name) { bool r = autofill_struct(ctx, type_dealias(ctx, field->type), fields); if (!r) { return false; } continue; } size_t i = 0; bool skip = false; for (; fields[i]; ++i) { if (!strcmp(field->name, fields[i]->field->name)) { skip = true; break; } } if (!skip) { fields[i] = xcalloc(1, sizeof(struct struct_literal)); fields[i]->field = field; fields[i]->value = xcalloc(1, sizeof(struct expression)); fields[i]->value->type = EXPR_LITERAL; fields[i]->value->result = field->type; // TODO: there should probably be a better error message // when this happens if (!literal_default(ctx, fields[i]->value)) { return false; } } } return true; } static bool eval_struct(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { assert(in->type == EXPR_STRUCT); assert(type_dealias(ctx, in->result)->storage != STORAGE_UNION); // TODO const struct type *type = type_dealias(ctx, in->result); size_t n = count_struct_fields(ctx, type); assert(n > 0); size_t i = 0; struct struct_literal **fields = xcalloc(n, sizeof(struct struct_literal *)); for (const struct expr_struct_field *field_in = in->_struct.fields; field_in; field_in = field_in->next, ++i) { const struct struct_field *field = type_get_field(ctx, type, field_in->field->name); fields[i] = xcalloc(1, sizeof(struct struct_literal)); fields[i]->field = field; fields[i]->value = xcalloc(1, sizeof(struct expression)); if (!eval_expr(ctx, field_in->value, fields[i]->value)) { return false; } } assert(in->_struct.autofill || i == n); if (in->_struct.autofill) { if (!autofill_struct(ctx, type, fields)) { return false; } } qsort(fields, n, sizeof(struct struct_literal *), field_compar); for (size_t i = 0; i < n - 1; ++i) { fields[i]->next = fields[i + 1]; } out->literal._struct = fields[0]; free(fields); return true; } static bool eval_slice(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { assert(in->type == EXPR_SLICE); const struct type *object_type = type_dealias(ctx, in->slice.object->result); struct expression object = {0}; if (object_type->storage == STORAGE_SLICE) { if (!eval_expr(ctx, in->slice.object, &object)) { return false; } object_type = type_dealias(ctx, object.result); } else if (object_type->storage == STORAGE_ARRAY) { object = *in->slice.object; } else { return false; } size_t start = 0; if (in->slice.start) { struct expression start_expr = {0}; if (!eval_expr(ctx, in->slice.start, &start_expr)) { return false; } start = start_expr.literal.uval; } size_t end; if (object_type->storage == STORAGE_ARRAY) { end = object_type->array.length; } else { end = object.literal.slice.len; } if (in->slice.end) { struct expression end_expr = {0}; if (!eval_expr(ctx, in->slice.end, &end_expr)) { return false; } end = end_expr.literal.uval; } if (object_type->storage == STORAGE_SLICE) { if (start >= end || start >= object.literal.slice.len || end > object.literal.slice.len) { error(ctx, in->loc, NULL, "slice access out of bounds"); return false; } out->literal = object.literal; out->literal.slice.start += start; out->literal.slice.len = end - start; out->literal.slice.cap -= start; return true; } assert(object_type->storage == STORAGE_ARRAY); out->literal.slice.start = start; out->literal.slice.len = end - start; out->literal.slice.cap = object_type->array.length - start; switch (object.type) { case EXPR_ACCESS:; struct expression addr_expr = {0}, addr = {0}; addr_expr.type = EXPR_UNARITHM; addr_expr.unarithm.op = UN_ADDRESS; addr_expr.unarithm.operand = &object; if (!eval_expr(ctx, &addr_expr, &addr)) { return false; } out->literal.object = addr.literal.object; out->literal.slice.offset = addr.literal.ival; break; case EXPR_LITERAL: out->literal.object = NULL; out->literal.slice.array = object.literal.array; break; default: assert(0); // Invariant } return true; } static bool eval_tuple(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { assert(in->type == EXPR_TUPLE); const struct type *type = type_dealias(ctx, in->result); struct tuple_literal *out_tuple_start, *out_tuple; out_tuple_start = out_tuple = xcalloc(1, sizeof(struct tuple_literal)); const struct expression_tuple *in_tuple = &in->tuple; for (const struct type_tuple *field_type = &type->tuple; field_type; field_type = field_type->next) { out_tuple->value = xcalloc(1, sizeof(struct expression)); if (!eval_expr(ctx, in_tuple->value, out_tuple->value)) { return false; } out_tuple->field = field_type; if (in_tuple->next) { in_tuple = in_tuple->next; out_tuple->next = xcalloc(1, sizeof(struct tuple_literal)); out_tuple = out_tuple->next; } } out->literal.tuple = out_tuple_start; return true; } static bool eval_unarithm(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { if (in->unarithm.op == UN_ADDRESS) { if (in->unarithm.operand->result == &builtin_type_error) { out->type = EXPR_LITERAL; out->result = &builtin_type_error; out->literal.uval = 0; return true; } if (in->unarithm.operand->type != EXPR_ACCESS) { return false; } const struct expression_access *access = &in->unarithm.operand->access; struct expression new_in = {0}; const struct type *operand_type; switch (access->type) { case ACCESS_IDENTIFIER: if (access->object->otype != O_DECL) { return false; } out->literal.object = access->object; out->literal.ival = 0; return true; case ACCESS_INDEX: new_in = *in; new_in.unarithm.operand = access->array; if (!eval_expr(ctx, &new_in, out)) { return false; } struct expression index = {0}; if (!eval_expr(ctx, access->index, &index)) { return false; } operand_type = type_dealias(ctx, access->array->result); if (operand_type->storage != STORAGE_ARRAY) { // autodereferencing not allowed return false; } out->literal.ival += index.literal.uval * operand_type->array.members->size; return true; case ACCESS_FIELD: new_in = *in; new_in.unarithm.operand = access->_struct; if (!eval_expr(ctx, &new_in, out)) { return false; } operand_type = type_dealias(ctx, access->tuple->result); if (operand_type->storage != STORAGE_STRUCT) { // autodereferencing not allowed return false; } out->literal.ival += access->field->offset; return true; case ACCESS_TUPLE: new_in = *in; new_in.unarithm.operand = access->tuple; if (!eval_expr(ctx, &new_in, out)) { return false; } operand_type = type_dealias(ctx, access->tuple->result); if (operand_type->storage != STORAGE_TUPLE) { // autodereferencing not allowed return false; } out->literal.ival += access->tvalue->offset; return true; } } struct expression lvalue = {0}; if (!eval_expr(ctx, in->unarithm.operand, &lvalue)) { return false; } switch (in->unarithm.op) { case UN_ADDRESS: assert(0); // handled above case UN_BNOT: out->literal.uval = itrunc(ctx, out->result, ~lvalue.literal.uval); break; case UN_DEREF: return false; case UN_LNOT: out->literal.bval = !lvalue.literal.bval; break; case UN_MINUS: if (type_is_float(ctx, out->result)) { out->literal.fval = -lvalue.literal.fval; } else { out->literal.ival = itrunc(ctx, out->result, -(uint64_t)lvalue.literal.ival); } break; } return true; } bool eval_expr(struct context *ctx, const struct expression *restrict in, struct expression *restrict out) { out->result = in->result; out->type = EXPR_LITERAL; switch (in->type) { case EXPR_ACCESS: return eval_access(ctx, in, out); case EXPR_BINARITHM: return eval_binarithm(ctx, in, out); case EXPR_CAST: switch (in->cast.kind) { case C_CAST: return eval_cast(ctx, in, out); case C_ASSERTION: return eval_type_assertion(ctx, in, out); case C_TEST: return eval_type_test(ctx, in, out); default: assert(0); // Unreacheable } case EXPR_LEN: return eval_len(ctx, in, out); case EXPR_LITERAL: return eval_literal(ctx, in, out); case EXPR_STRUCT: return eval_struct(ctx, in, out); case EXPR_SLICE: return eval_slice(ctx, in, out); case EXPR_TUPLE: return eval_tuple(ctx, in, out); case EXPR_UNARITHM: return eval_unarithm(ctx, in, out); case EXPR_ALLOC: case EXPR_APPEND: case EXPR_ASSERT: case EXPR_ASSIGN: case EXPR_BINDING: case EXPR_BREAK: case EXPR_CALL: case EXPR_COMPOUND: case EXPR_CONTINUE: case EXPR_DEFER: case EXPR_DEFINE: case EXPR_DELETE: case EXPR_FOR: case EXPR_FREE: case EXPR_IF: case EXPR_INSERT: case EXPR_MATCH: case EXPR_PROPAGATE: case EXPR_RETURN: case EXPR_SWITCH: case EXPR_VAARG: case EXPR_VAEND: case EXPR_VASTART: case EXPR_YIELD: return false; } assert(0); // Unreachable } harec-0.24.2/src/expr.c000066400000000000000000000046251464473277600146250ustar00rootroot00000000000000#include #include "expr.h" #include "types.h" #include "util.h" uint32_t expr_hash(const struct expression *expr) { assert(expr && expr->type == EXPR_LITERAL); uint32_t hash = FNV1A_INIT; hash = fnv1a_u32(hash, type_hash(expr->result)); // Add the storage a second time so that void and null expressions have // different hashes than their types. hash = fnv1a_u32(hash, expr->result->storage); enum type_storage storage = type_dealias(NULL, expr->result)->storage; switch (storage) { case STORAGE_ERROR: case STORAGE_VOID: case STORAGE_NULL: case STORAGE_DONE: break; case STORAGE_BOOL: hash = fnv1a(hash, expr->literal.bval); break; case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: // TODO Consider how to hash different NaNs. assert(!isnan(expr->literal.fval)); hash = fnv1a_u64(hash, expr->literal.uval); break; case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_SIZE: case STORAGE_ENUM: case STORAGE_ICONST: case STORAGE_POINTER: // All of these have been cast up to 8 bytes, so the reinterpret // cast to uval is correct. hash = fnv1a_u64(hash, expr->literal.uval); break; case STORAGE_RCONST: case STORAGE_RUNE: hash = fnv1a_u32(hash, expr->literal.rune); break; case STORAGE_STRING: for (size_t i = 0; i < expr->literal.string.len; i++) { hash = fnv1a(hash, expr->literal.string.value[i]); } break; case STORAGE_SLICE: // Slice literals are stored as arrays. case STORAGE_ARRAY: for (struct array_literal *al = expr->literal.array; al; al = al->next) { hash = fnv1a_u32(hash, expr_hash(al->value)); } break; case STORAGE_STRUCT: case STORAGE_UNION: for (struct struct_literal *sl = expr->literal._struct; sl; sl = sl->next) { hash = fnv1a_u32(hash, expr_hash(sl->value)); } break; case STORAGE_TUPLE: for (struct tuple_literal *tl = expr->literal.tuple; tl; tl = tl->next) { hash = fnv1a_u64(hash, expr_hash(tl->value)); } break; case STORAGE_TAGGED: hash = fnv1a_u32(hash, type_hash(expr->literal.tagged.tag)); hash = fnv1a_u32(hash, expr_hash(expr->literal.tagged.value)); break; case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_FUNCTION: case STORAGE_VALIST: case STORAGE_ALIAS: // handled above assert(0); } return hash; } harec-0.24.2/src/gen.c000066400000000000000000003435251464473277600144250ustar00rootroot00000000000000#include #include #include #include "check.h" #include "expr.h" #include "gen.h" #include "scope.h" #include "type_store.h" #include "types.h" #include "util.h" #define EXPR_GEN_VALUE -1 static const struct gen_value gv_void = { .kind = GV_CONST, .type = &builtin_type_void, }; static struct gen_value gen_expr(struct gen_context *ctx, const struct expression *expr); static void gen_expr_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out); static struct gen_value gen_expr_with(struct gen_context *ctx, const struct expression *expr, struct gen_value *out); static void gen_global_decl(struct gen_context *ctx, const struct declaration *decl); static struct gen_scope * gen_scope_lookup(struct gen_context *ctx, const struct scope *which) { for (struct gen_scope *scope = ctx->scope; scope; scope = scope->parent) { if (scope->scope == which) { return scope; } } abort(); } static struct gen_scope * push_scope(struct gen_context *ctx, const struct scope *scope) { struct gen_scope *new = xcalloc(1, sizeof(struct gen_scope)); new->parent = ctx->scope; new->scope = scope; ctx->scope = new; return new; } static void pop_scope(struct gen_context *ctx) { struct gen_scope *scope = ctx->scope; ctx->scope = scope->parent; for (struct gen_defer *defer = scope->defers; defer; /* n/a */) { struct gen_defer *next = defer->next; free(defer); defer = next; } free(scope); } static void gen_defers(struct gen_context *ctx, struct gen_scope *scope) { if (!scope) { return; } if (scope->defers) { pushc(ctx->current, "gen defers"); } struct gen_defer *defers = scope->defers; while (scope->defers) { struct gen_defer *defer = scope->defers; assert(defer->expr->type == EXPR_DEFER); scope->defers = scope->defers->next; push_scope(ctx, defer->expr->defer.scope); gen_expr(ctx, defer->expr->defer.deferred); pop_scope(ctx); } scope->defers = defers; } static void gen_copy_memcpy(struct gen_context *ctx, struct gen_value dest, struct gen_value src) { struct qbe_value dtemp = mklval(ctx, &dest); struct qbe_value stemp = mklval(ctx, &src); struct qbe_value sz = constl(dest.type->size); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memcpy, &dtemp, &stemp, &sz, NULL); } static void gen_copy_aligned(struct gen_context *ctx, struct gen_value dest, struct gen_value src) { if (dest.type->size > 128) { gen_copy_memcpy(ctx, dest, src); return; } struct qbe_value srcv = mkqval(ctx, &src); struct qbe_value destv = mkqval(ctx, &dest); struct qbe_value size = constl(dest.type->size); pushi(ctx->current, NULL, Q_BLIT, &srcv, &destv, &size, NULL); } static void gen_store(struct gen_context *ctx, struct gen_value object, struct gen_value value) { const struct type *ty = type_dealias(NULL, object.type); switch (ty->storage) { case STORAGE_ARRAY: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_UNION: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_VALIST: gen_copy_aligned(ctx, object, value); return; case STORAGE_ENUM: object.type = ty->alias.type; break; default: break; // no-op } if (value.type->size == 0 || value.type->storage == STORAGE_NEVER) { return; // no storage } struct qbe_value qobj = mkqval(ctx, &object), qval = mkqval(ctx, &value); enum qbe_instr qi = store_for_type(ctx, object.type); pushi(ctx->current, NULL, qi, &qval, &qobj, NULL); } // Generates code to store the type ID (id) in a tagged union's tag field (at // address "out" and of type "tagged") static void gen_store_tag(struct gen_context *ctx, struct qbe_value *out, const struct type *tagged, struct qbe_value *id) { assert(type_dealias(NULL, tagged)->storage == STORAGE_TAGGED); const enum qbe_instr store = store_for_type(ctx, &builtin_type_u32); pushi(ctx->current, NULL, store, id, out, NULL); } static struct gen_value gen_load(struct gen_context *ctx, struct gen_value object) { const struct type *ty = type_dealias(NULL, object.type); switch (ty->storage) { case STORAGE_ARRAY: case STORAGE_FUNCTION: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: return object; case STORAGE_ENUM: object.type = ty->alias.type; break; default: break; // no-op } struct gen_value value = mkgtemp(ctx, object.type, ".%d"); struct qbe_value qobj = mkqval(ctx, &object), qval = mkqval(ctx, &value); enum qbe_instr qi = load_for_type(ctx, object.type); pushi(ctx->current, &qval, qi, &qobj, NULL); return value; } // Generates code to load the type ID (into "out") from the tag field of a // tagged union (at "from" and of type "tagged"). static void gen_load_tag(struct gen_context *ctx, struct qbe_value *out, struct qbe_value *from, const struct type *tagged) { assert(type_dealias(NULL, tagged)->storage == STORAGE_TAGGED); const enum qbe_instr load = load_for_type(ctx, &builtin_type_u32); pushi(ctx->current, out, load, from, NULL); } static void gen_fixed_abort(struct gen_context *ctx, struct location loc, enum fixed_aborts reason) { for (struct gen_scope *scope = ctx->scope; scope; scope = scope->parent) { gen_defers(ctx, scope); if (scope->scope->class == SCOPE_DEFER) { break; } } struct qbe_value path = mklval(ctx, &ctx->sources[loc.file]); struct qbe_value line = constl(loc.lineno); struct qbe_value col = constl(loc.colno); struct qbe_value tmp = constl(reason); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.fixedabort, &path, &line, &col, &tmp, NULL); pushi(ctx->current, NULL, Q_HLT, NULL); } static void gen_fixed_assert(struct gen_context *ctx, struct location loc, enum fixed_aborts reason, enum qbe_instr cmp, struct qbe_value *lvalue, struct qbe_value *rvalue) { struct qbe_value cond = mkqtmp(ctx, &qbe_word, "cond.%d"); pushi(ctx->current, &cond, cmp, lvalue, rvalue, NULL); struct qbe_statement lfail, lpass; struct qbe_value bfail = mklabel(ctx, &lfail, "abort.%d"); struct qbe_value bpass = mklabel(ctx, &lpass, "pass.%d"); pushi(ctx->current, NULL, Q_JNZ, &cond, &bpass, &bfail, NULL); push(&ctx->current->body, &lfail); gen_fixed_abort(ctx, loc, reason); push(&ctx->current->body, &lpass); } static struct gen_value gen_autoderef(struct gen_context *ctx, struct gen_value val) { while (type_dealias(NULL, val.type)->storage == STORAGE_POINTER) { val.type = type_dealias(NULL, val.type)->pointer.referent; val = gen_load(ctx, val); } return val; } struct gen_slice gen_slice_ptrs(struct gen_context *ctx, struct gen_value object) { struct gen_slice slice = { .base = mkqval(ctx, &object), .len = mkqtmp(ctx, ctx->arch.ptr, ".%d"), .cap = mkqtmp(ctx, ctx->arch.ptr, ".%d"), }; struct qbe_value lenoff = constl(ctx->arch.ptr->size); struct qbe_value capoff = constl(ctx->arch.ptr->size + ctx->arch.sz->size); pushi(ctx->current, &slice.len, Q_ADD, &slice.base, &lenoff, NULL); pushi(ctx->current, &slice.cap, Q_ADD, &slice.base, &capoff, NULL); return slice; } void load_slice_data(struct gen_context *ctx, struct gen_slice *slobj, struct qbe_value *base, struct qbe_value *len, struct qbe_value *cap) { enum qbe_instr ptrload = load_for_type(ctx, &builtin_type_uintptr); enum qbe_instr szload = load_for_type(ctx, &builtin_type_size); if (base) { *base = mkqtmp(ctx, ctx->arch.ptr, ".%d"); pushi(ctx->current, base, ptrload, &slobj->base, NULL); } if (len) { *len = mkqtmp(ctx, ctx->arch.sz, ".%d"); pushi(ctx->current, len, szload, &slobj->len, NULL); } if (cap) { *cap = mkqtmp(ctx, ctx->arch.sz, ".%d"); pushi(ctx->current, cap, szload, &slobj->cap, NULL); } } void store_slice_data(struct gen_context *ctx, struct gen_slice *slobj, struct qbe_value *base, struct qbe_value *len, struct qbe_value *cap) { enum qbe_instr szstore = store_for_type(ctx, &builtin_type_size); enum qbe_instr ptrstore = store_for_type(ctx, &builtin_type_uintptr); if (base) { pushi(ctx->current, NULL, ptrstore, base, &slobj->base, NULL); } if (len) { pushi(ctx->current, NULL, szstore, len, &slobj->len, NULL); } if (cap) { pushi(ctx->current, NULL, szstore, cap, &slobj->cap, NULL); } } static struct gen_value gen_access_ident(struct gen_context *ctx, const struct scope_object *obj) { switch (obj->otype) { case O_BIND: for (const struct gen_binding *gb = ctx->bindings; gb; gb = gb->next) { if (gb->object == obj) { return gb->value; } } return gv_void; case O_DECL: return (struct gen_value){ .kind = GV_GLOBAL, .type = obj->type, .name = ident_to_sym(&obj->ident), .threadlocal = obj->flags & SO_THREADLOCAL, }; case O_CONST: case O_TYPE: case O_SCAN: break; } abort(); // Invariant } static struct gen_value gen_access_index(struct gen_context *ctx, const struct expression *expr) { struct gen_value glval = gen_expr(ctx, expr->access.array); glval = gen_autoderef(ctx, glval); struct qbe_value qival = mkqtmp(ctx, ctx->arch.ptr, ".%d"); bool checkbounds = !expr->access.bounds_checked; struct qbe_value length, qlval; const struct type *ty = type_dealias(NULL, glval.type); switch (ty->storage) { case STORAGE_SLICE:; struct gen_slice sl = gen_slice_ptrs(ctx, glval); load_slice_data(ctx, &sl, &qlval, &length, NULL); break; case STORAGE_ARRAY: qlval = mkqval(ctx, &glval); if (ty->array.length != SIZE_UNDEFINED) { length = constl(ty->array.length); } else { checkbounds = false; } break; default: assert(0); // Unreachable } struct gen_value index = gen_expr(ctx, expr->access.index); struct qbe_value qindex = mkqval(ctx, &index); struct qbe_value itemsz = constl(expr->result->size); pushi(ctx->current, &qival, Q_MUL, &qindex, &itemsz, NULL); pushi(ctx->current, &qival, Q_ADD, &qlval, &qival, NULL); if (checkbounds) { gen_fixed_assert(ctx, expr->loc, ABORT_OOB, Q_CULTL, &qindex, &length); } return (struct gen_value){ .kind = GV_TEMP, .type = expr->result, .name = qival.name, }; } static struct gen_value gen_access_field(struct gen_context *ctx, const struct expression *expr) { const struct struct_field *field = expr->access.field; struct gen_value glval = gen_expr(ctx, expr->access._struct); glval = gen_autoderef(ctx, glval); if (field->type->size == 0) { return gv_void; } struct qbe_value qlval = mkqval(ctx, &glval); struct qbe_value qfval = mkqtmp(ctx, ctx->arch.ptr, "field.%d"); struct qbe_value offs = constl(field->offset); pushi(ctx->current, &qfval, Q_ADD, &qlval, &offs, NULL); return (struct gen_value){ .kind = GV_TEMP, .type = field->type, .name = qfval.name, }; } static struct gen_value gen_access_value(struct gen_context *ctx, const struct expression *expr) { const struct type_tuple *tuple = expr->access.tvalue; struct gen_value glval = gen_expr(ctx, expr->access.tuple); glval = gen_autoderef(ctx, glval); if (tuple->type->size == 0) { return gv_void; } struct qbe_value qlval = mkqval(ctx, &glval); struct qbe_value qfval = mkqtmp(ctx, ctx->arch.ptr, "value.%d"); struct qbe_value offs = constl(tuple->offset); pushi(ctx->current, &qfval, Q_ADD, &qlval, &offs, NULL); return (struct gen_value){ .kind = GV_TEMP, .type = tuple->type, .name = qfval.name, }; } static struct gen_value gen_expr_access_addr(struct gen_context *ctx, const struct expression *expr) { struct gen_value addr; switch (expr->access.type) { case ACCESS_IDENTIFIER: addr = gen_access_ident(ctx, expr->access.object); break; case ACCESS_INDEX: addr = gen_access_index(ctx, expr); break; case ACCESS_FIELD: addr = gen_access_field(ctx, expr); break; case ACCESS_TUPLE: addr = gen_access_value(ctx, expr); break; } return addr; } static struct gen_value gen_expr_access(struct gen_context *ctx, const struct expression *expr) { struct gen_value addr = gen_expr_access_addr(ctx, expr); if (expr->result->size == 0) { return addr; } return gen_load(ctx, addr); } static struct gen_value gen_expr_alloc_slice_array_with(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { enum alloc_kind kind = expr->alloc.kind; struct gen_value ret = gv_void; if (out == NULL) { ret = mkgtemp(ctx, expr->result, "object.%d"); out = &ret; struct qbe_value base = mkqval(ctx, out); struct qbe_value sz = constl(expr->result->size); enum qbe_instr alloc = alloc_for_align(expr->result->align); pushprei(ctx->current, &base, alloc, &sz, NULL); } struct qbe_value qcap; if (kind == ALLOC_LEN || kind == ALLOC_CAP) { struct gen_value cap = gen_expr(ctx, expr->alloc.cap); qcap = mkqval(ctx, &cap); } struct qbe_value length, initdata; const struct type *inittype = type_dealias(NULL, expr->alloc.init->result); if (inittype->storage == STORAGE_SLICE) { assert(kind != ALLOC_LEN); struct gen_value init = gen_expr(ctx, expr->alloc.init); struct gen_slice sl = gen_slice_ptrs(ctx, init); load_slice_data(ctx, &sl, &initdata, &length, NULL); } else if (inittype->storage == STORAGE_ARRAY) { assert(inittype->array.length != SIZE_UNDEFINED); length = constl(inittype->array.length); } else { abort(); // invariant } if (kind == ALLOC_LEN || kind == ALLOC_CAP) { gen_fixed_assert(ctx, expr->loc, ABORT_CAP_TOO_SMALL, Q_CULEL, &length, &qcap); } else { qcap = length; } struct qbe_statement lzero, lnonzero; struct qbe_value bzero = mklabel(ctx, &lzero, "zero.%d"); struct qbe_value bnonzero = mklabel(ctx, &lnonzero, "nonzero.%d"); struct qbe_value newdata = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value cmpres = mkqtmp(ctx, &qbe_word, ".%d"); struct qbe_value zero = constl(0); pushi(ctx->current, &newdata, Q_COPY, &zero, NULL); pushi(ctx->current, &cmpres, Q_CNEL, &qcap, &zero, NULL); pushi(ctx->current, NULL, Q_JNZ, &cmpres, &bnonzero, &bzero, NULL); push(&ctx->current->body, &lnonzero); const struct type *sltype = type_dealias(NULL, expr->result); assert(sltype->storage == STORAGE_SLICE); struct qbe_value sz = mkqtmp(ctx, ctx->arch.sz, ".%d"); struct qbe_value membsz = constl(sltype->array.members->size); pushi(ctx->current, &sz, Q_MUL, &membsz, &qcap, NULL); pushi(ctx->current, &newdata, Q_CALL, &ctx->rt.malloc, &sz, NULL); gen_fixed_assert(ctx, expr->loc, ABORT_ALLOC_FAILURE, Q_CNEL, &newdata, &zero); if (inittype->storage == STORAGE_ARRAY) { push(&ctx->current->body, &lzero); struct gen_value storage = (struct gen_value){ .kind = GV_TEMP, .type = inittype, .name = newdata.name, }; gen_expr_at(ctx, expr->alloc.init, storage); if (kind == ALLOC_LEN) { struct qbe_value last = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value next = mkqtmp(ctx, ctx->arch.ptr, ".%d"); pushi(ctx->current, &next, Q_MUL, &length, &membsz, NULL); pushi(ctx->current, &next, Q_ADD, &next, &newdata, NULL); pushi(ctx->current, &last, Q_SUB, &next, &membsz, NULL); struct qbe_value remain = mkqtmp(ctx, ctx->arch.sz, ".%d"); pushi(ctx->current, &remain, Q_SUB, &qcap, &length, NULL); pushi(ctx->current, &remain, Q_MUL, &remain, &membsz, NULL); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memcpy, &next, &last, &remain, NULL); struct gen_slice sl = gen_slice_ptrs(ctx, *out); store_slice_data(ctx, &sl, &newdata, &qcap, &qcap); return ret; } } else { pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memcpy, &newdata, &initdata, &sz, NULL); push(&ctx->current->body, &lzero); } struct gen_slice sl = gen_slice_ptrs(ctx, *out); store_slice_data(ctx, &sl, &newdata, &length, &qcap); return ret; } static struct gen_value gen_expr_alloc_with(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { if (expr->alloc.kind != ALLOC_OBJECT) { return gen_expr_alloc_slice_array_with(ctx, expr, out); } // alloc(init) case assert(expr->alloc.cap == NULL); const struct type *objtype = type_dealias(NULL, expr->result); assert(objtype->storage == STORAGE_POINTER); objtype = objtype->pointer.referent; struct qbe_value sz = constl(objtype->size); struct gen_value result = mkgtemp(ctx, expr->result, ".%d"); struct qbe_value qresult = mkqval(ctx, &result); pushi(ctx->current, &qresult, Q_CALL, &ctx->rt.malloc, &sz, NULL); if (!(type_dealias(NULL, expr->result)->pointer.flags & PTR_NULLABLE)) { struct qbe_value zero = constl(0); gen_fixed_assert(ctx, expr->loc, ABORT_ALLOC_FAILURE, Q_CNEL, &qresult, &zero); } struct gen_value object = { .kind = GV_TEMP, .type = objtype, .name = result.name, }; gen_expr_at(ctx, expr->alloc.init, object); if (out) { gen_store(ctx, *out, result); } return result; } static struct gen_value gen_expr_assert(struct gen_context *ctx, const struct expression *expr) { struct qbe_statement failedl, passedl; if (expr->assert.cond) { struct qbe_value bfailed = mklabel(ctx, &failedl, "failed.%d"); struct qbe_value bpassed = mklabel(ctx, &passedl, "passed.%d"); struct gen_value cond = gen_expr(ctx, expr->assert.cond); struct qbe_value qcond = mkqval(ctx, &cond); pushi(ctx->current, NULL, Q_JNZ, &qcond, &bpassed, &bfailed, NULL); push(&ctx->current->body, &failedl); } if (expr->assert.message) { struct gen_value msg = gen_expr(ctx, expr->assert.message); for (struct gen_scope *scope = ctx->scope; scope; scope = scope->parent) { gen_defers(ctx, scope); if (scope->scope->class == SCOPE_DEFER) { break; } } struct qbe_value path = mklval(ctx, &ctx->sources[expr->loc.file]); struct qbe_value line = constl(expr->loc.lineno); struct qbe_value col = constl(expr->loc.colno); struct qbe_value qmsg = mkqval(ctx, &msg); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.abort, &path, &line, &col, &qmsg, NULL); pushi(ctx->current, NULL, Q_HLT, NULL); } else { gen_fixed_abort(ctx, expr->loc, expr->assert.fixed_reason); } if (expr->assert.cond) { push(&ctx->current->body, &passedl); } return gv_void; } static void gen_subslice_info(struct gen_context *ctx, const struct expression *expr, struct qbe_value *oldlen, struct qbe_value *oldcap, struct qbe_value *start, struct qbe_value *end, struct qbe_value *newlen, struct qbe_value *newcap) { assert(expr->type == EXPR_SLICE); // Callers are allowed to pass NULL for any of these, which tells us // they do not care about that value (and sometimes better code can be // generated based on that info). // The procedure may still need them in some cases internally. start = start ? start : &(struct qbe_value){0}; end = end ? end : &(struct qbe_value){0}; newlen = newlen ? newlen : &(struct qbe_value){0}; newcap = newcap ? newcap : &(struct qbe_value){0}; enum { START = 1, END = 1 << 1, LENGTH = 1 << 2 }; int bounds = oldlen ? LENGTH : 0; if (expr->slice.start) { struct gen_value gstart = gen_expr(ctx, expr->slice.start); *start = mkqval(ctx, &gstart); bounds |= START; } else { *start = constl(0); } if (expr->slice.end) { struct gen_value gend = gen_expr(ctx, expr->slice.end); *end = mkqval(ctx, &gend); bounds |= END; } else { *end = *oldlen; } *newlen = mkqtmp(ctx, ctx->arch.sz, ".%d"); *newcap = mkqtmp(ctx, ctx->arch.sz, ".%d"); pushi(ctx->current, newlen, Q_SUB, end, start, NULL); struct qbe_value end_oob = mkqtmp(ctx, &qbe_word, ".%d"); struct qbe_value start_oob = mkqtmp(ctx, &qbe_word, ".%d"); struct qbe_value valid = mkqtmp(ctx, &qbe_word, ".%d"); switch (bounds) { case START | END | LENGTH: pushi(ctx->current, &start_oob, Q_CULEL, start, end, NULL); pushi(ctx->current, &end_oob, Q_CULEL, end, oldlen, NULL); pushi(ctx->current, &valid, Q_AND, &start_oob, &end_oob, NULL); pushi(ctx->current, newlen, Q_SUB, end, start, NULL); pushi(ctx->current, newcap, Q_SUB, oldcap, start, NULL); break; case START | LENGTH: pushi(ctx->current, &valid, Q_CULEL, start, oldlen, NULL); pushi(ctx->current, newlen, Q_SUB, oldlen, start, NULL); pushi(ctx->current, newcap, Q_SUB, oldcap, start, NULL); break; case END | LENGTH: pushi(ctx->current, &valid, Q_CULEL, end, oldlen, NULL); pushi(ctx->current, newlen, Q_COPY, end, NULL); pushi(ctx->current, newcap, Q_COPY, oldcap, NULL); break; case START | END: pushi(ctx->current, &valid, Q_CULEL, start, end, NULL); pushi(ctx->current, newlen, Q_SUB, end, start, NULL); pushi(ctx->current, newcap, Q_COPY, newlen, NULL); break; case LENGTH: pushi(ctx->current, newlen, Q_COPY, oldlen, NULL); pushi(ctx->current, newcap, Q_COPY, oldcap, NULL); return; case END: pushi(ctx->current, newlen, Q_COPY, end, NULL); pushi(ctx->current, newcap, Q_COPY, end, NULL); return; case START: case 0: abort(); } struct qbe_statement linvalid, lvalid; struct qbe_value binvalid = mklabel(ctx, &linvalid, ".%d"); struct qbe_value bvalid = mklabel(ctx, &lvalid, ".%d"); pushi(ctx->current, NULL, Q_JNZ, &valid, &bvalid, &binvalid, NULL); push(&ctx->current->body, &linvalid); gen_fixed_abort(ctx, expr->loc, ABORT_OOB); push(&ctx->current->body, &lvalid); } static struct gen_value gen_expr_assign_slice_expandable(struct gen_context *ctx, struct qbe_value obase, struct qbe_value ostart, struct qbe_value olen, const struct expression *rvalue) { size_t arrlen = rvalue->result->array.length; size_t membsz = rvalue->result->array.members->size; struct qbe_value cmplen = constl(arrlen); gen_fixed_assert(ctx, rvalue->loc, ABORT_OOB, Q_CULEL, &cmplen, &olen); struct qbe_value sz = constl(membsz); struct qbe_value off = mkqtmp(ctx, ctx->arch.sz, ".%d"); struct gen_value odata = mkgtemp(ctx, &builtin_type_uintptr, ".%d"); struct qbe_value qodata = mkqval(ctx, &odata); pushi(ctx->current, &off, Q_MUL, &ostart, &sz, NULL); pushi(ctx->current, &qodata, Q_ADD, &obase, &off, NULL); gen_expr_at(ctx, rvalue, odata); // perform the copy minus the first element struct qbe_value loffset = constl(membsz * (arrlen - 1)); struct qbe_value last = mkqtmp(ctx, ctx->arch.ptr, ".%d"); pushi(ctx->current, &last, Q_ADD, &qodata, &loffset, NULL); struct qbe_value noffset = constl(membsz * arrlen); struct qbe_value next = mkqtmp(ctx, ctx->arch.ptr, ".%d"); pushi(ctx->current, &next, Q_ADD, &qodata, &noffset, NULL); pushi(ctx->current, &olen, Q_MUL, &olen, &sz, NULL); pushi(ctx->current, &olen, Q_SUB, &olen, &noffset, NULL); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memcpy, &next, &last, &olen, NULL); return gv_void; } static struct gen_value gen_expr_assign_slice(struct gen_context *ctx, const struct expression *expr) { assert(expr->assign.object->type == EXPR_SLICE); struct gen_value obj = gen_expr(ctx, expr->assign.object->slice.object); obj = gen_autoderef(ctx, obj); const struct type *srctype = type_dealias(NULL, obj.type); struct qbe_value optr, ostart, olen; struct qbe_value oldlen_, *oldlen = &oldlen_, oldcap_, *oldcap = &oldcap_; if (srctype->storage == STORAGE_ARRAY) { optr = mkcopy(ctx, &obj, ".%d"); if (srctype->array.length == SIZE_UNDEFINED) { oldlen = oldcap = NULL; } else { *oldlen = *oldcap = constl(srctype->array.length); } } else { struct gen_slice sl = gen_slice_ptrs(ctx, obj); load_slice_data(ctx, &sl, &optr, oldlen, oldcap); } gen_subslice_info(ctx, expr->assign.object, oldlen, oldcap, &ostart, NULL, &olen, NULL); const struct type *vtype = type_dealias(NULL, expr->assign.value->result); if (vtype->storage == STORAGE_ARRAY && vtype->array.expandable) { return gen_expr_assign_slice_expandable(ctx, optr, ostart, olen, expr->assign.value); } struct gen_value val = gen_expr(ctx, expr->assign.value); struct qbe_value qval = mkqval(ctx, &val); struct qbe_value vlen = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value step = constl(ctx->arch.ptr->size); struct qbe_value ptr = mkqtmp(ctx, ctx->arch.ptr, ".%d"); pushi(ctx->current, &ptr, Q_ADD, &qval, &step, NULL); pushi(ctx->current, &vlen, Q_LOADL, &ptr, NULL); gen_fixed_assert(ctx, expr->loc, ABORT_OOB, Q_CEQL, &olen, &vlen); struct qbe_value vptr = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value os = mkqtmp(ctx, ctx->arch.sz, ".%d"); struct qbe_value tmp = constl(vtype->array.members->size); pushi(ctx->current, &os, Q_MUL, &ostart, &tmp, NULL); pushi(ctx->current, &optr, Q_ADD, &optr, &os, NULL); pushi(ctx->current, &vptr, Q_LOADL, &qval, NULL); pushi(ctx->current, &olen, Q_MUL, &olen, &tmp, NULL); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memmove, &optr, &vptr, &olen, NULL); return gv_void; } static struct qbe_value extend(struct gen_context *ctx, struct qbe_value v, const struct type *type) { enum qbe_instr op; switch (type->size) { case 1: op = type_is_signed(NULL, type) ? Q_EXTSB : Q_EXTUB; break; case 2: op = type_is_signed(NULL, type) ? Q_EXTSH : Q_EXTUH; break; default: return v; } struct qbe_value temp = mkqtmp(ctx, &qbe_word, ".%d"); pushi(ctx->current, &temp, op, &v, NULL); return temp; } bool bin_extend[BIN_LAST + 1][2] = { [BIN_BAND] = { false, false }, [BIN_BOR] = { false, false }, [BIN_DIV] = { true, true }, [BIN_GREATER] = { true, true }, [BIN_GREATEREQ] = { true, true }, [BIN_LAND] = { true, true }, [BIN_LEQUAL] = { true, true }, [BIN_LESS] = { true, true }, [BIN_LESSEQ] = { true, true }, [BIN_LOR] = { true, true }, [BIN_LSHIFT] = { false, true }, [BIN_LXOR] = { true, true }, [BIN_MINUS] = { true, false }, [BIN_MODULO] = { true, true }, [BIN_NEQUAL] = { true, true }, [BIN_PLUS] = { false, false }, [BIN_RSHIFT] = { true, true }, [BIN_TIMES] = { false, false }, [BIN_BXOR] = { false, false }, }; static struct gen_value gen_expr_assign(struct gen_context *ctx, const struct expression *expr) { struct expression *object = expr->assign.object; struct expression *value = expr->assign.value; if (object->type == EXPR_SLICE) { return gen_expr_assign_slice(ctx, expr); } struct gen_value obj; switch (object->type) { case EXPR_ACCESS: obj = gen_expr_access_addr(ctx, object); break; case EXPR_UNARITHM: assert(object->unarithm.op == UN_DEREF); // Invariant obj = gen_expr(ctx, object->unarithm.operand); assert(type_dealias(NULL, obj.type)->storage == STORAGE_POINTER); obj.type = type_dealias(NULL, obj.type)->pointer.referent; break; default: abort(); // Invariant } if (expr->assign.op == BIN_LEQUAL || value->result->storage == STORAGE_NEVER) { struct gen_value rvalue = gen_expr(ctx, value); gen_store(ctx, obj, rvalue); } else if (expr->assign.op == BIN_LAND || expr->assign.op == BIN_LOR) { struct qbe_statement lrval, lshort; struct qbe_value brval = mklabel(ctx, &lrval, ".%d"); struct qbe_value bshort = mklabel(ctx, &lshort, ".%d"); struct gen_value load = gen_load(ctx, obj); struct qbe_value qload = mkqval(ctx, &load); if (expr->binarithm.op == BIN_LAND) { pushi(ctx->current, NULL, Q_JNZ, &qload, &brval, &bshort, NULL); } else { pushi(ctx->current, NULL, Q_JNZ, &qload, &bshort, &brval, NULL); } push(&ctx->current->body, &lrval); gen_expr_at(ctx, value, obj); pushi(ctx->current, NULL, Q_JMP, &bshort, NULL); push(&ctx->current->body, &lshort); } else { struct gen_value lvalue = gen_load(ctx, obj); struct gen_value rvalue = gen_expr(ctx, value); struct qbe_value qlval = mkqval(ctx, &lvalue); struct qbe_value ilval = qlval; struct qbe_value qrval = mkqval(ctx, &rvalue); enum qbe_instr instr = binarithm_for_op(ctx, expr->assign.op, lvalue.type); if (bin_extend[expr->assign.op][0]) { ilval = extend(ctx, ilval, lvalue.type); } if (bin_extend[expr->assign.op][1]) { qrval = extend(ctx, qrval, rvalue.type); } pushi(ctx->current, &qlval, instr, &ilval, &qrval, NULL); gen_store(ctx, obj, lvalue); } return gv_void; } static struct gen_value gen_expr_binarithm(struct gen_context *ctx, const struct expression *expr) { const struct type *ltype = type_dealias(NULL, expr->binarithm.lvalue->result); const struct type *rtype = type_dealias(NULL, expr->binarithm.rvalue->result); struct gen_value result = mkgtemp(ctx, expr->result, ".%d"); struct qbe_value qresult = mkqval(ctx, &result); if (expr->binarithm.op == BIN_LAND || expr->binarithm.op == BIN_LOR) { struct qbe_statement lrval, lshort; struct qbe_value brval = mklabel(ctx, &lrval, ".%d"); struct qbe_value bshort = mklabel(ctx, &lshort, ".%d"); struct gen_value lval = gen_expr(ctx, expr->binarithm.lvalue); struct qbe_value qlval = mkqval(ctx, &lval); pushi(ctx->current, &qresult, Q_COPY, &qlval, NULL); if (expr->binarithm.op == BIN_LAND) { pushi(ctx->current, NULL, Q_JNZ, &qresult, &brval, &bshort, NULL); } else { pushi(ctx->current, NULL, Q_JNZ, &qresult, &bshort, &brval, NULL); } push(&ctx->current->body, &lrval); struct gen_value rval = gen_expr(ctx, expr->binarithm.rvalue); struct qbe_value qrval = mkqval(ctx, &rval); pushi(ctx->current, &qresult, Q_COPY, &qrval, NULL); if (expr->binarithm.rvalue->result->storage != STORAGE_NEVER) { pushi(ctx->current, NULL, Q_JMP, &bshort, NULL); } push(&ctx->current->body, &lshort); return result; } struct gen_value lvalue = gen_expr(ctx, expr->binarithm.lvalue); struct gen_value rvalue = gen_expr(ctx, expr->binarithm.rvalue); struct qbe_value qlval = mkqval(ctx, &lvalue); struct qbe_value qrval = mkqval(ctx, &rvalue); if (bin_extend[expr->assign.op][0]) { qlval = extend(ctx, qlval, ltype); } if (bin_extend[expr->assign.op][1]) { qrval = extend(ctx, qrval, rtype); } assert((ltype->storage == STORAGE_STRING) == (rtype->storage == STORAGE_STRING)); if (ltype->storage == STORAGE_STRING) { pushi(ctx->current, &qresult, Q_CALL, &ctx->rt.strcmp, &qlval, &qrval, NULL); if (expr->binarithm.op == BIN_NEQUAL) { struct qbe_value one = constl(1); pushi(ctx->current, &qresult, Q_XOR, &qresult, &one, NULL); } else { assert(expr->binarithm.op == BIN_LEQUAL); } return result; } enum qbe_instr instr = binarithm_for_op(ctx, expr->binarithm.op, expr->binarithm.lvalue->result); pushi(ctx->current, &qresult, instr, &qlval, &qrval, NULL); return result; } static void gen_expr_binding_unpack_static(struct gen_context *ctx, const struct expression_binding *binding) { assert(binding->object == NULL); struct tuple_literal *tuplelit = binding->initializer->literal.tuple; for (const struct binding_unpack *unpack = binding->unpack; unpack; unpack = unpack->next) { if (unpack->object == NULL) { goto done; } assert(unpack->object->otype == O_DECL); struct declaration decl = { .decl_type = DECL_GLOBAL, .ident = unpack->object->ident, .global = { .type = unpack->object->type, .value = tuplelit->value, }, }; gen_global_decl(ctx, &decl); done: tuplelit = tuplelit->next; } } static void gen_expr_binding_unpack(struct gen_context *ctx, const struct expression_binding *binding) { assert(binding->object == NULL); const struct type *type = binding->initializer->result; struct gen_value tuple_gv = mkgtemp(ctx, type, "tupleunpack.%d"); struct qbe_value tuple_qv = mklval(ctx, &tuple_gv); struct qbe_value sz = constl(type->size); enum qbe_instr alloc = alloc_for_align(type->align); pushprei(ctx->current, &tuple_qv, alloc, &sz, NULL); gen_expr_at(ctx, binding->initializer, tuple_gv); for (const struct binding_unpack *unpack = binding->unpack; unpack; unpack = unpack->next) { if (unpack->object == NULL) { continue; } assert(unpack->object->otype != O_DECL); struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->value = mkgtemp(ctx, unpack->object->type, "binding.%d"); gb->object = unpack->object; gb->next = ctx->bindings; ctx->bindings = gb; struct qbe_value item_qv = mklval(ctx, &gb->value); struct qbe_value offs = constl(unpack->offset); pushprei(ctx->current, &item_qv, Q_ADD, &tuple_qv, &offs, NULL); } } static struct gen_value gen_expr_binding(struct gen_context *ctx, const struct expression *expr) { for (const struct expression_binding *binding = &expr->binding; binding; binding = binding->next) { if (binding->unpack) { if (binding->unpack->object->otype == O_DECL) { gen_expr_binding_unpack_static(ctx, binding); } else { gen_expr_binding_unpack(ctx, binding); } continue; } if (binding->object->otype == O_DECL) { // static binding struct declaration decl = { .decl_type = DECL_GLOBAL, .ident = binding->object->ident, .global = { .type = binding->object->type, .value = binding->initializer, }, }; gen_global_decl(ctx, &decl); continue; } const struct type *type = binding->object->type; if (type->size == 0) { gen_expr(ctx, binding->initializer); continue; } struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->value = mkgtemp(ctx, type, "binding.%d"); gb->object = binding->object; gb->next = ctx->bindings; ctx->bindings = gb; struct qbe_value qv = mklval(ctx, &gb->value); struct qbe_value sz = constl(type->size); enum qbe_instr alloc = alloc_for_align(type->align); pushprei(ctx->current, &qv, alloc, &sz, NULL); gen_expr_at(ctx, binding->initializer, gb->value); } return gv_void; } static struct gen_value gen_expr_control(struct gen_context *ctx, const struct expression *expr) { struct gen_scope *scope = gen_scope_lookup(ctx, expr->control.scope); if (expr->control.value) { struct gen_value result = gen_expr_with(ctx, expr->control.value, scope->out); branch_copyresult(ctx, result, scope->result, scope->out); if (expr->control.value->result->storage == STORAGE_NEVER) { return gv_void; } } struct gen_scope *deferred = ctx->scope; while (deferred != NULL) { gen_defers(ctx, deferred); if (deferred == scope) { break; } deferred = deferred->parent; } switch (expr->type) { case EXPR_BREAK: assert(scope->scope->class == SCOPE_LOOP); pushi(ctx->current, NULL, Q_JMP, scope->end, NULL); break; case EXPR_CONTINUE: assert(scope->scope->class == SCOPE_LOOP); pushi(ctx->current, NULL, Q_JMP, scope->after, NULL); break; case EXPR_YIELD: assert(scope->scope->class == SCOPE_COMPOUND); pushi(ctx->current, NULL, Q_JMP, scope->end, NULL); break; default: abort(); // Invariant } return gv_void; } static struct gen_value gen_expr_call(struct gen_context *ctx, const struct expression *expr) { struct gen_value lvalue = gen_expr(ctx, expr->call.lvalue); lvalue = gen_autoderef(ctx, lvalue); const struct type *rtype = type_dealias(NULL, lvalue.type); assert(rtype->storage == STORAGE_FUNCTION); struct qbe_statement call = { .type = Q_INSTR, .instr = Q_CALL, }; struct gen_value rval = gv_void; if (rtype->func.result->size != 0 && rtype->func.result->size != SIZE_UNDEFINED) { rval = mkgtemp(ctx, rtype->func.result, ".%d"); call.out = xcalloc(1, sizeof(struct qbe_value)); *call.out = mkqval(ctx, &rval); call.out->type = qtype_lookup(ctx, rtype->func.result, false); } bool cvar = false; struct type_func_param *param = rtype->func.params; struct qbe_arguments *args, **next = &call.args; args = *next = xcalloc(1, sizeof(struct qbe_arguments)); args->value = mkqval(ctx, &lvalue); next = &args->next; for (struct call_argument *carg = expr->call.args; carg; carg = carg->next) { struct gen_value arg = gen_expr(ctx, carg->value); if (carg->value->result->size == 0) { continue; } args = *next = xcalloc(1, sizeof(struct qbe_arguments)); if (carg->value->result->storage == STORAGE_NEVER) { return rval; } args->value = mkqval(ctx, &arg); args->value.type = qtype_lookup(ctx, carg->value->result, false); next = &args->next; if (param) { param = param->next; } if (!param && !cvar && rtype->func.variadism == VARIADISM_C) { cvar = true; args = *next = xcalloc(1, sizeof(struct qbe_arguments)); args->value.kind = QV_VARIADIC; next = &args->next; } } if (rtype->func.result->storage == STORAGE_NEVER) { for (struct gen_scope *scope = ctx->scope; scope; scope = scope->parent) { gen_defers(ctx, scope); if (scope->scope->class == SCOPE_DEFER) { break; } } } push(&ctx->current->body, &call); if (rtype->func.result->storage == STORAGE_NEVER) { pushi(ctx->current, NULL, Q_HLT, NULL); } return rval; } static struct gen_value gen_expr_cast(struct gen_context *ctx, const struct expression *expr); static struct gen_value gen_subset_match_tests(struct gen_context *ctx, struct qbe_value bmatch, struct qbe_value bnext, struct qbe_value tag, const struct type *type); static struct gen_value gen_nested_match_tests(struct gen_context *ctx, struct gen_value object, struct qbe_value bmatch, struct qbe_value bnext, struct qbe_value tag, const struct type *type); static struct gen_value gen_type_assertion_or_test(struct gen_context *ctx, const struct expression *expr, struct gen_value base) { assert(expr->cast.kind == C_TEST || expr->cast.kind == C_ASSERTION); const struct type *want = expr->cast.secondary; struct qbe_value tag = mkqtmp(ctx, &qbe_word, ".%d"); struct qbe_value qbase = mkqval(ctx, &base); gen_load_tag(ctx, &tag, &qbase, expr->cast.value->result); struct qbe_statement failedl, passedl; struct qbe_value bfailed, bpassed = mklabel(ctx, &passedl, "passed.%d"); if (expr->cast.kind == C_ASSERTION) { bfailed = mklabel(ctx, &failedl, "failed.%d"); } else { bfailed = bpassed; } struct gen_value result = {0}; if (tagged_select_subtype(NULL, expr->cast.value->result, want, true)) { result = gen_nested_match_tests(ctx, base, bpassed, bfailed, tag, want); } else if (tagged_subset_compat(NULL, expr->cast.value->result, want)) { result = gen_subset_match_tests(ctx, bpassed, bfailed, tag, type_dealias(NULL, want)); } else { abort(); } if (expr->cast.kind == C_ASSERTION) { push(&ctx->current->body, &failedl); gen_fixed_abort(ctx, expr->loc, ABORT_TYPE_ASSERTION); } push(&ctx->current->body, &passedl); return result; } static void gen_expr_cast_slice_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { const struct type *from = type_dealias(NULL, expr->cast.value->result); if (from->storage == STORAGE_POINTER) { from = type_dealias(NULL, from->pointer.referent); } assert(from->storage == STORAGE_ARRAY); assert(from->array.length != SIZE_UNDEFINED); struct qbe_value data; struct gen_value value = gen_expr(ctx, expr->cast.value); if (from->array.length == 0) { data = constl(0); } else { data = mkqval(ctx, &value); } struct qbe_value ln = constl(from->array.length); struct gen_slice sl = gen_slice_ptrs(ctx, out); store_slice_data(ctx, &sl, &data, &ln, &ln); } static void gen_expr_cast_tagged_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { // Generate a cast from type "from" to type "to". Two cases are handled // here: // // 1. "from" is a tagged union which is compatible with "to" // let from: (i32 | void); // let to: (i32 | i64 | void) = from; // 2. "from" is a member of "to" // let from: i32; // let to: (i32 | void) = from; assert(expr->type == EXPR_CAST); const struct type *to = expr->result, *from = expr->cast.value->result; const struct type *subtype = tagged_select_subtype(NULL, to, from, true); if (!subtype) { // Compatible tagged unions // // Create a new "out" parameter with the same storage location // as the target of this expression, but with the "from" type. // The "from" type is a tagged union and will produce a // compatible tagged union when evaluated there. struct gen_value out2 = out; out2.type = from; gen_expr_at(ctx, expr->cast.value, out2); if (expr->cast.kind == C_ASSERTION) { gen_type_assertion_or_test(ctx, expr, out2); } } else { // "from" is a member of "to" // // Update the tag and generate the "from" expression at the // offset of the value field. struct qbe_value qout = mkqval(ctx, &out); struct qbe_value id = constw(subtype->id); gen_store_tag(ctx, &qout, to, &id); if (subtype->size == 0) { gen_expr(ctx, expr->cast.value); // side-effects return; } struct gen_value storage = mkgtemp(ctx, subtype, ".%d"); struct qbe_value qstor = mklval(ctx, &storage); struct qbe_value offs = compute_tagged_memb_offset(from); pushi(ctx->current, &qstor, Q_ADD, &qout, &offs, NULL); gen_expr_at(ctx, expr->cast.value, storage); } } static bool cast_prefers_at(const struct expression *expr) { const struct type *to = expr->result, *from = expr->cast.value->result; if (expr->cast.kind == C_TEST) { return false; } // tagged => *; subtype compatible if (type_dealias(NULL, from)->storage == STORAGE_TAGGED && tagged_select_subtype(NULL, from, to, true)) { return false; } // * => tagged if (type_dealias(NULL, to)->storage == STORAGE_TAGGED) { return true; } // array => array if (type_dealias(NULL, to)->storage == STORAGE_ARRAY && type_dealias(NULL, from)->storage == STORAGE_ARRAY) { return true; } // array => slice if (type_dealias(NULL, to)->storage == STORAGE_SLICE) { switch (type_dealias(NULL, from)->storage) { case STORAGE_ARRAY: return true; case STORAGE_POINTER: from = type_dealias(NULL, from)->pointer.referent; return type_dealias(NULL, from)->storage == STORAGE_ARRAY; default: return false; } } return false; } static void gen_expr_cast_array_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { const struct type *typeout = type_dealias(NULL, expr->result); const struct type *typein = type_dealias(NULL, expr->cast.value->result); gen_expr_at(ctx, expr->cast.value, out); if (!typein->array.expandable) { return; } assert(typein->array.length != SIZE_UNDEFINED && typeout->array.length != SIZE_UNDEFINED); assert(typeout->array.length >= typein->array.length); const struct type *membtype = typein->array.members; size_t remain = typeout->array.length - typein->array.length; struct qbe_value base = mkqval(ctx, &out); struct qbe_value offs = constl((typein->array.length - 1) * membtype->size); struct gen_value next = mkgtemp(ctx, membtype, ".%d"); struct qbe_value ptr = mklval(ctx, &next); struct gen_value item = mkgtemp(ctx, membtype, "item.%d"); struct qbe_value qitem = mklval(ctx, &item); pushi(ctx->current, &qitem, Q_ADD, &base, &offs, NULL); if (remain * membtype->size <= 128) { struct gen_value last = gen_load(ctx, item); for (size_t n = typein->array.length; n < typeout->array.length; ++n) { struct qbe_value offs = constl(n * membtype->size); pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); gen_store(ctx, next, last); } return; } offs = constl(typein->array.length * membtype->size); pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); struct qbe_value dtemp = mklval(ctx, &next); struct qbe_value stemp = mklval(ctx, &item); struct qbe_value sz = constl(remain * membtype->size); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memcpy, &dtemp, &stemp, &sz, NULL); } static void gen_expr_cast_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { if (!cast_prefers_at(expr)) { struct gen_value result = gen_expr_cast(ctx, expr); gen_store(ctx, out, result); return; } if (expr->cast.lowered) { pushc(ctx->current, "gen lowered cast"); } const struct type *to = expr->result; switch (type_dealias(NULL, to)->storage) { case STORAGE_SLICE: gen_expr_cast_slice_at(ctx, expr, out); break; case STORAGE_TAGGED: gen_expr_cast_tagged_at(ctx, expr, out); break; case STORAGE_ARRAY: gen_expr_cast_array_at(ctx, expr, out); break; default: abort(); // Invariant } } static struct qbe_value nested_tagged_offset(const struct type *tu, const struct type *targed); static struct gen_value gen_expr_cast(struct gen_context *ctx, const struct expression *expr) { const struct type *to = expr->cast.secondary, *from = expr->cast.value->result; if (expr->cast.kind != C_CAST) { bool is_valid_tagged, is_valid_pointer; is_valid_tagged = type_dealias(NULL, from)->storage == STORAGE_TAGGED && (tagged_select_subtype(NULL, from, to, true) || tagged_subset_compat(NULL, from, to)); is_valid_pointer = type_dealias(NULL, from)->storage == STORAGE_POINTER && (type_dealias(NULL, to)->storage == STORAGE_POINTER || type_dealias(NULL, to)->storage == STORAGE_NULL); assert(is_valid_tagged || is_valid_pointer); if (expr->cast.kind == C_TEST && is_valid_tagged) { return gen_type_assertion_or_test(ctx, expr, gen_expr(ctx, expr->cast.value)); } } if (cast_prefers_at(expr)) { struct gen_value out = mkgtemp(ctx, expr->result, "object.%d"); struct qbe_value base = mkqval(ctx, &out); struct qbe_value sz = constl(expr->result->size); enum qbe_instr alloc = alloc_for_align(expr->result->align); pushprei(ctx->current, &base, alloc, &sz, NULL); gen_expr_cast_at(ctx, expr, out); return out; } if (expr->cast.lowered) { pushc(ctx->current, "gen lowered cast"); } // Special cases bool want_null = false; switch (type_dealias(NULL, to)->storage) { case STORAGE_NULL: want_null = true; // fallthrough case STORAGE_POINTER: if (type_dealias(NULL, from)->storage == STORAGE_SLICE) { struct gen_value value = gen_expr(ctx, expr->cast.value); value.type = to; return gen_load(ctx, value); } if (type_dealias(NULL, from)->storage != STORAGE_POINTER) { break; } struct gen_value val = gen_expr(ctx, expr->cast.value); struct qbe_value qval = mkqval(ctx, &val); struct qbe_value zero = constl(0); enum qbe_instr compare = want_null ? Q_CEQL : Q_CNEL; if (expr->cast.kind == C_TEST) { struct gen_value out = mkgtemp(ctx, &builtin_type_bool, ".%d"); struct qbe_value qout = mkqval(ctx, &out); pushi(ctx->current, &qout, compare, &qval, &zero, NULL); return out; } else if (expr->cast.kind == C_ASSERTION) { gen_fixed_assert(ctx, expr->loc, ABORT_TYPE_ASSERTION, compare, &qval, &zero); if (want_null) { return (struct gen_value){ .kind = GV_CONST, .type = &builtin_type_null, .lval = 0, }; } } val.type = to; return val; default: break; } // Special case: cast to type that doesn't have a size if (type_dealias(NULL, to)->size == 0) { gen_expr(ctx, expr->cast.value); // Side-effects return gv_void; } // Special case: tagged => non-tagged if (type_dealias(NULL, from)->storage == STORAGE_TAGGED) { struct gen_value value = gen_expr(ctx, expr->cast.value); struct qbe_value base = mkcopy(ctx, &value, ".%d"); if (expr->cast.kind == C_ASSERTION) { gen_type_assertion_or_test(ctx, expr, value); } struct qbe_value align = nested_tagged_offset( expr->cast.value->result, expr->cast.secondary); pushi(ctx->current, &base, Q_ADD, &base, &align, NULL); struct gen_value storage = (struct gen_value){ .kind = GV_TEMP, .type = to, .name = base.name, }; return gen_load(ctx, storage); } // Special case: no conversion required if (type_dealias(NULL, to)->storage == type_dealias(NULL, from)->storage && to->size == from->size) { struct gen_value value = gen_expr(ctx, expr->cast.value); value.type = to; return value; } struct gen_value value = gen_expr(ctx, expr->cast.value); struct qbe_value qvalue = mkqval(ctx, &value); struct gen_value result = mkgtemp(ctx, expr->result, ".%d"); struct qbe_value qresult = mkqval(ctx, &result); struct gen_value intermediate; struct qbe_value qintermediate; from = lower_flexible(NULL, from, NULL); enum qbe_instr op; bool is_signed = type_is_signed(NULL, from); enum type_storage fstor = type_dealias(NULL, from)->storage, tstor = type_dealias(NULL, to)->storage; switch (tstor) { case STORAGE_ENUM: case STORAGE_U8: case STORAGE_I8: case STORAGE_I16: case STORAGE_U16: case STORAGE_I32: case STORAGE_U32: case STORAGE_INT: case STORAGE_UINT: case STORAGE_I64: case STORAGE_U64: case STORAGE_UINTPTR: case STORAGE_RUNE: case STORAGE_SIZE: if (type_is_integer(NULL, from) || fstor == STORAGE_RUNE) { if (to->size <= from->size) { op = Q_COPY; } else { switch (from->size) { case 4: op = is_signed ? Q_EXTSW : Q_EXTUW; break; case 2: op = is_signed ? Q_EXTSH : Q_EXTUH; break; case 1: op = is_signed ? Q_EXTSB : Q_EXTUB; break; default: assert(0); // Invariant } } } else if (fstor == STORAGE_POINTER || fstor == STORAGE_NULL) { assert(tstor == STORAGE_UINTPTR); op = Q_COPY; } else if (type_is_float(NULL, from)) { if (type_is_signed(NULL, to)) { switch (fstor) { case STORAGE_F32: op = Q_STOSI; break; case STORAGE_F64: op = Q_DTOSI; break; default: abort(); // Invariant } } else { switch (fstor) { case STORAGE_F32: op = Q_STOUI; break; case STORAGE_F64: op = Q_DTOUI; break; default: abort(); // Invariant } } } else { abort(); // Invariant } pushi(ctx->current, &qresult, op, &qvalue, NULL); break; case STORAGE_F32: case STORAGE_F64: if (type_is_float(NULL, from) && from->size == to->size) { op = Q_COPY; } else if (type_is_float(NULL, from) && to->size < from->size) { op = Q_TRUNCD; } else if (type_is_float(NULL, from) && to->size > from->size) { op = Q_EXTS; } else if (type_is_integer(NULL, from)) { if (type_is_signed(NULL, from)) { switch (from->size) { case 1: case 2: intermediate = mkgtemp(ctx, &builtin_type_i32, ".%d"); qintermediate = mkqval(ctx, &intermediate); pushi(ctx->current, &qintermediate, from->size == 1? Q_EXTSB : Q_EXTSH, &qvalue, NULL); qvalue = qintermediate; /* fallthrough */ case 4: op = Q_SWTOF; break; case 8: op = Q_SLTOF; break; default: abort(); // Invariant } } else { switch (from->size) { case 1: case 2: intermediate = mkgtemp(ctx, &builtin_type_i32, ".%d"); qintermediate = mkqval(ctx, &intermediate); pushi(ctx->current, &qintermediate, from->size == 1? Q_EXTUB : Q_EXTUH, &qvalue, NULL); qvalue = qintermediate; /* fallthrough */ case 4: op = Q_UWTOF; break; case 8: op = Q_ULTOF; break; default: abort(); // Invariant } } } else { abort(); // Invariant } pushi(ctx->current, &qresult, op, &qvalue, NULL); break; case STORAGE_NULL: case STORAGE_POINTER: pushi(ctx->current, &qresult, Q_COPY, &qvalue, NULL); break; case STORAGE_ARRAY: assert(from->storage == STORAGE_ARRAY); pushi(ctx->current, &qresult, Q_COPY, &qvalue, NULL); break; case STORAGE_SLICE: assert(from->storage == STORAGE_SLICE); pushi(ctx->current, &qresult, Q_COPY, &qvalue, NULL); break; case STORAGE_ALIAS: case STORAGE_BOOL: case STORAGE_ERROR: case STORAGE_FCONST: case STORAGE_FUNCTION: case STORAGE_ICONST: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_RCONST: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: abort(); // Invariant } return result; } static struct gen_value gen_expr_compound_with(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { struct qbe_statement lend; struct qbe_value bend = mklabel(ctx, &lend, ".%d"); struct gen_scope *scope = push_scope(ctx, expr->compound.scope); scope->end = &bend; struct gen_value gvout = gv_void; if (!out) { gvout = mkgtemp(ctx, expr->result, ".%d"); } scope->out = out; scope->result = gvout; const struct expressions *exprs; for (exprs = &expr->compound.exprs; exprs->next; exprs = exprs->next) { gen_expr(ctx, exprs->expr); } struct gen_value last = gen_expr_with(ctx, exprs->expr, out); branch_copyresult(ctx, last, gvout, out); pop_scope(ctx); push(&ctx->current->body, &lend); return gvout; } static void gen_literal_array_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { struct array_literal *aexpr = expr->literal.array; struct qbe_value base = mkqval(ctx, &out); size_t n = 0; const struct type *atype = type_dealias(NULL, expr->result); size_t msize = atype->array.members->size; struct gen_value item = mkgtemp(ctx, atype->array.members, "item.%d"); struct qbe_value ptr; for (const struct array_literal *ac = aexpr; ac; ac = ac->next) { struct qbe_value offs = constl(n * msize); ptr = mklval(ctx, &item); pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); gen_expr_at(ctx, ac->value, item); ++n; } assert(n == atype->array.length); if (!atype->array.expandable || n == 0) { return; } assert(out.type); const struct type_array arr = type_dealias(NULL, out.type)->array; if (arr.length <= n) { return; } struct qbe_value nsize = constl(n * msize); struct qbe_value next = mkqtmp(ctx, ctx->arch.ptr, ".%d"); pushi(ctx->current, &next, Q_ADD, &base, &nsize, NULL); struct qbe_value qlen = constl((arr.length - n) * msize); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memcpy, &next, &ptr, &qlen, NULL); } static void gen_literal_slice_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { // Slice literals are stored as array literals. struct array_literal *aexpr = expr->literal.array; enum qbe_instr store = store_for_type(ctx, &builtin_type_size); struct qbe_value base = mklval(ctx, &out); struct qbe_value ln; if (aexpr) { struct expression *first = aexpr->value; struct qbe_type *qt = xcalloc(1, sizeof(struct qbe_type)); qt->stype = Q_LONG; struct qbe_value aobj = mkqtmp(ctx, qt, "object.%d"); size_t n = 0; struct gen_value item = mkgtemp(ctx, first->result, "item.%d"); struct qbe_value ptr; for (const struct array_literal *ac = aexpr; ac; ac = ac->next) { struct qbe_value offs = constl(n * first->result->size); ptr = mklval(ctx, &item); pushi(ctx->current, &ptr, Q_ADD, &aobj, &offs, NULL); gen_expr_at(ctx, ac->value, item); ++n; } struct qbe_value asz = constl(n); enum qbe_instr alloc = alloc_for_align(first->result->align); pushprei(ctx->current, &aobj, alloc, &asz, NULL); ln = constl(n); pushi(ctx->current, NULL, store, &aobj, &base, NULL); } else { ln = constl(0); struct qbe_value tmp = constl(0); pushi(ctx->current, NULL, store, &tmp, &base, NULL); } struct qbe_value qptr = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value sz = constl(builtin_type_size.size); pushi(ctx->current, &qptr, Q_ADD, &base, &sz, NULL); pushi(ctx->current, NULL, store, &ln, &qptr, NULL); pushi(ctx->current, &qptr, Q_ADD, &qptr, &sz, NULL); pushi(ctx->current, NULL, store, &ln, &qptr, NULL); } static struct qbe_data_item *gen_data_item(struct gen_context *, const struct expression *, struct qbe_data_item *); static struct gen_value gen_literal_string(struct gen_context *ctx, const struct expression *expr) { struct qbe_def *str = xcalloc(1, sizeof(struct qbe_def)); str->kind = Q_DATA; str->data.align = ALIGN_UNDEFINED; str->exported = false; str->name = gen_name(&ctx->id, "strliteral.%d"); str->file = expr->loc.file; gen_data_item(ctx, expr, &str->data.items); qbe_append_def(ctx->out, str); return (struct gen_value){ .kind = GV_GLOBAL, .type = expr->result, .name = xstrdup(str->name), }; } static void gen_literal_struct_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { // TODO: Merge me into literal expressions struct qbe_value base = mkqval(ctx, &out); struct gen_value ftemp = mkgtemp(ctx, &builtin_type_void, "field.%d"); for (const struct struct_literal *field = expr->literal._struct; field; field = field->next) { assert(field->value); struct qbe_value offs = constl(field->field->offset); ftemp.type = field->value->result; struct qbe_value ptr = mklval(ctx, &ftemp); pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); gen_expr_at(ctx, field->value, ftemp); } } static void gen_literal_tagged_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { struct qbe_value qout = mklval(ctx, &out); const struct type *subtype = expr->literal.tagged.tag; struct qbe_value id = constw(subtype->id); gen_store_tag(ctx, &qout, expr->result, &id); if (subtype->size == 0) { return; } struct gen_value storage = mkgtemp(ctx, subtype, ".%d"); struct qbe_value qstor = mklval(ctx, &storage); struct qbe_value offs = compute_tagged_memb_offset(subtype); pushi(ctx->current, &qstor, Q_ADD, &qout, &offs, NULL); gen_expr_at(ctx, expr->literal.tagged.value, storage); } static void gen_literal_tuple_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { // TODO: Merge me into literal expressions struct qbe_value base = mkqval(ctx, &out); struct gen_value ftemp = mkgtemp(ctx, &builtin_type_void, "field.%d"); for (const struct tuple_literal *field = expr->literal.tuple; field; field = field->next) { assert(field->value); struct qbe_value offs = constl(field->field->offset); ftemp.type = field->value->result; struct qbe_value ptr = mklval(ctx, &ftemp); pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); gen_expr_at(ctx, field->value, ftemp); } } static void gen_expr_literal_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { if (!type_is_aggregate(type_dealias(NULL, expr->result))) { struct gen_value val = gen_expr(ctx, expr); gen_store(ctx, out, val); return; } switch (type_dealias(NULL, expr->result)->storage) { case STORAGE_ARRAY: gen_literal_array_at(ctx, expr, out); break; case STORAGE_SLICE: gen_literal_slice_at(ctx, expr, out); break; case STORAGE_STRUCT: gen_literal_struct_at(ctx, expr, out); break; case STORAGE_TAGGED: gen_literal_tagged_at(ctx, expr, out); break; case STORAGE_TUPLE: gen_literal_tuple_at(ctx, expr, out); break; default: gen_store(ctx, out, gen_expr(ctx, expr)); } } static struct gen_value gen_expr_literal(struct gen_context *ctx, const struct expression *expr) { struct gen_value val = { .kind = GV_CONST, .type = expr->result, }; // Special cases switch (type_dealias(NULL, expr->result)->storage) { case STORAGE_BOOL: val.wval = expr->literal.bval ? 1 : 0; return val; case STORAGE_NULL: val.lval = 0; return val; case STORAGE_STRING: return gen_literal_string(ctx, expr); default: if (expr->result->size == 0) { return gv_void; } // Moving right along break; } if (type_is_aggregate(type_dealias(NULL, expr->result))) { struct gen_value out = mkgtemp(ctx, expr->result, "object.%d"); struct qbe_value base = mkqval(ctx, &out); struct qbe_value sz = constl(expr->result->size); enum qbe_instr alloc = alloc_for_align(expr->result->align); pushprei(ctx->current, &base, alloc, &sz, NULL); gen_expr_at(ctx, expr, out); return out; } if (expr->literal.object != NULL) { assert(expr->literal.ival == 0); val = gen_access_ident(ctx, expr->literal.object); val.type = expr->result; return val; } const struct qbe_type *qtype = qtype_lookup(ctx, expr->result, false); switch (qtype->stype) { case Q_BYTE: case Q_HALF: case Q_WORD: val.wval = (uint32_t)expr->literal.uval; return val; case Q_LONG: val.lval = expr->literal.uval; return val; case Q_SINGLE: pushc(ctx->current, "%f", (float)expr->literal.fval); val.sval = (float)expr->literal.fval; return val; case Q_DOUBLE: pushc(ctx->current, "%f", expr->literal.fval); val.dval = expr->literal.fval; return val; case Q__VOID: case Q__AGGREGATE: case Q__UNION: assert(0); // Invariant } abort(); // Invariant } static struct gen_value gen_expr_defer(struct gen_context *ctx, const struct expression *expr) { struct gen_defer *defer = xcalloc(1, sizeof(struct gen_defer)); defer->expr = expr; defer->next = ctx->scope->defers; ctx->scope->defers = defer; return gv_void; } static struct gen_value gen_expr_delete(struct gen_context *ctx, const struct expression *expr) { struct gen_value object; struct qbe_value qstart; const struct expression *dexpr = expr->delete.expr; if (dexpr->type == EXPR_SLICE) { object = gen_expr(ctx, dexpr->slice.object); } else { assert(dexpr->type == EXPR_ACCESS && dexpr->access.type == ACCESS_INDEX); object = gen_expr(ctx, dexpr->access.array); struct gen_value start = gen_expr(ctx, dexpr->access.index); qstart = mkqval(ctx, &start); } object = gen_autoderef(ctx, object); assert(type_dealias(NULL, object.type)->storage == STORAGE_SLICE); struct qbe_value data, qlen, qcap; struct gen_slice sl = gen_slice_ptrs(ctx, object); load_slice_data(ctx, &sl, &data, &qlen, &qcap); struct qbe_value qend; if (dexpr->type == EXPR_SLICE) { gen_subslice_info(ctx, dexpr, &qlen, &qcap, &qstart, &qend, NULL, NULL); } else { gen_fixed_assert(ctx, expr->loc, ABORT_OOB, Q_CULTL, &qstart, &qlen); struct qbe_value tmp = constl(1); qend = mkqtmp(ctx, qstart.type, ".%d"); pushi(ctx->current, &qend, Q_ADD, &qstart, &tmp, NULL); } struct qbe_value startptr = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value endptr = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value mlen = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value membsz = constl(type_dealias(NULL, object.type)->array.members->size); pushi(ctx->current, &startptr, Q_MUL, &qstart, &membsz, NULL); pushi(ctx->current, &startptr, Q_ADD, &startptr, &data, NULL); pushi(ctx->current, &endptr, Q_MUL, &qend, &membsz, NULL); pushi(ctx->current, &endptr, Q_ADD, &endptr, &data, NULL); pushi(ctx->current, &qlen, Q_SUB, &qlen, &qend, NULL); pushi(ctx->current, &mlen, Q_MUL, &qlen, &membsz, NULL); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memmove, &startptr, &endptr, &mlen, NULL); pushi(ctx->current, &qlen, Q_ADD, &qlen, &qstart, NULL); store_slice_data(ctx, &sl, NULL, &qlen, NULL); if (!expr->delete.is_static) { struct qbe_value qobj = mklval(ctx, &object); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.unensure, &qobj, &membsz, NULL); } return gv_void; } static struct gen_value gen_expr_for(struct gen_context *ctx, const struct expression *expr) { struct qbe_statement lloop, lbody, lvalid, lafter, lend; struct qbe_value bloop = mklabel(ctx, &lloop, "loop.%d"); struct qbe_value bbody = mklabel(ctx, &lbody, "body.%d"); struct qbe_value bvalid = mklabel(ctx, &lvalid, "valid.%d"); struct qbe_value bend = mklabel(ctx, &lend, ".%d"); struct qbe_value bafter = mklabel(ctx, &lafter, "after.%d"); struct gen_value gcur_object, ginitializer, gptr; struct qbe_value qcur_object, qinitializer, qptr, qcur_idx, qlength; enum for_kind kind = expr->_for.kind; if (kind == FOR_ACCUMULATOR && expr->_for.bindings != NULL) { gen_expr_binding(ctx, expr->_for.bindings); } if (kind == FOR_EACH_VALUE || kind == FOR_EACH_POINTER) { ginitializer = gen_autoderef(ctx, gen_expr(ctx, expr->_for.bindings->binding.initializer)); qinitializer = mklval(ctx, &ginitializer); const struct type *initializer_type = type_dealias(NULL, ginitializer.type); const struct type *var_type = initializer_type->array.members; if (kind == FOR_EACH_POINTER) { var_type = type_dealias(NULL, expr->_for.bindings->binding.object->type); } gcur_object = mkgtemp(ctx, var_type, "cur_object.%d"); qcur_object = mklval(ctx, &gcur_object); struct qbe_value qcur_object_sz = constl(var_type->size); enum qbe_instr alloc = alloc_for_align(var_type->align); pushprei(ctx->current, &qcur_object, alloc, &qcur_object_sz, NULL); if (initializer_type->storage == STORAGE_ARRAY) { gptr = mkgtemp(ctx, &builtin_type_uintptr, ".%d"); qptr = mklval(ctx, &gptr); pushi(ctx->current, &qptr, Q_COPY, &qinitializer, NULL); qlength = constl(initializer_type->array.length); } else { assert(initializer_type->storage == STORAGE_SLICE); qlength = mkqtmp(ctx, ctx->arch.ptr, "len.%d"); struct gen_slice slice = gen_slice_ptrs(ctx, ginitializer); load_slice_data(ctx, &slice, &qptr, &qlength, NULL); gptr = (struct gen_value){ .kind = GV_TEMP, .type = &builtin_type_uintptr, .name = qptr.name }; } struct qbe_value qzero = constl(0); qcur_idx = mkqtmp(ctx, ctx->arch.sz, "cur_idx.%d"); pushi(ctx->current, &qcur_idx, Q_COPY, &qzero, NULL); } push_scope(ctx, expr->_for.scope); ctx->scope->after = &bafter; ctx->scope->end = &bend; push(&ctx->current->body, &lloop); switch (kind) { case FOR_EACH_VALUE: case FOR_EACH_POINTER: { struct qbe_value qvalid = mkqtmp(ctx, &qbe_word, "valid.%d"); pushi(ctx->current, &qvalid, Q_CULTL, &qcur_idx, &qlength, NULL); pushi(ctx->current, NULL, Q_JNZ, &qvalid, &bvalid, &bend, NULL); push(&ctx->current->body, &lvalid); if (expr->_for.bindings->binding.unpack == NULL) { struct expression_binding *binding = &expr->_for.bindings->binding; if (type_dealias(NULL, binding->object->type)->size != 0) { struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->object = binding->object; gb->value = gcur_object; gb->next = ctx->bindings; ctx->bindings = gb; } } if (kind == FOR_EACH_VALUE) { gen_copy_aligned(ctx, gcur_object, gptr); struct binding_unpack *unpack = expr->_for.bindings->binding.unpack; if (unpack != NULL) { for (struct binding_unpack *cur_unpack = unpack; cur_unpack; cur_unpack = cur_unpack->next) { if (cur_unpack->object->type->size == 0) { continue; } struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->value = mkgtemp(ctx, cur_unpack->object->type, "unpack.%d"); gb->object = cur_unpack->object; gb->next = ctx->bindings; ctx->bindings = gb; struct qbe_value qoff = constl(cur_unpack->offset); struct qbe_value qitem = mklval(ctx, &gb->value); pushi(ctx->current, &qitem, Q_ADD, &qcur_object, &qoff, NULL); } } } else { // FOR_EACH_POINTER enum qbe_instr store = store_for_type(ctx, gcur_object.type); pushi(ctx->current, NULL, store, &qptr, &qcur_object, NULL); } struct qbe_value qone = constl(1); pushi(ctx->current, &qcur_idx, Q_ADD, &qcur_idx, &qone, NULL); break; } case FOR_EACH_ITERATOR: ginitializer = gen_expr(ctx, expr->_for.bindings->binding.initializer); qinitializer = mklval(ctx, &ginitializer); const struct type *initializer_type = type_dealias(NULL, ginitializer.type); struct qbe_value qtag = mkqtmp(ctx, &qbe_word, "tag.%d"); gen_load_tag(ctx, &qtag, &qinitializer, initializer_type); const struct type *done_type = NULL; for (const struct type_tagged_union *tu = &initializer_type->tagged; tu; tu = tu->next) { if (type_dealias(NULL, tu->type)->storage == STORAGE_DONE) { done_type = tu->type; break; } } struct qbe_value qdone_tag = constw(done_type->id); struct qbe_value qisdone = mkqtmp(ctx, &qbe_word, ".%d"); pushi(ctx->current, &qisdone, Q_CEQW, &qtag, &qdone_tag, NULL); pushi(ctx->current, NULL, Q_JNZ, &qisdone, &bend, &bvalid, NULL); push(&ctx->current->body, &lvalid); struct binding_unpack *unpack = expr->_for.bindings->binding.unpack; const struct type *var_type; if (unpack != NULL) { const struct type_tagged_union *tagged = &initializer_type->tagged; if (tagged->type->storage == STORAGE_TUPLE) { var_type = tagged->type; } else { var_type = tagged->next->type; assert(var_type->storage == STORAGE_TUPLE); } } else { var_type = expr->_for.bindings->binding.object->type; } struct qbe_value qptr = qinitializer; if (var_type->storage != STORAGE_TAGGED) { qptr = mkqtmp(ctx, ctx->arch.ptr, "cur_val.%d"); struct qbe_value qoffset = nested_tagged_offset( ginitializer.type, var_type); pushi(ctx->current, &qptr, Q_ADD, &qinitializer, &qoffset, NULL); } if (unpack != NULL) { for (struct binding_unpack *cur_unpack = unpack; cur_unpack; cur_unpack = cur_unpack->next) { if (cur_unpack->object->type->size == 0) { continue; } struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->value = mkgtemp(ctx, cur_unpack->object->type, "unpack.%d"); gb->object = cur_unpack->object; gb->next = ctx->bindings; ctx->bindings = gb; struct qbe_value qoff = constl(cur_unpack->offset); struct qbe_value qitem = mklval(ctx, &gb->value); pushi(ctx->current, &qitem, Q_ADD, &qptr, &qoff, NULL); } } else { struct expression_binding *binding = &expr->_for.bindings->binding; if (binding->object->type->size != 0) { struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->object = binding->object; gb->value = (struct gen_value) { .kind = GV_TEMP, .type = binding->object->type, .name = qptr.name, }; gb->next = ctx->bindings; ctx->bindings = gb; } } break; case FOR_ACCUMULATOR: { struct gen_value cond = gen_expr(ctx, expr->_for.cond); struct qbe_value qcond = mkqval(ctx, &cond); pushi(ctx->current, NULL, Q_JNZ, &qcond, &bbody, &bend, NULL); }} push(&ctx->current->body, &lbody); gen_expr(ctx, expr->_for.body); push(&ctx->current->body, &lafter); if (expr->_for.afterthought) { gen_expr(ctx, expr->_for.afterthought); } if (kind == FOR_EACH_VALUE || kind == FOR_EACH_POINTER) { struct qbe_value qmember_sz = constl( type_dealias(NULL, ginitializer.type)->array.members->size); pushi(ctx->current, &qptr, Q_ADD, &qptr, &qmember_sz, NULL); } pop_scope(ctx); pushi(ctx->current, NULL, Q_JMP, &bloop, NULL); push(&ctx->current->body, &lend); return gv_void; } static struct gen_value gen_expr_free(struct gen_context *ctx, const struct expression *expr) { const struct type *type = type_dealias(NULL, expr->free.expr->result); if (type->storage == STORAGE_NULL) { return gv_void; } struct gen_value val = gen_expr(ctx, expr->free.expr); struct qbe_value qval = mkqval(ctx, &val); if (type->storage == STORAGE_SLICE || type->storage == STORAGE_STRING) { struct qbe_value lval = mklval(ctx, &val); qval = mkqtmp(ctx, ctx->arch.ptr, ".%d"); pushi(ctx->current, &qval, Q_LOADL, &lval, NULL); } pushi(ctx->current, NULL, Q_CALL, &ctx->rt.free, &qval, NULL); return gv_void; } static struct gen_value gen_expr_if_with(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { struct gen_value gvout = gv_void; if (!out) { gvout = mkgtemp(ctx, expr->result, ".%d"); } struct qbe_statement ltrue, lfalse, lend; struct qbe_value btrue = mklabel(ctx, <rue, "true.%d"); struct qbe_value bfalse = mklabel(ctx, &lfalse, "false.%d"); struct qbe_value bend = mklabel(ctx, &lend, ".%d"); struct gen_value cond = gen_expr(ctx, expr->_if.cond); struct qbe_value qcond = mkqval(ctx, &cond); qcond = extend(ctx, qcond, &builtin_type_bool); pushi(ctx->current, NULL, Q_JNZ, &qcond, &btrue, &bfalse, NULL); push(&ctx->current->body, <rue); struct gen_value vtrue = gen_expr_with(ctx, expr->_if.true_branch, out); branch_copyresult(ctx, vtrue, gvout, out); if (expr->_if.true_branch->result->storage != STORAGE_NEVER) { pushi(ctx->current, NULL, Q_JMP, &bend, NULL); } push(&ctx->current->body, &lfalse); if (expr->_if.false_branch) { struct gen_value vfalse = gen_expr_with(ctx, expr->_if.false_branch, out); branch_copyresult(ctx, vfalse, gvout, out); } else if (expr->result->storage == STORAGE_TAGGED) { // if false branch is absent, store void tag struct qbe_value qout = mkqval(ctx, out ? out : &gvout); if (!out) { struct qbe_value sz = constl(builtin_type_u32.size); enum qbe_instr alloc = alloc_for_align(expr->result->align); pushprei(ctx->current, &qout, alloc, &sz, NULL); } struct qbe_value id = constw(builtin_type_void.id); enum qbe_instr store = store_for_type(ctx, &builtin_type_u32); pushi(ctx->current, NULL, store, &id, &qout, NULL); } push(&ctx->current->body, &lend); return gvout; } static struct gen_value gen_expr_append_insert(struct gen_context *ctx, const struct expression *expr) { assert(expr->type == EXPR_APPEND || expr->type == EXPR_INSERT); struct gen_value slice; if (expr->type == EXPR_APPEND) { slice = gen_expr(ctx, expr->append.object); } else { const struct expression *objexpr = expr->append.object; assert(objexpr->type == EXPR_ACCESS && objexpr->access.type == ACCESS_INDEX); slice = gen_expr(ctx, objexpr->access.array); } slice = gen_autoderef(ctx, slice); struct qbe_value prevlen, cap; struct gen_slice sl = gen_slice_ptrs(ctx, slice); load_slice_data(ctx, &sl, NULL, &prevlen, expr->append.is_static ? &cap : NULL); enum qbe_instr load = load_for_type(ctx, &builtin_type_size); struct qbe_value qindex, *qindex_ptr; if (expr->type == EXPR_APPEND) { qindex_ptr = &prevlen; } else { struct gen_value index = gen_expr(ctx, expr->append.object->access.index); qindex = mkqval(ctx, &index); qindex_ptr = &qindex; gen_fixed_assert(ctx, expr->loc, ABORT_OOB, Q_CULEL, &qindex, &prevlen); } struct qbe_value appendlen; const struct type *valtype = type_dealias(NULL, expr->append.value->result); if (expr->append.length != NULL) { struct gen_value length = gen_expr(ctx, expr->append.length); if (expr->append.length->result->storage == STORAGE_NEVER) { return gv_void; } appendlen = mkqval(ctx, &length); assert(valtype->storage == STORAGE_ARRAY && valtype->array.expandable); } else if (!expr->append.is_multi) { appendlen = constl(1); } struct gen_value value; struct qbe_value qvalue; if (!expr->append.is_multi || valtype->storage != STORAGE_ARRAY) { // We use gen_expr_at for the array case to avoid a copy value = gen_expr(ctx, expr->append.value); if (expr->append.value->result->storage == STORAGE_NEVER) { return gv_void; } qvalue = mkqval(ctx, &value); } if (expr->append.is_multi) { if (valtype->storage == STORAGE_ARRAY) { assert(valtype->array.length != SIZE_UNDEFINED); appendlen = constl(valtype->array.length); } else { appendlen = mkqtmp(ctx, ctx->arch.sz, ".%d"); struct qbe_value ptr = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value offs = constl(ctx->arch.ptr->size); pushi(ctx->current, &ptr, Q_ADD, &qvalue, &offs, NULL); pushi(ctx->current, &appendlen, load, &ptr, NULL); } } struct qbe_value newlen = mkqtmp(ctx, ctx->arch.sz, ".%d"); pushi(ctx->current, &newlen, Q_ADD, &prevlen, &appendlen, NULL); store_slice_data(ctx, &sl, NULL, &newlen, NULL); struct qbe_value ptr = mkqtmp(ctx, ctx->arch.ptr, ".%d"); const struct type *mtype = type_dealias(NULL, slice.type)->array.members; struct qbe_value membsz = constl(mtype->size); if (!expr->append.is_static) { struct qbe_value lval = mklval(ctx, &slice); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.ensure, &lval, &membsz, NULL); } else { gen_fixed_assert(ctx, expr->loc, ABORT_OOB, Q_CULEL, &newlen, &cap); } struct qbe_value base = mkqtmp(ctx, ctx->arch.ptr, ".%d"); pushi(ctx->current, &base, load, &sl.base, NULL); pushi(ctx->current, &ptr, Q_MUL, qindex_ptr, &membsz, NULL); pushi(ctx->current, &ptr, Q_ADD, &base, &ptr, NULL); struct qbe_value nbyte = mkqtmp(ctx, ctx->arch.sz, ".%d"); pushi(ctx->current, &nbyte, Q_MUL, &appendlen, &membsz, NULL); if (expr->type == EXPR_INSERT) { struct qbe_value dest = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value ncopy = mkqtmp(ctx, ctx->arch.sz, ".%d"); pushi(ctx->current, &ncopy, Q_SUB, &prevlen, qindex_ptr, NULL); pushi(ctx->current, &ncopy, Q_MUL, &ncopy, &membsz, NULL); pushi(ctx->current, &dest, Q_ADD, &ptr, &nbyte, NULL); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memmove, &dest, &ptr, &ncopy, NULL); } struct gen_value item = { .kind = GV_TEMP, .type = mtype, .name = ptr.name, }; if (expr->append.is_multi && valtype->storage == STORAGE_ARRAY) { item.type = valtype; gen_expr_at(ctx, expr->append.value, item); } else if (expr->append.is_multi && valtype->storage == STORAGE_SLICE) { struct qbe_value qsrc = mkqtmp(ctx, ctx->arch.ptr, ".%d"); pushi(ctx->current, &qsrc, load, &qvalue, NULL); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memmove, &ptr, &qsrc, &nbyte, NULL); } else if (expr->append.length != NULL) { // XXX: This could be made more efficient for some cases if // check could determine the length at compile time and lower it // to a fixed-length array type assert(valtype->storage == STORAGE_ARRAY); item.type = valtype; gen_expr_at(ctx, expr->append.value, item); assert(valtype->array.length != SIZE_UNDEFINED); struct qbe_value next = mkqtmp(ctx, ctx->arch.ptr, "next.%d"); struct qbe_value last = mkqtmp(ctx, ctx->arch.ptr, "last.%d"); struct qbe_value arlen = constl(valtype->array.length * mtype->size); pushi(ctx->current, &next, Q_ADD, &ptr, &arlen, NULL); arlen = constl((valtype->array.length - 1) * mtype->size); pushi(ctx->current, &last, Q_ADD, &ptr, &arlen, NULL); struct qbe_value remain = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct qbe_value one = constl(1); pushi(ctx->current, &remain, Q_SUB, &appendlen, &one, NULL); pushi(ctx->current, &remain, Q_MUL, &remain, &membsz, NULL); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memcpy, &next, &last, &remain, NULL); } else { gen_store(ctx, item, value); } return gv_void; } enum match_compat { // The case type is a member of the match object type and can be used // directly from the match object's tagged union storage area. COMPAT_SUBTYPE, // The case type is a tagged union which is a subset of the object type. COMPAT_SUBSET, }; static struct qbe_value nested_tagged_offset(const struct type *tu, const struct type *target) { // This function calculates the offset of a member in a nested tagged union // // type foo = (int | void); // type bar = (size | foo); // // The offset of the "foo" field from the start of "bar" is 4, and the // offset of int inside "foo" is 4, so the offset of int from the start // of "bar" is 8. The size is at offset 8. const struct type *tu_memb; uint64_t offset = 0; do { offset += builtin_type_u32.align; tu_memb = tagged_select_subtype(NULL, tu, target, false); if (!tu_memb) { break; } if (tu_memb->align != 0 && offset % tu_memb->align != 0) { offset += tu_memb->align - offset % tu_memb->align; } tu = tu_memb; } while (tu_memb->id != target->id && type_dealias(NULL, tu_memb)->id != target->id); return constl(offset); } static struct gen_value gen_nested_match_tests(struct gen_context *ctx, struct gen_value object, struct qbe_value bmatch, struct qbe_value bnext, struct qbe_value tag, const struct type *type) { // This function handles the case where we're matching against a type // which is a member of the tagged union, or an inner tagged union. // // type foo = (int | void); // type bar = (size | foo); // // let x: bar = 10i; // match (x) { // case let z: size => ... // case let i: int => ... // case void => ... // }; // // In the first case, we can simply test the object's tag. In the second // case, we have to test if the selected tag is 'foo', then check the // tag of the foo object for int. struct qbe_value *subtag = &tag; struct qbe_value subval = mkcopy(ctx, &object, "subval.%d"); struct gen_value match = mkgtemp(ctx, &builtin_type_bool, ".%d"); struct qbe_value qmatch = mkqval(ctx, &match); struct qbe_value temp = mkqtmp(ctx, &qbe_word, ".%d"); const struct type *subtype = object.type; const struct type *test = type; do { struct qbe_statement lsubtype; struct qbe_value bsubtype = mklabel(ctx, &lsubtype, "subtype.%d"); if (type_dealias(NULL, subtype)->storage != STORAGE_TAGGED) { break; } test = tagged_select_subtype(NULL, subtype, type, false); if (!test) { break; } struct qbe_value id = constw(test->id); pushi(ctx->current, &qmatch, Q_CEQW, subtag, &id, NULL); pushi(ctx->current, NULL, Q_JNZ, &qmatch, &bsubtype, &bnext, NULL); push(&ctx->current->body, &lsubtype); // In the case of a tagged union which is a subset of the // object, where we're testing for a type within that subset, // move the pointer to this tagged union and continue looking // for the relevant type ID there. if (test->id != type->id && type_dealias(NULL, test)->id != type->id && type_dealias(NULL, test)->storage == STORAGE_TAGGED) { struct qbe_value offs = compute_tagged_memb_offset(test); pushi(ctx->current, &subval, Q_ADD, &subval, &offs, NULL); gen_load_tag(ctx, &temp, &subval, test); subtag = &temp; } subtype = test; } while (test->id != type->id && type_dealias(NULL, test)->id != type->id); pushi(ctx->current, NULL, Q_JMP, &bmatch, NULL); return match; } static struct gen_value gen_subset_match_tests(struct gen_context *ctx, struct qbe_value bmatch, struct qbe_value bnext, struct qbe_value tag, const struct type *type) { // In this case, we're testing a case which is itself a tagged union, // and is a subset of the match object. // // type foo = (size | int | void); // // let x: foo = 10i; // match (x) { // case let n: (size | int) => ... // case void => ... // }; // // In this situation, we test the match object's tag against each type // ID of the case type. struct gen_value match = mkgtemp(ctx, &builtin_type_bool, ".%d"); for (const struct type_tagged_union *tu = &type->tagged; tu; tu = tu->next) { struct qbe_statement lnexttag; struct qbe_value bnexttag = mklabel(ctx, &lnexttag, ".%d"); struct qbe_value id = constl(tu->type->id); struct qbe_value qmatch = mkqval(ctx, &match); pushi(ctx->current, &qmatch, Q_CEQW, &tag, &id, NULL); pushi(ctx->current, NULL, Q_JNZ, &qmatch, &bmatch, &bnexttag, NULL); push(&ctx->current->body, &lnexttag); } pushi(ctx->current, NULL, Q_JMP, &bnext, NULL); return match; } static struct gen_value gen_match_with_tagged(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { struct gen_value gvout = gv_void; if (!out) { gvout = mkgtemp(ctx, expr->result, ".%d"); } const struct type *objtype = expr->match.value->result; struct gen_value object = gen_expr(ctx, expr->match.value); struct qbe_value qobject = mkqval(ctx, &object); struct qbe_value tag = mkqtmp(ctx, ctx->arch.sz, "tag.%d"); gen_load_tag(ctx, &tag, &qobject, objtype); struct qbe_statement lout; struct qbe_value bout = mklabel(ctx, &lout, ".%d"); struct gen_value bval; const struct match_case *_default = NULL; for (const struct match_case *_case = expr->match.cases; _case; _case = _case->next) { if (!_case->type) { _default = _case; continue; } struct qbe_statement lmatch, lnext; struct qbe_value bmatch = mklabel(ctx, &lmatch, "matches.%d"); struct qbe_value bnext = mklabel(ctx, &lnext, "next.%d"); const struct type *subtype = tagged_select_subtype(NULL, objtype, _case->type, false); enum match_compat compat = COMPAT_SUBTYPE; if (subtype) { gen_nested_match_tests(ctx, object, bmatch, bnext, tag, _case->type); } else { assert(type_dealias(NULL, _case->type)->storage == STORAGE_TAGGED); assert(tagged_subset_compat(NULL, objtype, _case->type)); compat = COMPAT_SUBSET; const struct type *casetype = type_dealias(NULL, _case->type); gen_subset_match_tests(ctx, bmatch, bnext, tag, casetype); } push(&ctx->current->body, &lmatch); if (!_case->object || _case->type->size == 0) { goto next; } struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->value = mkgtemp(ctx, _case->type, "binding.%d"); gb->object = _case->object; gb->next = ctx->bindings; ctx->bindings = gb; struct qbe_value qv = mklval(ctx, &gb->value); enum qbe_instr alloc = alloc_for_align(_case->type->align); struct qbe_value sz = constl(_case->type->size); pushprei(ctx->current, &qv, alloc, &sz, NULL); struct qbe_value ptr = mkqtmp(ctx, ctx->arch.ptr, ".%d"); struct gen_value src = { .kind = GV_TEMP, .type = _case->type, .name = ptr.name, }; struct gen_value load; struct qbe_value offset; switch (compat) { case COMPAT_SUBTYPE: offset = nested_tagged_offset(object.type, _case->type); pushi(ctx->current, &ptr, Q_ADD, &qobject, &offset, NULL); load = gen_load(ctx, src); gen_store(ctx, gb->value, load); break; case COMPAT_SUBSET: pushi(ctx->current, &ptr, Q_COPY, &qobject, NULL); load = gen_load(ctx, src); gen_store(ctx, gb->value, load); break; } next: bval = gen_expr_with(ctx, _case->value, out); branch_copyresult(ctx, bval, gvout, out); if (_case->value->result->storage != STORAGE_NEVER) { pushi(ctx->current, NULL, Q_JMP, &bout, NULL); } push(&ctx->current->body, &lnext); } if (_default) { bval = gen_expr_with(ctx, _default->value, out); branch_copyresult(ctx, bval, gvout, out); } else { struct qbe_statement labort; mklabel(ctx, &labort, ".%d"); push(&ctx->current->body, &labort); gen_fixed_abort(ctx, expr->loc, ABORT_UNREACHABLE); } push(&ctx->current->body, &lout); return gvout; } static struct gen_value gen_match_with_nullable(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { struct gen_value gvout = gv_void; if (!out) { gvout = mkgtemp(ctx, expr->result, ".%d"); } struct qbe_statement lout; struct qbe_value bout = mklabel(ctx, &lout, ".%d"); struct gen_value object = gen_expr(ctx, expr->match.value); struct qbe_value qobject = mkqval(ctx, &object); struct qbe_value zero = constl(0); struct gen_value bval; const struct match_case *_default = NULL; for (const struct match_case *_case = expr->match.cases; _case; _case = _case->next) { if (!_case->type) { _default = _case; continue; } struct qbe_statement lmatch, lnext; struct qbe_value cmpres = mkqtmp(ctx, &qbe_word, ".%d"); struct qbe_value bmatch = mklabel(ctx, &lmatch, "matches.%d"); struct qbe_value bnext = mklabel(ctx, &lnext, "next.%d"); enum qbe_instr compare; if (_case->type->storage == STORAGE_NULL) { compare = Q_CEQL; } else { compare = Q_CNEL; } pushi(ctx->current, &cmpres, compare, &qobject, &zero, NULL); pushi(ctx->current, NULL, Q_JNZ, &cmpres, &bmatch, &bnext, NULL); push(&ctx->current->body, &lmatch); if (!_case->object) { goto next; } struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->value = mkgtemp(ctx, _case->type, "binding.%d"); gb->object = _case->object; gb->next = ctx->bindings; ctx->bindings = gb; enum qbe_instr store = store_for_type(ctx, _case->type); enum qbe_instr alloc = alloc_for_align(_case->type->align); struct qbe_value qv = mkqval(ctx, &gb->value); struct qbe_value sz = constl(_case->type->size); pushprei(ctx->current, &qv, alloc, &sz, NULL); pushi(ctx->current, NULL, store, &qobject, &qv, NULL); next: bval = gen_expr_with(ctx, _case->value, out); branch_copyresult(ctx, bval, gvout, out); if (_case->value->result->storage != STORAGE_NEVER) { pushi(ctx->current, NULL, Q_JMP, &bout, NULL); } push(&ctx->current->body, &lnext); } if (_default) { bval = gen_expr_with(ctx, _default->value, out); branch_copyresult(ctx, bval, gvout, out); if (_default->value->result->storage != STORAGE_NEVER) { pushi(ctx->current, NULL, Q_JMP, &bout, NULL); } } struct qbe_statement labort; mklabel(ctx, &labort, ".%d"); push(&ctx->current->body, &labort); gen_fixed_abort(ctx, expr->loc, ABORT_UNREACHABLE); push(&ctx->current->body, &lout); return gvout; } static struct gen_value gen_expr_match_with(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { const struct type *objtype = expr->match.value->result; switch (type_dealias(NULL, objtype)->storage) { case STORAGE_POINTER: return gen_match_with_nullable(ctx, expr, out); case STORAGE_TAGGED: return gen_match_with_tagged(ctx, expr, out); default: abort(); // Invariant } } static struct gen_value gen_expr_len(struct gen_context *ctx, const struct expression *expr) { const struct expression *value = expr->len.value; const struct type *type = type_dereference(NULL, value->result); assert(type != NULL); type = type_dealias(NULL, type); assert(type->storage == STORAGE_SLICE || type->storage == STORAGE_STRING); struct gen_value gv = gen_autoderef(ctx, gen_expr(ctx, value)); struct qbe_value len; struct gen_slice sl = gen_slice_ptrs(ctx, gv); load_slice_data(ctx, &sl, NULL, &len, NULL); return (struct gen_value){ .kind = GV_TEMP, .type = &builtin_type_size, .name = len.name }; } static struct gen_value gen_expr_return(struct gen_context *ctx, const struct expression *expr) { struct gen_value ret = gen_expr(ctx, expr->_return.value); if (expr->_return.value->result->storage == STORAGE_NEVER) { return gv_void; } for (struct gen_scope *scope = ctx->scope; scope; scope = scope->parent) { gen_defers(ctx, scope); } if (ret.type->size == 0) { pushi(ctx->current, NULL, Q_RET, NULL); } else { struct qbe_value qret = mkqval(ctx, &ret); pushi(ctx->current, NULL, Q_RET, &qret, NULL); } return gv_void; } static void gen_expr_struct_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { // TODO: Merge me into literal expressions struct qbe_value base = mkqval(ctx, &out); if (expr->_struct.autofill) { struct qbe_value size = constl(expr->result->size), zero = constl(0); struct qbe_value base = mklval(ctx, &out); pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memset, &base, &zero, &size, NULL); } struct gen_value ftemp = mkgtemp(ctx, &builtin_type_void, "field.%d"); for (const struct expr_struct_field *field = expr->_struct.fields; field; field = field->next) { if (!field->value) { assert(expr->_struct.autofill); continue; } struct qbe_value offs = constl(field->field->offset); ftemp.type = field->value->result; struct qbe_value ptr = mklval(ctx, &ftemp); pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); gen_expr_at(ctx, field->value, ftemp); } } static struct gen_value gen_expr_struct(struct gen_context *ctx, const struct expression *expr) { if (expr->result->size != 0) { struct gen_value out = mkgtemp(ctx, expr->result, "object.%d"); struct qbe_value base = mklval(ctx, &out); struct qbe_value sz = constl(expr->result->size); enum qbe_instr alloc = alloc_for_align(expr->result->align); pushprei(ctx->current, &base, alloc, &sz, NULL); gen_expr_struct_at(ctx, expr, out); return out; } for (const struct expr_struct_field *field = expr->_struct.fields; field; field = field->next) { gen_expr(ctx, field->value); } return gv_void; } static struct gen_value gen_expr_switch_with(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { struct gen_value gvout = gv_void; if (!out) { gvout = mkgtemp(ctx, expr->result, ".%d"); } struct qbe_statement lout; struct qbe_value bout = mklabel(ctx, &lout, ".%d"); struct gen_value value = gen_expr(ctx, expr->_switch.value); struct gen_value bval; const struct switch_case *_default = NULL; for (const struct switch_case *_case = expr->_switch.cases; _case; _case = _case->next) { if (!_case->options) { _default = _case; continue; } struct qbe_statement lmatch, lnextcase; struct qbe_value bmatch = mklabel(ctx, &lmatch, "matches.%d"); struct qbe_value bnextcase = mklabel(ctx, &lnextcase, "next.%d"); for (struct case_option *opt = _case->options; opt; opt = opt->next) { struct qbe_statement lnextopt; struct qbe_value bnextopt = mklabel(ctx, &lnextopt, ".%d"); struct gen_value test = gen_expr_literal(ctx, opt->value); struct expression lvalue = { .type = EXPR_GEN_VALUE, .result = value.type, .user = &value, }, rvalue = { .type = EXPR_GEN_VALUE, .result = test.type, .user = &test, }, compare = { .type = EXPR_BINARITHM, .result = &builtin_type_bool, .binarithm = { .op = BIN_LEQUAL, .lvalue = &lvalue, .rvalue = &rvalue, }, }; struct gen_value match = gen_expr(ctx, &compare); struct qbe_value cond = mkqval(ctx, &match); pushi(ctx->current, NULL, Q_JNZ, &cond, &bmatch, &bnextopt, NULL); push(&ctx->current->body, &lnextopt); } pushi(ctx->current, NULL, Q_JMP, &bnextcase, NULL); push(&ctx->current->body, &lmatch); bval = gen_expr_with(ctx, _case->value, out); branch_copyresult(ctx, bval, gvout, out); if (_case->value->result->storage != STORAGE_NEVER) { pushi(ctx->current, NULL, Q_JMP, &bout, NULL); } push(&ctx->current->body, &lnextcase); } if (_default) { bval = gen_expr_with(ctx, _default->value, out); branch_copyresult(ctx, bval, gvout, out); if (_default->value->result->storage != STORAGE_NEVER) { pushi(ctx->current, NULL, Q_JMP, &bout, NULL); } } struct qbe_statement labort; mklabel(ctx, &labort, ".%d"); push(&ctx->current->body, &labort); gen_fixed_abort(ctx, expr->loc, ABORT_UNREACHABLE); push(&ctx->current->body, &lout); return gvout; } static void gen_expr_slice_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { struct gen_value object = gen_expr(ctx, expr->slice.object); object = gen_autoderef(ctx, object); const struct type *srctype = type_dealias(NULL, object.type); struct qbe_value qbase, qstart, qnewlen, qnewcap; struct qbe_value qlen_, qcap_, *qlen = &qlen_, *qcap = &qcap_; if (srctype->storage == STORAGE_ARRAY) { qbase = mkcopy(ctx, &object, ".%d"); if (srctype->array.length == SIZE_UNDEFINED) { qcap = qlen = NULL; } else { *qcap = *qlen = constl(srctype->array.length); } } else { struct gen_slice sl = gen_slice_ptrs(ctx, object); load_slice_data(ctx, &sl, &qbase, qlen, qcap); } gen_subslice_info(ctx, expr, qlen, qcap, &qstart, NULL, &qnewlen, &qnewcap); struct qbe_value data = mkqtmp(ctx, ctx->arch.ptr, "data.%d"); struct qbe_value isz = constl(srctype->array.members->size); pushi(ctx->current, &data, Q_MUL, &qstart, &isz, NULL); pushi(ctx->current, &data, Q_ADD, &qbase, &data, NULL); struct gen_slice sl = gen_slice_ptrs(ctx, out); store_slice_data(ctx, &sl, &data, &qnewlen, &qnewcap); } static void gen_expr_tuple_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { // TODO: Merge me into literal expressions struct qbe_value base = mkqval(ctx, &out); const struct type *type = type_dealias(NULL, expr->result); struct gen_value vtemp = mkgtemp(ctx, &builtin_type_void, "value.%d"); const struct expression_tuple *value = &expr->tuple; for (const struct type_tuple *tuple = &type->tuple; tuple; tuple = tuple->next) { struct qbe_value offs = constl(tuple->offset); vtemp.type = value->value->result; struct qbe_value ptr = mklval(ctx, &vtemp); pushi(ctx->current, &ptr, Q_ADD, &base, &offs, NULL); gen_expr_at(ctx, value->value, vtemp); value = value->next; } } static struct gen_value gen_expr_tuple(struct gen_context *ctx, const struct expression *expr) { if (expr->result->size != 0) { struct gen_value out = mkgtemp(ctx, expr->result, "object.%d"); struct qbe_value base = mklval(ctx, &out); struct qbe_value sz = constl(expr->result->size); enum qbe_instr alloc = alloc_for_align(expr->result->align); pushprei(ctx->current, &base, alloc, &sz, NULL); gen_expr_tuple_at(ctx, expr, out); return out; } for (const struct expression_tuple *value = &expr->tuple; value; value = value->next) { gen_expr(ctx, value->value); } return gv_void; } static struct gen_value gen_expr_unarithm(struct gen_context *ctx, const struct expression *expr) { struct gen_value val, temp; struct qbe_value qval, qtmp; const struct expression *operand = expr->unarithm.operand; switch (expr->unarithm.op) { case UN_ADDRESS: if (operand->type == EXPR_ACCESS) { val = gen_expr_access_addr(ctx, operand); val.type = expr->result; return val; } struct gen_value val = mkgtemp(ctx, operand->result, ".%d"); struct qbe_value qv = mklval(ctx, &val); struct qbe_value sz = constl(val.type->size); enum qbe_instr alloc = alloc_for_align(val.type->align); pushprei(ctx->current, &qv, alloc, &sz, NULL); gen_expr_at(ctx, operand, val); val.type = expr->result; return val; case UN_DEREF: val = gen_expr(ctx, operand); assert(type_dealias(NULL, val.type)->storage == STORAGE_POINTER); val.type = type_dealias(NULL, val.type)->pointer.referent; return gen_load(ctx, val); case UN_BNOT: val = gen_expr(ctx, operand); temp = mkgtemp(ctx, operand->result, ".%d"); qval = mkqval(ctx, &val), qtmp = mkqval(ctx, &temp); struct qbe_value ones = constl((uint64_t)-1); pushi(ctx->current, &qtmp, Q_XOR, &qval, &ones, NULL); return temp; case UN_LNOT: val = gen_expr(ctx, operand); temp = mkgtemp(ctx, operand->result, ".%d"); qval = mkqval(ctx, &val), qtmp = mkqval(ctx, &temp); qval = extend(ctx, qval, operand->result); struct qbe_value zerow = constw(0); pushi(ctx->current, &qtmp, Q_CEQW, &qval, &zerow, NULL); return temp; case UN_MINUS: val = gen_expr(ctx, operand); temp = mkgtemp(ctx, operand->result, ".%d"); qval = mkqval(ctx, &val), qtmp = mkqval(ctx, &temp); pushi(ctx->current, &qtmp, Q_NEG, &qval, NULL); return temp; } abort(); // Invariant } static struct gen_value gen_expr_vaarg(struct gen_context *ctx, const struct expression *expr) { // XXX: qbe only supports variadic base types, should check for this struct gen_value result = mkgtemp(ctx, expr->result, ".%d"); struct qbe_value qresult = mkqval(ctx, &result); struct gen_value ap = gen_expr(ctx, expr->vaarg.ap); struct qbe_value qap = mkqval(ctx, &ap); pushi(ctx->current, &qresult, Q_VAARG, &qap, NULL); return result; } static void gen_expr_vastart_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { struct qbe_value base = mklval(ctx, &out); pushi(ctx->current, NULL, Q_VASTART, &base, NULL); } static struct gen_value gen_expr(struct gen_context *ctx, const struct expression *expr) { if (expr->loc.file && expr->loc.lineno) { struct qbe_value qline = constl(expr->loc.lineno); struct qbe_value qcol = constl(expr->loc.colno); pushi(ctx->current, NULL, Q_DBGLOC, &qline, &qcol, NULL); } struct gen_value out; switch ((int)expr->type) { case EXPR_ACCESS: out = gen_expr_access(ctx, expr); break; case EXPR_ALLOC: out = gen_expr_alloc_with(ctx, expr, NULL); break; case EXPR_APPEND: case EXPR_INSERT: out = gen_expr_append_insert(ctx, expr); break; case EXPR_ASSERT: out = gen_expr_assert(ctx, expr); break; case EXPR_ASSIGN: out = gen_expr_assign(ctx, expr); break; case EXPR_BINARITHM: out = gen_expr_binarithm(ctx, expr); break; case EXPR_BINDING: out = gen_expr_binding(ctx, expr); break; case EXPR_BREAK: case EXPR_CONTINUE: case EXPR_YIELD: out = gen_expr_control(ctx, expr); break; case EXPR_CALL: out = gen_expr_call(ctx, expr); break; case EXPR_CAST: out = gen_expr_cast(ctx, expr); break; case EXPR_COMPOUND: out = gen_expr_compound_with(ctx, expr, NULL); break; case EXPR_LITERAL: out = gen_expr_literal(ctx, expr); break; case EXPR_DEFER: out = gen_expr_defer(ctx, expr); break; case EXPR_DELETE: out = gen_expr_delete(ctx, expr); break; case EXPR_FOR: out = gen_expr_for(ctx, expr); break; case EXPR_FREE: out = gen_expr_free(ctx, expr); break; case EXPR_IF: out = gen_expr_if_with(ctx, expr, NULL); break; case EXPR_LEN: out = gen_expr_len(ctx, expr); break; case EXPR_MATCH: out = gen_expr_match_with(ctx, expr, NULL); break; case EXPR_PROPAGATE: assert(0); // Lowered in check (for now?) case EXPR_RETURN: out = gen_expr_return(ctx, expr); break; case EXPR_SWITCH: out = gen_expr_switch_with(ctx, expr, NULL); break; case EXPR_UNARITHM: out = gen_expr_unarithm(ctx, expr); break; case EXPR_VAARG: out = gen_expr_vaarg(ctx, expr); break; break; case EXPR_STRUCT: out = gen_expr_struct(ctx, expr); break; case EXPR_TUPLE: out = gen_expr_tuple(ctx, expr); break; case EXPR_SLICE: case EXPR_VASTART: // Prefers -at style out = mkgtemp(ctx, expr->result, "object.%d"); struct qbe_value base = mkqval(ctx, &out); struct qbe_value sz = constl(expr->result->size); enum qbe_instr alloc = alloc_for_align(expr->result->align); pushprei(ctx->current, &base, alloc, &sz, NULL); gen_expr_at(ctx, expr, out); return out; case EXPR_DEFINE: case EXPR_VAEND: out = gv_void; // no-op break; // gen-specific psuedo-expressions case EXPR_GEN_VALUE: return *(struct gen_value *)expr->user; } if (expr->result->storage == STORAGE_NEVER) { // XXX: This is a bit hacky, to appease qbe struct qbe_statement dummyl; mklabel(ctx, &dummyl, ".%d"); push(&ctx->current->body, &dummyl); out.type = &builtin_type_never; } return out; } static void gen_expr_at(struct gen_context *ctx, const struct expression *expr, struct gen_value out) { assert(out.kind != GV_CONST); switch (expr->type) { case EXPR_ALLOC: gen_expr_alloc_with(ctx, expr, &out); return; case EXPR_CAST: gen_expr_cast_at(ctx, expr, out); return; case EXPR_COMPOUND: gen_expr_compound_with(ctx, expr, &out); return; case EXPR_LITERAL: gen_expr_literal_at(ctx, expr, out); return; case EXPR_IF: gen_expr_if_with(ctx, expr, &out); return; case EXPR_MATCH: gen_expr_match_with(ctx, expr, &out); return; case EXPR_SLICE: gen_expr_slice_at(ctx, expr, out); return; case EXPR_STRUCT: gen_expr_struct_at(ctx, expr, out); return; case EXPR_SWITCH: gen_expr_switch_with(ctx, expr, &out); return; case EXPR_TUPLE: gen_expr_tuple_at(ctx, expr, out); return; case EXPR_VASTART: gen_expr_vastart_at(ctx, expr, out); return; default: break; // Prefers non-at style } struct gen_value result = gen_expr(ctx, expr); if (expr->result->storage != STORAGE_NEVER) { gen_store(ctx, out, result); } } static struct gen_value gen_expr_with(struct gen_context *ctx, const struct expression *expr, struct gen_value *out) { if (out) { gen_expr_at(ctx, expr, *out); return *out; } return gen_expr(ctx, expr); } static void gen_function_decl(struct gen_context *ctx, const struct declaration *decl) { const struct function_decl *func = &decl->func; const struct type *fntype = func->type; if (func->body == NULL) { return; // Prototype } struct qbe_def *qdef = xcalloc(1, sizeof(struct qbe_def)); qdef->kind = Q_FUNC; qdef->exported = decl->exported; ctx->current = &qdef->func; qdef->name = decl->symbol ? xstrdup(decl->symbol) : ident_to_sym(&decl->ident); qdef->file = decl->file; struct qbe_statement start_label = {0}; mklabel(ctx, &start_label, "start.%d"); push(&qdef->func.prelude, &start_label); if (fntype->func.result->size != 0 && fntype->func.result->size != SIZE_UNDEFINED) { qdef->func.returns = qtype_lookup( ctx, fntype->func.result, false); } else { qdef->func.returns = &qbe_void; } if (fntype->func.variadism == VARIADISM_C) { qdef->func.variadic = true; } struct qbe_func_param *param, **next = &qdef->func.params; for (struct scope_object *obj = decl->func.scope->objects; obj; obj = obj->lnext) { const struct type *type = obj->type; if (type->size == 0) { continue; } param = *next = xcalloc(1, sizeof(struct qbe_func_param)); assert(!obj->ident.ns); // Invariant param->name = xstrdup(obj->ident.name); param->type = qtype_lookup(ctx, type, false); struct gen_binding *gb = xcalloc(1, sizeof(struct gen_binding)); gb->value.kind = GV_TEMP; gb->value.type = type; gb->object = obj; if (type_is_aggregate(type)) { // No need to copy to stack gb->value.name = xstrdup(param->name); } else { gb->value.name = gen_name(&ctx->id, "param.%d"); struct qbe_value qv = mklval(ctx, &gb->value); struct qbe_value sz = constl(type->size); enum qbe_instr alloc = alloc_for_align(type->align); pushprei(ctx->current, &qv, alloc, &sz, NULL); struct gen_value src = { .kind = GV_TEMP, .type = type, .name = param->name, }; gen_store(ctx, gb->value, src); } gb->next = ctx->bindings; ctx->bindings = gb; next = ¶m->next; } struct qbe_statement lbody; mklabel(ctx, &lbody, "body.%d"); push(&ctx->current->body, &lbody); struct gen_value ret = gen_expr(ctx, decl->func.body); if (fntype->func.result->storage == STORAGE_NEVER) { pushi(ctx->current, NULL, Q_HLT, NULL); } else if (decl->func.body->result->storage == STORAGE_NEVER) { // XXX: This is a bit hacky, to appease qbe size_t ln = ctx->current->body.ln; struct qbe_statement *last = &ctx->current->body.stmts[ln - 1]; if (last->type != Q_INSTR || last->instr != Q_RET) { pushi(ctx->current, NULL, Q_RET, NULL); } } else if (fntype->func.result->size != 0) { struct qbe_value qret = mkqval(ctx, &ret); pushi(ctx->current, NULL, Q_RET, &qret, NULL); } else { pushi(ctx->current, NULL, Q_RET, NULL); } qbe_append_def(ctx->out, qdef); if (func->flags & FN_INIT) { struct qbe_def *init = xcalloc(1, sizeof *init); init->kind = Q_DATA; init->exported = false; init->data.align = 8; init->data.section = ".init_array"; init->data.secflags = NULL; size_t n = snprintf(NULL, 0, ".init.%s", qdef->name); init->name = xcalloc(n + 1, 1); snprintf(init->name, n + 1, ".init.%s", qdef->name); struct qbe_data_item dataitem = { .type = QD_VALUE, .value = { .kind = QV_GLOBAL, .type = &qbe_long, .name = xstrdup(qdef->name), }, .next = NULL, }; init->data.items = dataitem; qbe_append_def(ctx->out, init); } if (func->flags & FN_FINI) { struct qbe_def *fini = xcalloc(1, sizeof *fini); fini->kind = Q_DATA; fini->exported = false; fini->data.align = 8; fini->data.section = ".fini_array"; fini->data.secflags = NULL; size_t n = snprintf(NULL, 0, ".fini.%s", qdef->name); fini->name = xcalloc(n + 1, 1); snprintf(fini->name, n + 1, ".fini.%s", qdef->name); struct qbe_data_item dataitem = { .type = QD_VALUE, .value = { .kind = QV_GLOBAL, .type = &qbe_long, .name = xstrdup(qdef->name), }, .next = NULL, }; fini->data.items = dataitem; qbe_append_def(ctx->out, fini); } if (func->flags & FN_TEST) { struct qbe_def *test = xcalloc(1, sizeof *test); test->kind = Q_DATA; test->exported = false; test->data.align = 8; test->data.section = ".test_array"; test->data.secflags = "aw"; size_t n = snprintf(NULL, 0, ".test.%s", qdef->name); test->name = xcalloc(n + 1, 1); snprintf(test->name, n + 1, ".test.%s", qdef->name); char *ident = identifier_unparse(&decl->ident); struct qbe_data_item *dataitem = &test->data.items; struct expression expr; mkstrliteral(&expr, "%s", ident); free(ident); dataitem = gen_data_item(ctx, &expr, dataitem); struct qbe_data_item *next = xcalloc(1, sizeof *next); next->type = QD_VALUE; next->value.kind = QV_GLOBAL; next->value.type = &qbe_long; next->value.name = xstrdup(qdef->name); next->next = NULL; dataitem->next = next; qbe_append_def(ctx->out, test); } ctx->current = NULL; } static struct qbe_data_item * gen_data_item(struct gen_context *ctx, const struct expression *expr, struct qbe_data_item *item) { assert(expr->type == EXPR_LITERAL); struct qbe_def *def; const struct expression_literal *literal = &expr->literal; const struct type *type = type_dealias(NULL, expr->result); if (type->storage == STORAGE_ENUM) { type = type->alias.type; } type = lower_flexible(NULL, type, NULL); if (literal->object) { item->type = QD_SYMOFFS; item->sym = ident_to_sym(&literal->object->ident); if (type->storage == STORAGE_SLICE) { item->offset = literal->slice.offset + literal->slice.start * type->array.members->size; item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_VALUE; item->value = constl(literal->slice.len); item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_VALUE; item->value = constl(literal->slice.cap); } else { item->offset = literal->ival; } return item; } switch (type->storage) { case STORAGE_I8: case STORAGE_U8: item->type = QD_VALUE; item->value = constw((uint8_t)literal->uval); item->value.type = &qbe_byte; break; case STORAGE_BOOL: item->type = QD_VALUE; item->value = constw(literal->bval ? 1 : 0); item->value.type = &qbe_byte; break; case STORAGE_I16: case STORAGE_U16: item->type = QD_VALUE; item->value = constw((uint16_t)literal->uval); item->value.type = &qbe_half; break; case STORAGE_I32: case STORAGE_U32: case STORAGE_INT: case STORAGE_UINT: case STORAGE_RUNE: item->type = QD_VALUE; item->value = constw((uint32_t)literal->uval); break; case STORAGE_U64: case STORAGE_I64: case STORAGE_SIZE: item->type = QD_VALUE; item->value = constl((uint64_t)literal->uval); break; case STORAGE_F32: item->type = QD_VALUE; item->value = consts((float)literal->fval); break; case STORAGE_F64: item->type = QD_VALUE; item->value = constd((double)literal->fval); break; case STORAGE_UINTPTR: case STORAGE_POINTER: item->type = QD_VALUE; switch (ctx->arch.ptr->stype) { case Q_LONG: item->value = constl((uint64_t)literal->uval); break; default: assert(0); } break; case STORAGE_ARRAY: assert(type->array.length != SIZE_UNDEFINED); size_t n = type->array.length; for (struct array_literal *c = literal->array; c && n; c = c->next ? c->next : c, --n) { item = gen_data_item(ctx, c->value, item); if (n > 1 || c->next) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; } } break; case STORAGE_STRING: def = xcalloc(1, sizeof(struct qbe_def)); def->name = gen_name(&ctx->id, "strdata.%d"); def->kind = Q_DATA; def->data.align = ALIGN_UNDEFINED; def->data.items.type = QD_STRING; def->data.items.str = xcalloc(expr->literal.string.len, 1); def->data.items.sz = expr->literal.string.len; memcpy(def->data.items.str, expr->literal.string.value, expr->literal.string.len); item->type = QD_VALUE; if (expr->literal.string.len != 0) { qbe_append_def(ctx->out, def); item->value.kind = QV_GLOBAL; item->value.type = &qbe_long; item->value.name = xstrdup(def->name); } else { free(def); item->value = constl(0); } item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_VALUE; item->value = constl(expr->literal.string.len); item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_VALUE; item->value = constl(expr->literal.string.len); break; case STORAGE_SLICE: def = xcalloc(1, sizeof(struct qbe_def)); def->name = gen_name(&ctx->id, "sldata.%d"); def->kind = Q_DATA; def->data.align = ALIGN_UNDEFINED; if (literal->slice.len != 0) { struct qbe_data_item *subitem = &def->data.items; for (struct array_literal *c = literal->slice.array; c; c = c->next) { subitem = gen_data_item(ctx, c->value, subitem); if (c->next) { subitem->next = xcalloc(1, sizeof(struct qbe_data_item)); subitem = subitem->next; } } qbe_append_def(ctx->out, def); item->type = QD_SYMOFFS; item->sym = xstrdup(def->name); item->offset = literal->slice.start * type->array.members->size; } else { item->type = QD_VALUE; item->value = constl(0); } item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_VALUE; item->value = constl(literal->slice.len); item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_VALUE; item->value = constl(literal->slice.cap); break; case STORAGE_STRUCT: for (struct struct_literal *f = literal->_struct; f; f = f->next) { if (f->field->type->size != 0) { item = gen_data_item(ctx, f->value, item); } if (f->next) { const struct struct_field *f1 = f->field; const struct struct_field *f2 = f->next->field; if (f2->offset > f1->offset + f1->type->size) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_ZEROED; item->zeroed = f2->offset - (f1->offset + f1->type->size); } if (f->field->type->size != 0) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; } } else { const struct struct_field *fi = f->field; if (fi->offset + fi->type->size != expr->result->size) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_ZEROED; item->zeroed = expr->result->size - (fi->offset + fi->type->size); } } } break; case STORAGE_TUPLE: for (const struct tuple_literal *tuple = literal->tuple; tuple; tuple = tuple->next) { if (tuple->field->type->size != 0) { item = gen_data_item(ctx, tuple->value, item); } if (tuple->next) { const struct type_tuple *f1 = tuple->field; const struct type_tuple *f2 = tuple->next->field; if (f2->offset > f1->offset + f1->type->size) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_ZEROED; item->zeroed = f2->offset - (f1->offset + f1->type->size); } if (tuple->field->type->size != 0) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; } } else { const struct type_tuple *fi = tuple->field; if (fi->offset + fi->type->size != expr->result->size) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_ZEROED; item->zeroed = expr->result->size - (fi->offset + fi->type->size); } } } break; case STORAGE_TAGGED: item->type = QD_VALUE; item->value = constw((uint32_t)literal->tagged.tag->id); size_t offs = builtin_type_u32.size; size_t tag_align = literal->tagged.tag->align; if (tag_align > offs) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_ZEROED; item->zeroed = tag_align - offs; offs = tag_align; } if (literal->tagged.tag->size != 0) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item = gen_data_item(ctx, literal->tagged.value, item); offs += literal->tagged.tag->size; } if (offs < type->size) { item->next = xcalloc(1, sizeof(struct qbe_data_item)); item = item->next; item->type = QD_ZEROED; item->zeroed = type->size - offs; } break; case STORAGE_VOID: case STORAGE_DONE: break; case STORAGE_ENUM: case STORAGE_UNION: case STORAGE_ALIAS: case STORAGE_ERROR: case STORAGE_FCONST: case STORAGE_FUNCTION: case STORAGE_ICONST: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_RCONST: case STORAGE_NULL: case STORAGE_VALIST: assert(0); // Invariant } assert(item->type != QD_VALUE || item->value.type); return item; } static void gen_global_decl(struct gen_context *ctx, const struct declaration *decl) { assert(decl->decl_type == DECL_GLOBAL); const struct global_decl *global = &decl->global; if (!global->value) { return; // Forward declaration } struct qbe_def *qdef = xcalloc(1, sizeof(struct qbe_def)); qdef->kind = Q_DATA; qdef->data.align = ALIGN_UNDEFINED; qdef->data.threadlocal = global->threadlocal; qdef->exported = decl->exported; qdef->name = decl->symbol ? xstrdup(decl->symbol) : ident_to_sym(&decl->ident); qdef->file = decl->file; gen_data_item(ctx, global->value, &qdef->data.items); qbe_append_def(ctx->out, qdef); } static void gen_decl(struct gen_context *ctx, const struct declaration *decl) { switch (decl->decl_type) { case DECL_FUNC: gen_function_decl(ctx, decl); break; case DECL_GLOBAL: gen_global_decl(ctx, decl); break; case DECL_TYPE: case DECL_CONST: break; // no-op } } void gen(const struct unit *unit, struct qbe_program *out) { struct gen_context ctx = { .out = out, .ns = unit->ns, .arch = { .ptr = &qbe_long, .sz = &qbe_long, }, }; ctx.out->next = &ctx.out->defs; rtfunc_init(&ctx); ctx.sources = xcalloc(nsources + 1, sizeof(struct gen_value)); for (size_t i = 1; i <= nsources; i++) { struct expression eloc; mkstrliteral(&eloc, "%s", sources[i]); ctx.sources[i] = gen_literal_string(&ctx, &eloc); } const struct declarations *decls = unit->declarations; while (decls) { gen_decl(&ctx, &decls->decl); decls = decls->next; } } harec-0.24.2/src/genutil.c000066400000000000000000000067641464473277600153240ustar00rootroot00000000000000#include #include #include #include "gen.h" #include "qbe.h" #include "types.h" #include "util.h" static struct qbe_value mkrtfunc(struct gen_context *ctx, char *name) { return (struct qbe_value){ .kind = QV_GLOBAL, .name = name, .type = ctx->arch.ptr, }; } void rtfunc_init(struct gen_context *ctx) { ctx->rt = (struct rt){ .abort = mkrtfunc(ctx, "rt.abort"), .ensure = mkrtfunc(ctx, "rt.ensure"), .fixedabort = mkrtfunc(ctx, "rt.abort_fixed"), .free = mkrtfunc(ctx, "rt.free"), .malloc = mkrtfunc(ctx, "rt.malloc"), .memcpy = mkrtfunc(ctx, "rt.memcpy"), .memmove = mkrtfunc(ctx, "rt.memmove"), .memset = mkrtfunc(ctx, "rt.memset"), .strcmp = mkrtfunc(ctx, "rt.strcmp"), .unensure = mkrtfunc(ctx, "rt.unensure"), }; } static struct qbe_value mkval(const struct gen_value *value, const struct qbe_type *type) { struct qbe_value qval = {0}; switch (value->kind) { case GV_CONST: qval.kind = QV_CONST; qval.lval = value->lval; // XXX: Kind of hacky break; case GV_GLOBAL: qval.kind = QV_GLOBAL; qval.name = value->name; qval.threadlocal = value->threadlocal; break; case GV_TEMP: qval.kind = QV_TEMPORARY; qval.name = value->name; break; } qval.type = type; return qval; } struct qbe_value mkqval(struct gen_context *ctx, const struct gen_value *value) { return mkval(value, qtype_lookup(ctx, value->type, true)); } struct qbe_value mklval(struct gen_context *ctx, const struct gen_value *value) { return mkval(value, ctx->arch.ptr); } struct qbe_value mkcopy(struct gen_context *ctx, const struct gen_value *value, const char *fmt) { struct qbe_value qval = mkqval(ctx, value); struct qbe_value copy = mkqtmp(ctx, ctx->arch.ptr, fmt); pushi(ctx->current, ©, Q_COPY, &qval, NULL); return copy; } struct qbe_value mkqtmp(struct gen_context *ctx, const struct qbe_type *qtype, const char *fmt) { return (struct qbe_value){ .kind = QV_TEMPORARY, .type = qtype, .name = gen_name(&ctx->id, fmt), }; } struct gen_value mkgtemp(struct gen_context *ctx, const struct type *type, const char *fmt) { return (struct gen_value){ .kind = GV_TEMP, .type = type, .name = gen_name(&ctx->id, fmt), }; } struct qbe_value mklabel(struct gen_context *ctx, struct qbe_statement *stmt, const char *fmt) { size_t n = snprintf(NULL, 0, fmt, ctx->id); char *l = xcalloc(1, n + 1); snprintf(l, n + 1, fmt, ctx->id); stmt->label = l; stmt->type = Q_LABEL; ctx->id++; return (struct qbe_value){ .kind = QV_LABEL, .name = xstrdup(l), }; } void branch_copyresult(struct gen_context *ctx, struct gen_value result, struct gen_value merged, struct gen_value *out) { // Branching expressions written in the _with style may need to // consolodate each branch's result into a single temporary to return to // the caller. This function facilitates that. if (out || type_dealias(NULL, merged.type)->storage == STORAGE_VOID || type_dealias(NULL, merged.type)->storage == STORAGE_DONE || merged.type->storage == STORAGE_NEVER || type_dealias(NULL, result.type)->storage == STORAGE_VOID || type_dealias(NULL, result.type)->storage == STORAGE_DONE || result.type->storage == STORAGE_NEVER) { return; } struct qbe_value qmerged = mkqval(ctx, &merged); struct qbe_value qval = mkqval(ctx, &result); pushi(ctx->current, &qmerged, Q_COPY, &qval, NULL); } struct qbe_value compute_tagged_memb_offset(const struct type *subtype) { if (builtin_type_u32.align > subtype->align) { return constl(builtin_type_u32.align); } return constl(subtype->align); } harec-0.24.2/src/identifier.c000066400000000000000000000042461464473277600157700ustar00rootroot00000000000000#include #include #include #include #include "identifier.h" #include "util.h" uint32_t identifier_hash(uint32_t init, const struct identifier *ident) { init = fnv1a_s(init, ident->name); init = fnv1a(init, 0); if (ident->ns) { init = identifier_hash(init, ident->ns); } return init; } static void identifier_unparse_ex(const struct identifier *ident, const char *delim, size_t delimlen, char **buf, size_t *len, size_t *cap) { if (ident->ns) { identifier_unparse_ex(ident->ns, delim, delimlen, buf, len, cap); memcpy(*buf + *len, delim, delimlen); *len += delimlen; } size_t namelen = strlen(ident->name); if (*len + namelen + delimlen > *cap) { *cap += namelen + delimlen; *buf = xrealloc(*buf, *cap); } memcpy(*buf + *len, ident->name, namelen + 1); *len += namelen; } char * identifier_unparse(const struct identifier *ident) { size_t len = 0; size_t cap = strlen(ident->name) + 1; char *buf = xcalloc(cap, sizeof(char)); identifier_unparse_ex(ident, "::", 2, &buf, &len, &cap); return buf; } int identifier_unparse_static(const struct identifier *ident, char *buf) { if (ident->ns) { int prefix = identifier_unparse_static(ident->ns, buf); int n = snprintf(&buf[prefix], IDENT_BUFSIZ - prefix, "::%s", ident->name); n += prefix; assert(n < IDENT_BUFSIZ); return n; } int n = snprintf(buf, IDENT_BUFSIZ, "%s", ident->name); assert(n < IDENT_BUFSIZ); return n; } char * ident_to_sym(const struct identifier *ident) { size_t len = 0; size_t cap = strlen(ident->name) + 1; char *buf = xcalloc(cap, sizeof(char)); identifier_unparse_ex(ident, ".", 1, &buf, &len, &cap); return buf; } void identifier_dup(struct identifier *new, const struct identifier *ident) { assert(ident && new); new->name = xstrdup(ident->name); if (ident->ns) { new->ns = xcalloc(1, sizeof(struct identifier)); identifier_dup(new->ns, ident->ns); } else { new->ns = NULL; } } bool identifier_eq(const struct identifier *a, const struct identifier *b) { if (!a && !b) { return true; } else if (!a || !b) { return false; } if (strcmp(a->name, b->name) != 0) { return false; } return identifier_eq(a->ns, b->ns); } harec-0.24.2/src/lex.c000066400000000000000000000604611464473277600144370ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "lex.h" #include "utf8.h" #include "util.h" static const char *tokens[] = { // Must match enum lexical_token (lex.h) [T_ATTR_FINI] = "@fini", [T_ATTR_INIT] = "@init", [T_ATTR_OFFSET] = "@offset", [T_ATTR_PACKED] = "@packed", [T_ATTR_SYMBOL] = "@symbol", [T_ATTR_TEST] = "@test", [T_ATTR_THREADLOCAL] = "@threadlocal", [T_UNDERSCORE] = "_", [T_ABORT] = "abort", [T_ALIGN] = "align", [T_ALLOC] = "alloc", [T_APPEND] = "append", [T_AS] = "as", [T_ASSERT] = "assert", [T_BOOL] = "bool", [T_BREAK] = "break", [T_CASE] = "case", [T_CONST] = "const", [T_CONTINUE] = "continue", [T_DEFER] = "defer", [T_DEF] = "def", [T_DELETE] = "delete", [T_DONE] = "done", [T_ELSE] = "else", [T_ENUM] = "enum", [T_EXPORT] = "export", [T_F32] = "f32", [T_F64] = "f64", [T_FALSE] = "false", [T_FN] = "fn", [T_FOR] = "for", [T_FREE] = "free", [T_I16] = "i16", [T_I32] = "i32", [T_I64] = "i64", [T_I8] = "i8", [T_IF] = "if", [T_INSERT] = "insert", [T_INT] = "int", [T_IS] = "is", [T_LEN] = "len", [T_LET] = "let", [T_MATCH] = "match", [T_NEVER] = "never", [T_NULL] = "null", [T_NULLABLE] = "nullable", [T_OFFSET] = "offset", [T_OPAQUE] = "opaque", [T_RETURN] = "return", [T_RUNE] = "rune", [T_SIZE] = "size", [T_STATIC] = "static", [T_STR] = "str", [T_STRUCT] = "struct", [T_SWITCH] = "switch", [T_TRUE] = "true", [T_TYPE] = "type", [T_U16] = "u16", [T_U32] = "u32", [T_U64] = "u64", [T_U8] = "u8", [T_UINT] = "uint", [T_UINTPTR] = "uintptr", [T_UNION] = "union", [T_USE] = "use", [T_VAARG] = "vaarg", [T_VAEND] = "vaend", [T_VALIST] = "valist", [T_VASTART] = "vastart", [T_VOID] = "void", [T_YIELD] = "yield", // Operators [T_ARROW] = "=>", [T_BANDEQ] = "&=", [T_BAND] = "&", [T_BNOT] = "~", [T_BOR] = "|", [T_COLON] = ":", [T_COMMA] = ",", [T_DIV] = "/", [T_DIVEQ] = "/=", [T_DOT] = ".", [T_DOUBLE_COLON] = "::", [T_DOUBLE_DOT] = "..", [T_ELLIPSIS] = "...", [T_EQUAL] = "=", [T_GREATER] = ">", [T_GREATEREQ] = ">=", [T_LAND] = "&&", [T_LANDEQ] = "&&=", [T_LBRACE] = "{", [T_LBRACKET] = "[", [T_LEQUAL] = "==", [T_LESS] = "<", [T_LESSEQ] = "<=", [T_LNOT] = "!", [T_LOR] = "||", [T_LOREQ] = "||=", [T_LPAREN] = "(", [T_LSHIFT] = "<<", [T_LSHIFTEQ] = "<<=", [T_LXOR] = "^^", [T_LXOREQ] = "^^=", [T_MINUS] = "-", [T_MINUSEQ] = "-=", [T_MODEQ] = "%=", [T_MODULO] = "%", [T_NEQUAL] = "!=", [T_BOREQ] = "|=", [T_PLUS] = "+", [T_PLUSEQ] = "+=", [T_QUESTION] = "?", [T_RBRACE] = "}", [T_RBRACKET] = "]", [T_RPAREN] = ")", [T_RSHIFT] = ">>", [T_RSHIFTEQ] = ">>=", [T_SEMICOLON] = ";", [T_TIMES] = "*", [T_TIMESEQ] = "*=", [T_BXOR] = "^", [T_BXOREQ] = "^=", }; static_assert(sizeof(tokens) / sizeof(const char *) == T_LAST_OPERATOR + 1, "tokens array isn't in sync with lexical_token enum"); static noreturn void error(struct location loc, const char *fmt, ...) { xfprintf(stderr, "%s:%d:%d: syntax error: ", sources[loc.file], loc.lineno, loc.colno); va_list ap; va_start(ap, fmt); xvfprintf(stderr, fmt, ap); va_end(ap); xfprintf(stderr, "\n"); errline(loc); exit(EXIT_LEX); } void lex_init(struct lexer *lexer, FILE *f, int fileid) { memset(lexer, 0, sizeof(*lexer)); lexer->in = f; lexer->bufsz = 256; lexer->buf = xcalloc(1, lexer->bufsz); lexer->un.token = T_NONE; lexer->loc.lineno = 1; lexer->loc.colno = 0; lexer->loc.file = fileid; lexer->c[0] = UINT32_MAX; lexer->c[1] = UINT32_MAX; } void lex_finish(struct lexer *lexer) { fclose(lexer->in); free(lexer->buf); } static void update_lineno(struct location *loc, uint32_t c) { if (c == '\n') { loc->lineno++; loc->colno = 0; } else if (c == '\t') { loc->colno += 8; } else { loc->colno++; } } static void append_buffer(struct lexer *lexer, const char *buf, size_t sz) { if (lexer->buflen + sz >= lexer->bufsz) { lexer->bufsz *= 2; lexer->buf = xrealloc(lexer->buf, lexer->bufsz); } memcpy(lexer->buf + lexer->buflen, buf, sz); lexer->buflen += sz; lexer->buf[lexer->buflen] = '\0'; } static uint32_t next(struct lexer *lexer, struct location *loc, bool buffer) { uint32_t c; if (lexer->c[0] != UINT32_MAX) { c = lexer->c[0]; lexer->c[0] = lexer->c[1]; lexer->c[1] = UINT32_MAX; } else { c = utf8_get(lexer->in); update_lineno(&lexer->loc, c); if (c == UTF8_INVALID && !feof(lexer->in)) { error(lexer->loc, "Invalid UTF-8 sequence encountered"); } } if (loc != NULL) { *loc = lexer->loc; for (size_t i = 0; i < 2 && lexer->c[i] != UINT32_MAX; i++) { update_lineno(&lexer->loc, lexer->c[i]); } } if (c == C_EOF || !buffer) { return c; } char buf[UTF8_MAX_SIZE]; size_t sz = utf8_encode(&buf[0], c); append_buffer(lexer, buf, sz); return c; } static bool isharespace(uint32_t c) { return c == '\t' || c == '\n' || c == ' '; } static uint32_t wgetc(struct lexer *lexer, struct location *loc) { uint32_t c; while ((c = next(lexer, loc, false)) != C_EOF && isharespace(c)) ; return c; } static void clearbuf(struct lexer *lexer) { lexer->buflen = 0; lexer->buf[0] = 0; } static void consume(struct lexer *lexer, size_t n) { for (size_t i = 0; i < n; i++) { while ((lexer->buf[--lexer->buflen] & 0xC0) == 0x80) ; } lexer->buf[lexer->buflen] = 0; } static void push(struct lexer *lexer, uint32_t c, bool buffer) { assert(lexer->c[1] == UINT32_MAX); lexer->c[1] = lexer->c[0]; lexer->c[0] = c; if (buffer) { consume(lexer, 1); } } static int cmp_keyword(const void *va, const void *vb) { return strcmp(*(const char **)va, *(const char **)vb); } static enum lexical_token lex_name(struct lexer *lexer, struct token *out) { uint32_t c = next(lexer, &out->loc, true); assert(c != C_EOF && c <= 0x7F && (isalpha(c) || c == '_' || c == '@')); while ((c = next(lexer, NULL, true)) != C_EOF) { if (c > 0x7F || (!isalnum(c) && c != '_')) { push(lexer, c, true); break; } } void *token = bsearch(&lexer->buf, tokens, T_LAST_KEYWORD + 1, sizeof(tokens[0]), cmp_keyword); if (!token) { if (lexer->buf[0] == '@') { error(out->loc, "Unknown attribute %s", lexer->buf); } out->token = T_NAME; out->name = xstrdup(lexer->buf); } else { out->token = (const char **)token - tokens; } clearbuf(lexer); return out->token; } static uint64_t compute_exp(uint64_t n, int exponent, bool _signed) { if (n == 0) { return 0; } for (int i = 0; i < exponent; i++) { uint64_t old = n; n *= 10; if (n / 10 != old) { errno = ERANGE; return INT64_MAX; } } if (_signed && n > (uint64_t)INT64_MIN) { errno = ERANGE; return INT64_MAX; } return n; } static void lex_number(struct lexer *lexer, struct token *out) { enum bases { BIN = 1, OCT, HEX, DEC = 0x07, MASK = DEC }; static_assert((BIN | OCT | HEX | DEC) == DEC, "DEC bits must be a superset of all other bases"); enum flags { FLT = 3, EXP, SUFF, DIG, SEP, }; static const char chrs[][24] = { [BIN] = "01", [OCT] = "01234567", [DEC] = "0123456789", [HEX] = "0123456789abcdefABCDEF", }; static const char matching_states[0x80][7] = { ['.'] = {DEC, HEX, 0}, ['e'] = {DEC, DEC | 1<loc, true), last = 0; assert(c != C_EOF && c <= 0x7F && isdigit(c)); if (c == '0') { c = next(lexer, NULL, true); if (c <= 0x7F && (isdigit(c) || c == '_')) { error(out->loc, "Leading zero in base 10 literal"); } else if (c == 'b') { state = BIN | 1 << DIG; base = 2; } else if (c == 'o') { state = OCT | 1 << DIG; base = 8; } else if (c == 'x') { state = HEX | 1 << DIG; base = 16; } } if (state != DEC) { last = c; c = next(lexer, NULL, true); } size_t exp = 0, suff = 0; do { if (strchr(chrs[state & MASK], c)) { state &= ~(1 << DIG | 1 << SEP); last = c; continue; } else if (state & 1 << SEP) { error(out->loc, "Expected digit after separator"); } else if (c > 0x7f || !strchr(matching_states[c], state)) { goto end; } oldstate = state; switch (c) { case '.': if (lexer->require_int) { goto want_int; } state |= 1 << FLT; break; case '-': case 'p': case 'P': state |= 1 << FLT; /* fallthrough */ case 'e': case 'E': case '+': state |= DEC | 1 << EXP; exp = lexer->buflen - 1; break; case 'f': state |= 1 << FLT; /* fallthrough */ case 'i': case 'u': case 'z': state |= DEC | 1 << SUFF; suff = lexer->buflen - 1; break; case '_': consume(lexer, 1); state |= 1 << SEP; break; default: goto end; } if (state & 1 << FLT && lexer->require_int) { error(out->loc, "Expected integer literal"); } last = c; state |= 1 << DIG; } while ((c = next(lexer, NULL, true)) != C_EOF); last = 0; end: if (last && !strchr("iuz", last) && !strchr(chrs[state & MASK], last)) { state = oldstate; push(lexer, c, true); push(lexer, last, true); } else if (c != C_EOF) { want_int: push(lexer, c, true); } out->token = T_NUMBER; lexer->require_int = false; enum kind { UNKNOWN = -1, ICONST, SIGNED, UNSIGNED, FLOAT } kind = UNKNOWN; static const struct { const char suff[4]; enum kind kind; enum type_storage storage; } storages[] = { {"f32", FLOAT, STORAGE_F32}, {"f64", FLOAT, STORAGE_F64}, {"i", SIGNED, STORAGE_INT}, {"i16", SIGNED, STORAGE_I16}, {"i32", SIGNED, STORAGE_I32}, {"i64", SIGNED, STORAGE_I64}, {"i8", SIGNED, STORAGE_I8}, {"u", UNSIGNED, STORAGE_UINT}, {"u16", UNSIGNED, STORAGE_U16}, {"u32", UNSIGNED, STORAGE_U32}, {"u64", UNSIGNED, STORAGE_U64}, {"u8", UNSIGNED, STORAGE_U8}, {"z", UNSIGNED, STORAGE_SIZE}, }; if (suff) { for (size_t i = 0; i < sizeof storages / sizeof storages[0]; i++) { if (!strcmp(storages[i].suff, lexer->buf + suff)) { out->storage = storages[i].storage; kind = storages[i].kind; break; } } if (kind == UNKNOWN) { error(out->loc, "Invalid suffix '%s'", lexer->buf + suff); } } if (state & 1 << FLT) { if (kind == UNKNOWN) { out->storage = STORAGE_FCONST; } else if (kind != FLOAT) { error(out->loc, "Unexpected decimal point in integer literal"); } out->fval = strtod(lexer->buf, NULL); clearbuf(lexer); return; } if (kind == UNKNOWN) { kind = ICONST; out->storage = STORAGE_ICONST; } uint64_t exponent = 0; errno = 0; if (exp != 0) { exponent = strtoumax(lexer->buf + exp + 1, NULL, 10); } out->uval = strtoumax(lexer->buf + (base == 10 ? 0 : 2), NULL, base); out->uval = compute_exp(out->uval, exponent, kind == SIGNED); if (errno == ERANGE) { error(out->loc, "Integer literal overflow"); } if (kind == ICONST && out->uval > (uint64_t)INT64_MAX) { out->storage = STORAGE_U64; } else if (kind == SIGNED && out->uval == (uint64_t)INT64_MIN) { // XXX: Hack out->ival = INT64_MIN; } else if (kind != UNSIGNED) { out->ival = (int64_t)out->uval; } clearbuf(lexer); } static size_t lex_rune(struct lexer *lexer, char *out) { char buf[9]; char *endptr; struct location loc; uint32_t c = next(lexer, NULL, false); assert(c != C_EOF); switch (c) { case '\\': loc = lexer->loc; c = next(lexer, NULL, false); switch (c) { case '0': out[0] = '\0'; return 1; case 'a': out[0] = '\a'; return 1; case 'b': out[0] = '\b'; return 1; case 'f': out[0] = '\f'; return 1; case 'n': out[0] = '\n'; return 1; case 'r': out[0] = '\r'; return 1; case 't': out[0] = '\t'; return 1; case 'v': out[0] = '\v'; return 1; case '\\': out[0] = '\\'; return 1; case '\'': out[0] = '\''; return 1; case '"': out[0] = '\"'; return 1; case 'x': buf[0] = next(lexer, NULL, false); buf[1] = next(lexer, NULL, false); buf[2] = '\0'; c = strtoul(&buf[0], &endptr, 16); if (*endptr != '\0') { error(loc, "Invalid hex literal"); } out[0] = c; return 1; case 'u': buf[0] = next(lexer, NULL, false); buf[1] = next(lexer, NULL, false); buf[2] = next(lexer, NULL, false); buf[3] = next(lexer, NULL, false); buf[4] = '\0'; c = strtoul(&buf[0], &endptr, 16); if (*endptr != '\0') { error(loc, "Invalid hex literal"); } return utf8_encode(out, c); case 'U': buf[0] = next(lexer, NULL, false); buf[1] = next(lexer, NULL, false); buf[2] = next(lexer, NULL, false); buf[3] = next(lexer, NULL, false); buf[4] = next(lexer, NULL, false); buf[5] = next(lexer, NULL, false); buf[6] = next(lexer, NULL, false); buf[7] = next(lexer, NULL, false); buf[8] = '\0'; c = strtoul(&buf[0], &endptr, 16); if (*endptr != '\0') { error(loc, "Invalid hex literal"); } return utf8_encode(out, c); case C_EOF: error(lexer->loc, "Unexpected end of file"); default: error(loc, "Invalid escape '\\%c'", c); } assert(0); default: return utf8_encode(out, c); } assert(0); } static enum lexical_token lex_string(struct lexer *lexer, struct token *out) { uint32_t c = next(lexer, &out->loc, false); uint32_t delim; char buf[UTF8_MAX_SIZE + 1]; switch (c) { case '"': case '`': delim = c; while ((c = next(lexer, NULL, false)) != delim) { if (c == C_EOF) { error(lexer->loc, "Unexpected end of file"); } push(lexer, c, false); if (delim == '"') { size_t sz = lex_rune(lexer, buf); append_buffer(lexer, buf, sz); } else { next(lexer, NULL, true); } } char *s = xcalloc(lexer->buflen + 1, 1); memcpy(s, lexer->buf, lexer->buflen); out->token = T_NUMBER; out->storage = STORAGE_STRING; out->string.len = lexer->buflen; out->string.value = s; clearbuf(lexer); return out->token; case '\'': c = next(lexer, NULL, false); switch (c) { case '\'': error(out->loc, "Expected rune before trailing single quote"); case '\\': push(lexer, c, false); struct location loc = lexer->loc; size_t sz = lex_rune(lexer, buf); buf[sz] = '\0'; const char *s = buf; out->rune = utf8_decode(&s); if (out->rune == UTF8_INVALID) { error(loc, "invalid UTF-8 in rune literal"); } break; default: out->rune = c; } if (next(lexer, NULL, false) != '\'') { error(out->loc, "Expected trailing single quote"); } out->token = T_NUMBER; out->storage = STORAGE_RCONST; return out->token; default: assert(0); // Invariant } assert(0); } static enum lexical_token lex3(struct lexer *lexer, struct token *out, uint32_t c) { assert(c != C_EOF); switch (c) { case '.': switch ((c = next(lexer, NULL, false))) { case '.': switch ((c = next(lexer, NULL, false))) { case '.': out->token = T_ELLIPSIS; break; default: push(lexer, c, false); out->token = T_DOUBLE_DOT; break; } break; default: push(lexer, c, false); out->token = T_DOT; lexer->require_int = true; break; } break; case '<': switch ((c = next(lexer, NULL, false))) { case '<': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_LSHIFTEQ; break; default: push(lexer, c, false); out->token = T_LSHIFT; break; } break; case '=': out->token = T_LESSEQ; break; default: push(lexer, c, false); out->token = T_LESS; break; } break; case '>': switch ((c = next(lexer, NULL, false))) { case '>': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_RSHIFTEQ; break; default: push(lexer, c, false); out->token = T_RSHIFT; break; } break; case '=': out->token = T_GREATEREQ; break; default: push(lexer, c, false); out->token = T_GREATER; break; } break; case '&': switch ((c = next(lexer, NULL, false))) { case '&': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_LANDEQ; break; default: push(lexer, c, false); out->token = T_LAND; break; } break; case '=': out->token = T_BANDEQ; break; default: push(lexer, c, false); out->token = T_BAND; break; } break; case '|': switch ((c = next(lexer, NULL, false))) { case '|': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_LOREQ; break; default: push(lexer, c, false); out->token = T_LOR; break; } break; case '=': out->token = T_BOREQ; break; default: push(lexer, c, false); out->token = T_BOR; break; } break; case '^': switch ((c = next(lexer, NULL, false))) { case '^': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_LXOREQ; break; default: push(lexer, c, false); out->token = T_LXOR; break; } break; case '=': out->token = T_BXOREQ; break; default: push(lexer, c, false); out->token = T_BXOR; break; } break; default: assert(0); // Invariant } return out->token; } static enum lexical_token lex2(struct lexer *lexer, struct token *out, uint32_t c) { assert(c != C_EOF); switch (c) { case '*': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_TIMESEQ; break; default: push(lexer, c, false); out->token = T_TIMES; break; } break; case '%': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_MODEQ; break; default: push(lexer, c, false); out->token = T_MODULO; break; } break; case '/': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_DIVEQ; break; case '/': while ((c = next(lexer, NULL, false)) != C_EOF && c != '\n') ; return lex(lexer, out); default: push(lexer, c, false); out->token = T_DIV; break; } break; case '+': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_PLUSEQ; break; default: push(lexer, c, false); out->token = T_PLUS; break; } break; case '-': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_MINUSEQ; break; default: push(lexer, c, false); out->token = T_MINUS; break; } break; case ':': switch ((c = next(lexer, NULL, false))) { case ':': out->token = T_DOUBLE_COLON; break; default: push(lexer, c, false); out->token = T_COLON; break; } break; case '!': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_NEQUAL; break; default: push(lexer, c, false); out->token = T_LNOT; break; } break; case '=': switch ((c = next(lexer, NULL, false))) { case '=': out->token = T_LEQUAL; break; case '>': out->token = T_ARROW; break; default: push(lexer, c, false); out->token = T_EQUAL; break; } break; default: assert(0); // Invariant } return out->token; } static const char * rune_unparse(uint32_t c) { static char buf[11]; switch (c) { case '\0': snprintf(buf, sizeof(buf), "\\0"); break; case '\a': snprintf(buf, sizeof(buf), "\\a"); break; case '\b': snprintf(buf, sizeof(buf), "\\b"); break; case '\f': snprintf(buf, sizeof(buf), "\\f"); break; case '\n': snprintf(buf, sizeof(buf), "\\n"); break; case '\r': snprintf(buf, sizeof(buf), "\\r"); break; case '\t': snprintf(buf, sizeof(buf), "\\t"); break; case '\v': snprintf(buf, sizeof(buf), "\\v"); break; case '\\': snprintf(buf, sizeof(buf), "\\\\"); break; case '\'': snprintf(buf, sizeof(buf), "\\'"); break; case '"': snprintf(buf, sizeof(buf), "\\\""); break; default: if (c > 0xffff) { snprintf(buf, sizeof(buf), "\\U%08x", c); } else if (c > 0x7F) { snprintf(buf, sizeof(buf), "\\u%04x", c); } else if (!isprint(c)) { snprintf(buf, sizeof(buf), "\\x%02x", c); } else { buf[utf8_encode(buf, c)] = '\0'; } break; } return buf; } enum lexical_token lex(struct lexer *lexer, struct token *out) { if (lexer->un.token != T_NONE) { *out = lexer->un; lexer->un.token = T_NONE; return out->token; } uint32_t c = wgetc(lexer, &out->loc); if (c == C_EOF) { out->token = T_EOF; return out->token; } if (c <= 0x7F && isdigit(c)) { push(lexer, c, false); lex_number(lexer, out); return T_NUMBER; } lexer->require_int = false; if (c <= 0x7F && (isalpha(c) || c == '_' || c == '@')) { push(lexer, c, false); return lex_name(lexer, out); } switch (c) { case '"': case '`': case '\'': push(lexer, c, false); return lex_string(lexer, out); case '.': // . .. ... case '<': // < << <= <<= case '>': // > >> >= >>= case '&': // & && &= &&= case '|': // | || |= ||= case '^': // ^ ^^ ^= ^^= return lex3(lexer, out, c); case '*': // * *= case '%': // % %= case '/': // / /= // case '+': // + += case '-': // - -= case ':': // : :: case '!': // ! != case '=': // = == => return lex2(lexer, out, c); case '~': out->token = T_BNOT; break; case ',': out->token = T_COMMA; break; case '{': out->token = T_LBRACE; break; case '[': out->token = T_LBRACKET; break; case '(': out->token = T_LPAREN; break; case '}': out->token = T_RBRACE; break; case ']': out->token = T_RBRACKET; break; case ')': out->token = T_RPAREN; break; case ';': out->token = T_SEMICOLON; break; case '?': out->token = T_QUESTION; break; default: error(lexer->loc, "unexpected codepoint '%s'", rune_unparse(c)); } return out->token; } void token_finish(struct token *tok) { switch (tok->token) { case T_NAME: free(tok->name); break; case T_NUMBER: switch (tok->storage) { case STORAGE_STRING: free(tok->string.value); break; default: break; } break; default: break; } tok->token = 0; tok->storage = 0; tok->loc.file = 0; tok->loc.colno = 0; tok->loc.lineno = 0; } const char * lexical_token_str(enum lexical_token tok) { switch (tok) { case T_NAME: return "name"; case T_NUMBER: return "number"; case T_EOF: return "end of file"; case T_NONE: abort(); default: assert(tok < sizeof(tokens) / sizeof(tokens[0])); return tokens[tok]; } } static const char * string_unparse(const struct token *tok) { static char buf[1024]; assert(tok->token == T_NUMBER && tok->storage == STORAGE_STRING); int bytes = 0; memset(buf, 0, sizeof(buf)); bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "\""); const char *s = tok->string.value; for (uint32_t c = utf8_decode(&s); s - tok->string.value <= (ptrdiff_t)tok->string.len; c = utf8_decode(&s)) { bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "%s", rune_unparse(c)); } bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "\""); return buf; } const char * token_str(const struct token *tok) { static char buf[1024]; int bytes = 0; switch (tok->token) { case T_NAME: snprintf(buf, sizeof(buf), "name %s", tok->name); return buf; case T_NUMBER: switch (tok->storage) { case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_SIZE: snprintf(buf, sizeof(buf), "%" PRIu64, tok->uval); break; case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_ICONST: case STORAGE_INT: snprintf(buf, sizeof(buf), "%" PRIi64, tok->ival); break; case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: snprintf(buf, sizeof(buf), "%f", tok->fval); break; case STORAGE_RCONST: bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "'"); bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "%s", rune_unparse(tok->rune)); bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "'"); break; case STORAGE_STRING: return string_unparse(tok); case STORAGE_ALIAS: case STORAGE_ARRAY: case STORAGE_BOOL: case STORAGE_ENUM: case STORAGE_ERROR: case STORAGE_FUNCTION: case STORAGE_POINTER: case STORAGE_NEVER: case STORAGE_NULL: case STORAGE_OPAQUE: case STORAGE_RUNE: case STORAGE_SLICE: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: assert(0); } return buf; default:; const char *out = lexical_token_str(tok->token); return out; } } void unlex(struct lexer *lexer, const struct token *in) { assert(lexer->un.token == T_NONE); lexer->un = *in; } harec-0.24.2/src/main.c000066400000000000000000000115231464473277600145660ustar00rootroot00000000000000#include #include #include #include #include #include #include "ast.h" #include "check.h" #include "emit.h" #include "gen.h" #include "lex.h" #include "parse.h" #include "qbe.h" #include "type_store.h" #include "typedef.h" #include "util.h" static void usage(const char *argv_0) { xfprintf(stderr, "Usage: %s [-a arch] [-D ident[:type]=value] [-M path] [-m symbol] [-N namespace] [-o output] [-T] [-t typedefs] [-v] input.ha...\n\n", argv_0); xfprintf(stderr, "-a: set target architecture\n" "-D: define a constant\n" "-h: print this help text\n" "-M: set module path prefix, to be stripped from error messages\n" "-m: set symbol of hosted main function\n" "-N: override namespace for module\n" "-o: set output file name\n" "-T: emit tests\n" "-t: emit typedefs to file\n" "-v: print version and exit\n"); } static struct ast_global_decl * parse_define(const char *argv_0, const char *in) { struct ast_global_decl *def = xcalloc(1, sizeof(struct ast_global_decl)); struct token tok; struct lexer lexer; FILE *f = fmemopen((char *)in, strlen(in), "r"); if (f == NULL) { perror("fmemopen"); exit(EXIT_ABNORMAL); } const char *d = "-D"; sources = &d; lex_init(&lexer, f, 0); parse_identifier(&lexer, &def->ident, false); def->type = NULL; if (lex(&lexer, &tok) == T_COLON) { def->type = parse_type(&lexer); lex(&lexer, &tok); } if (tok.token != T_EQUAL) { lex_finish(&lexer); usage(argv_0); exit(EXIT_USER); } def->init = parse_expression(&lexer); lex_finish(&lexer); return def; } int main(int argc, char *argv[]) { const char *output = NULL, *typedefs = NULL; const char *target = DEFAULT_TARGET; const char *modpath = NULL; const char *mainsym = "main"; bool is_test = false; struct unit unit = {0}; struct lexer lexer; struct ast_global_decl *defines = NULL, **next_def = &defines; int c; while ((c = getopt(argc, argv, "a:D:hM:m:N:o:Tt:v")) != -1) { switch (c) { case 'a': target = optarg; break; case 'D': *next_def = parse_define(argv[0], optarg); next_def = &(*next_def)->next; break; case 'h': usage(argv[0]); return EXIT_SUCCESS; case 'M': modpath = optarg; break; case 'm': mainsym = optarg; break; case 'N': unit.ns = xcalloc(1, sizeof(struct identifier)); if (strlen(optarg) == 0) { unit.ns->name = ""; unit.ns->ns = NULL; } else { FILE *in = fmemopen(optarg, strlen(optarg), "r"); if (in == NULL) { perror("fmemopen"); exit(EXIT_ABNORMAL); } const char *ns = "-N"; sources = &ns; lex_init(&lexer, in, 0); parse_identifier(&lexer, unit.ns, false); lex_finish(&lexer); } break; case 'o': output = optarg; break; case 'T': is_test = true; break; case 't': typedefs = optarg; break; case 'v': xfprintf(stdout, "harec %s\n", VERSION); return EXIT_SUCCESS; default: usage(argv[0]); return EXIT_USER; } } builtin_types_init(target); nsources = argc - optind; if (nsources == 0) { usage(argv[0]); return EXIT_USER; } struct ast_unit aunit = {0}; struct ast_subunit *subunit = &aunit.subunits; sources = xcalloc(nsources + 2, sizeof(char **)); memcpy((char **)sources + 1, argv + optind, sizeof(char **) * nsources); sources[0] = ""; if (modpath) { size_t modlen = strlen(modpath); for (size_t i = 1; i <= nsources; i++) { if (strncmp(sources[i], modpath, modlen) == 0) { sources[i] += modlen; } } } for (size_t i = 0; i < nsources; ++i) { FILE *in; const char *path = argv[optind + i]; if (strcmp(path, "-") == 0) { in = stdin; sources[i + 1] = ""; } else { in = fopen(path, "r"); struct stat buf; if (in && fstat(fileno(in), &buf) == 0 && S_ISDIR(buf.st_mode) != 0) { xfprintf(stderr, "Unable to open %s for reading: Is a directory\n", path); return EXIT_USER; } } if (!in) { xfprintf(stderr, "Unable to open %s for reading: %s\n", path, strerror(errno)); return EXIT_ABNORMAL; } lex_init(&lexer, in, i + 1); parse(&lexer, subunit); if (i + 1 < nsources) { subunit->next = xcalloc(1, sizeof(struct ast_subunit)); subunit = subunit->next; } lex_finish(&lexer); } static type_store ts = {0}; check(&ts, is_test, mainsym, defines, &aunit, &unit); if (typedefs) { FILE *out = fopen(typedefs, "w"); if (!out) { xfprintf(stderr, "Unable to open %s for writing: %s\n", typedefs, strerror(errno)); return EXIT_ABNORMAL; } emit_typedefs(&unit, out); fclose(out); } struct qbe_program prog = {0}; gen(&unit, &prog); FILE *out; if (!output) { out = stdout; } else { out = fopen(output, "w"); if (!out) { xfprintf(stderr, "Unable to open %s for writing: %s\n", output, strerror(errno)); return EXIT_ABNORMAL; } } emit(&prog, out); fclose(out); return EXIT_SUCCESS; } harec-0.24.2/src/mod.c000066400000000000000000000035401464473277600144210ustar00rootroot00000000000000#include #include #include #include #include #include "check.h" #include "identifier.h" #include "lex.h" #include "mod.h" #include "parse.h" #include "scope.h" #include "util.h" // unfortunately necessary since this is used in an array declaration, and we // don't want a VLA #define strlen_HARE_TD_ (sizeof("HARE_TD_") - 1) struct scope * module_resolve(struct context *ctx, const struct ast_global_decl *defines, const struct identifier *ident) { uint32_t hash = identifier_hash(FNV1A_INIT, ident); struct modcache **bucket = &ctx->modcache[hash % MODCACHE_BUCKETS]; for (; *bucket; bucket = &(*bucket)->next) { if (identifier_eq(&(*bucket)->ident, ident)) { return (*bucket)->scope; } } struct lexer lexer = {0}; struct ast_unit aunit = {0}; // env = "HARE_TD_foo::bar::baz" char env[strlen_HARE_TD_ + IDENT_BUFSIZ] = "HARE_TD_"; identifier_unparse_static(ident, &env[strlen_HARE_TD_]); char *path = getenv(env); if (!path) { xfprintf(stderr, "Could not open module '%s': typedef variable $%s not set\n", &env[strlen_HARE_TD_], env); exit(EXIT_USER); } FILE *f = fopen(path, "r"); if (!f) { xfprintf(stderr, "Could not open module '%s' for reading from %s: %s\n", &env[strlen_HARE_TD_], path, strerror(errno)); exit(EXIT_ABNORMAL); } const char *old = sources[0]; sources[0] = path; lex_init(&lexer, f, 0); parse(&lexer, &aunit.subunits); lex_finish(&lexer); // TODO: Free unused bits struct unit u = {0}; struct scope *scope = check_internal(ctx->store, ctx->modcache, ctx->is_test, ctx->mainsym, defines, &aunit, &u, true); sources[0] = old; bucket = &ctx->modcache[hash % MODCACHE_BUCKETS]; struct modcache *item = xcalloc(1, sizeof(struct modcache)); identifier_dup(&item->ident, ident); item->scope = scope; item->next = *bucket; *bucket = item; return scope; } harec-0.24.2/src/parse.c000066400000000000000000001637251464473277600147700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "ast.h" #include "identifier.h" #include "lex.h" #include "parse.h" #include "types.h" #include "utf8.h" #include "util.h" static noreturn void error(struct location loc, const char *fmt, ...) { xfprintf(stderr, "%s:%d:%d: ", sources[loc.file], loc.lineno, loc.colno); va_list ap; va_start(ap, fmt); xvfprintf(stderr, fmt, ap); va_end(ap); xfprintf(stderr, "\n"); errline(loc); exit(EXIT_PARSE); } static void synassert_msg(bool cond, const char *msg, struct token *tok) { if (!cond) { error(tok->loc, "syntax error: %s (found '%s')", msg, token_str(tok)); } } static noreturn void vsynerr(struct token *tok, va_list ap) { enum lexical_token t = va_arg(ap, enum lexical_token); xfprintf(stderr, "%s:%d:%d: syntax error: expected ", sources[tok->loc.file], tok->loc.lineno, tok->loc.colno); while (t != T_EOF) { if (t == T_NUMBER || t == T_NAME) { xfprintf(stderr, "%s", lexical_token_str(t)); } else { xfprintf(stderr, "'%s'", lexical_token_str(t)); } t = va_arg(ap, enum lexical_token); xfprintf(stderr, ", "); } xfprintf(stderr, "found '%s'\n", token_str(tok)); errline(tok->loc); exit(EXIT_PARSE); } static noreturn void synerr(struct token *tok, ...) { va_list ap; va_start(ap, tok); vsynerr(tok, ap); va_end(ap); } static void synassert(bool cond, struct token *tok, ...) { if (!cond) { va_list ap; va_start(ap, tok); vsynerr(tok, ap); va_end(ap); } } static void want(struct lexer *lexer, enum lexical_token ltok, struct token *tok) { struct token _tok = {0}; struct token *out = tok ? tok : &_tok; lex(lexer, out); synassert(out->token == ltok, out, ltok, T_EOF); if (!tok) { token_finish(out); } } static struct ast_expression * mkexpr(struct location loc) { struct ast_expression *exp = xcalloc(1, sizeof(struct ast_expression)); exp->loc = loc; return exp; } static struct ast_type * mktype(struct location loc) { struct ast_type *t = xcalloc(1, sizeof(struct ast_type)); t->loc = loc; return t; } static struct ast_function_parameters * mkfuncparams(struct location loc) { struct ast_function_parameters *p = xcalloc(1, sizeof(struct ast_function_parameters)); p->loc = loc; return p; } static struct ast_expression *parse_statement(struct lexer *lexer); bool parse_identifier(struct lexer *lexer, struct identifier *ident, bool trailing) { struct token tok = {0}; struct location loc = {0}; struct identifier *i = ident; *ident = (struct identifier){0}; size_t len = 0; bool found_trailing = false; while (!i->name) { switch (lex(lexer, &tok)) { case T_NAME: len += strlen(tok.name); i->name = xstrdup(tok.name); if (loc.file == 0) { loc = tok.loc; } token_finish(&tok); break; default: synassert(trailing && i->ns, &tok, T_NAME, T_EOF); unlex(lexer, &tok); struct identifier *ns = i->ns; *i = *ns; free(ns); found_trailing = true; continue; } struct identifier *ns; switch (lex(lexer, &tok)) { case T_DOUBLE_COLON: len++; ns = xcalloc(1, sizeof(struct identifier)); *ns = *i; i->ns = ns; i->name = NULL; break; default: unlex(lexer, &tok); break; } } if (len > IDENT_MAX) { error(loc, "identifier exceeds maximum length"); } return found_trailing; } static void parse_import_members(struct lexer *lexer, struct ast_import_members **members) { struct token tok = {0}; while (true) { *members = xcalloc(1, sizeof(struct ast_import_members)); want(lexer, T_NAME, &tok); (*members)->loc = tok.loc; (*members)->name = tok.name; members = &(*members)->next; switch (lex(lexer, &tok)) { case T_COMMA: break; case T_RBRACE: return; default: synerr(&tok, T_COMMA, T_RBRACE, T_EOF); } switch (lex(lexer, &tok)) { case T_NAME: unlex(lexer, &tok); break; case T_RBRACE: return; default: synerr(&tok, T_NAME, T_RBRACE, T_EOF); } } } static void parse_import(struct lexer *lexer, struct ast_imports *import) { struct token tok = {0}; import->mode = IMPORT_NORMAL; bool trailing_colon = parse_identifier(lexer, &import->ident, true); if (trailing_colon) { switch (lex(lexer, &tok)) { case T_LBRACE: import->mode = IMPORT_MEMBERS; parse_import_members(lexer, &import->members); break; case T_TIMES: import->mode = IMPORT_WILDCARD; break; default: synerr(&tok, T_LBRACE, T_TIMES, T_EOF); } } else if (import->ident.ns == NULL) { switch (lex(lexer, &tok)) { case T_EQUAL: import->mode = IMPORT_ALIAS; import->alias = import->ident.name; parse_identifier(lexer, &import->ident, false); break; default: unlex(lexer, &tok); break; } } } static void parse_imports(struct lexer *lexer, struct ast_subunit *subunit) { struct token tok = {0}; struct ast_imports **next = &subunit->imports; bool more = true; while (more) { struct ast_imports *imports; switch (lex(lexer, &tok)) { case T_USE: imports = xcalloc(1, sizeof(struct ast_imports)); parse_import(lexer, imports); want(lexer, T_SEMICOLON, NULL); *next = imports; next = &imports->next; break; default: unlex(lexer, &tok); more = false; break; } } } static void parse_parameter_list(struct lexer *lexer, struct ast_function_type *type) { struct token tok = {0}, tok2 = {0}; want(lexer, T_LPAREN, NULL); type->params = mkfuncparams(lexer->loc); struct ast_function_parameters **next = &type->params; for (;;) { switch (lex(lexer, &tok)) { case T_NAME: // Assumes ownership of tok.name. switch (lex(lexer, &tok2)) { case T_COLON: (*next)->name = tok.name; (*next)->type = parse_type(lexer); break; case T_DOUBLE_COLON: (*next)->type = parse_type(lexer); synassert((*next)->type->storage == STORAGE_ALIAS, &tok, T_NAME, T_EOF); struct identifier *ident = xcalloc(1, sizeof(struct identifier)); struct identifier *ns; ident->name = tok.name; for (ns = &(*next)->type->alias; ns->ns; ns = ns->ns); ns->ns = ident; break; default: unlex(lexer, &tok2); (*next)->type = mktype(tok.loc); (*next)->type->storage = STORAGE_ALIAS; (*next)->type->alias.name = tok.name; break; } break; case T_ELLIPSIS: free(*next); *next = NULL; type->variadism = VARIADISM_C; want(lexer, T_RPAREN, NULL); return; case T_RPAREN: free(*next); *next = NULL; return; default: unlex(lexer, &tok); (*next)->type = parse_type(lexer); break; } switch (lex(lexer, &tok)) { case T_EQUAL: (*next)->default_value = parse_expression(lexer); break; default: unlex(lexer, &tok); break; } switch (lex(lexer, &tok)) { case T_COMMA: (*next)->next = mkfuncparams(lexer->loc); next = &(*next)->next; break; case T_ELLIPSIS: type->variadism = VARIADISM_HARE; want(lexer, T_RPAREN, NULL); return; case T_RPAREN: return; default: synerr(&tok, T_COMMA, T_ELLIPSIS, T_RPAREN, T_EQUAL, T_EOF); } } } static void parse_prototype(struct lexer *lexer, struct ast_function_type *type) { parse_parameter_list(lexer, type); type->result = parse_type(lexer); } static enum type_storage parse_integer_type(struct lexer *lexer) { struct token tok = {0}; switch (lex(lexer, &tok)) { case T_I8: return STORAGE_I8; case T_I16: return STORAGE_I16; case T_I32: return STORAGE_I32; case T_I64: return STORAGE_I64; case T_U8: return STORAGE_U8; case T_U16: return STORAGE_U16; case T_U32: return STORAGE_U32; case T_U64: return STORAGE_U64; case T_INT: return STORAGE_INT; case T_UINT: return STORAGE_UINT; case T_SIZE: return STORAGE_SIZE; case T_UINTPTR: return STORAGE_UINTPTR; default: assert(0); } } static struct ast_type * parse_primitive_type(struct lexer *lexer) { struct token tok = {0}; struct ast_type *type = mktype(lexer->loc); switch (lex(lexer, &tok)) { case T_I8: case T_I16: case T_I32: case T_I64: case T_U8: case T_U16: case T_U32: case T_U64: case T_INT: case T_UINT: case T_SIZE: case T_UINTPTR: unlex(lexer, &tok); type->storage = parse_integer_type(lexer); break; case T_RUNE: type->storage = STORAGE_RUNE; break; case T_STR: type->storage = STORAGE_STRING; break; case T_F32: type->storage = STORAGE_F32; break; case T_F64: type->storage = STORAGE_F64; break; case T_BOOL: type->storage = STORAGE_BOOL; break; case T_VOID: type->storage = STORAGE_VOID; break; case T_DONE: type->storage = STORAGE_DONE; break; case T_OPAQUE: type->storage = STORAGE_OPAQUE; break; case T_NEVER: type->storage = STORAGE_NEVER; break; case T_VALIST: type->storage = STORAGE_VALIST; break; default: assert(0); } return type; } static void parse_binding_unpack(struct lexer *lexer, struct ast_binding_unpack **next); static struct ast_expression *parse_binding_list( struct lexer *lexer, bool is_static); static struct ast_expression *parse_object_selector(struct lexer *lexer); static struct ast_type * parse_enum_type(struct identifier *ident, struct lexer *lexer) { struct token tok = {0}; struct ast_type *type = mktype(lexer->loc); type->storage = STORAGE_ENUM; identifier_dup(&type->alias, ident); struct ast_enum_field **next = &type->_enum.values; switch (lex(lexer, &tok)) { case T_LBRACE: type->_enum.storage = STORAGE_INT; unlex(lexer, &tok); break; case T_I8: case T_I16: case T_I32: case T_I64: case T_U8: case T_U16: case T_U32: case T_U64: case T_INT: case T_UINT: case T_SIZE: case T_UINTPTR: unlex(lexer, &tok); type->_enum.storage = parse_integer_type(lexer); break; case T_RUNE: type->_enum.storage = STORAGE_RUNE; break; default: synassert_msg(false, "Enum storage must be an integer or rune", &tok); } want(lexer, T_LBRACE, NULL); while (tok.token != T_RBRACE) { *next = xcalloc(1, sizeof(struct ast_enum_field)); want(lexer, T_NAME, &tok); (*next)->name = tok.name; (*next)->loc = tok.loc; if (lex(lexer, &tok) == T_EQUAL) { (*next)->value = parse_expression(lexer); } else { unlex(lexer, &tok); } next = &(*next)->next; switch (lex(lexer, &tok)) { case T_COMMA: if (lex(lexer, &tok) != T_RBRACE) { unlex(lexer, &tok); } break; case T_RBRACE: break; default: synerr(&tok, T_COMMA, T_RBRACE, T_EOF); } } return type; } static struct ast_type * parse_struct_union_type(struct lexer *lexer) { struct token tok = {0}; struct ast_type *type = mktype(lexer->loc); struct ast_struct_union_field *next = &type->struct_union.fields; switch (lex(lexer, &tok)) { case T_STRUCT: type->storage = STORAGE_STRUCT; break; case T_UNION: type->storage = STORAGE_UNION; break; default: synerr(&tok, T_STRUCT, T_UNION, T_EOF); break; } if (lex(lexer, &tok) == T_ATTR_PACKED) { type->struct_union.packed = true; } else { unlex(lexer, &tok); } want(lexer, T_LBRACE, NULL); while (tok.token != T_RBRACE) { if (lex(lexer, &tok) == T_ATTR_OFFSET) { want(lexer, T_LPAREN, NULL); next->offset = parse_expression(lexer); want(lexer, T_RPAREN, NULL); } else { unlex(lexer, &tok); } char *name; switch (lex(lexer, &tok)) { case T_NAME: name = tok.name; struct location loc = tok.loc; switch (lex(lexer, &tok)) { case T_COLON: next->name = name; next->type = parse_type(lexer); break; case T_DOUBLE_COLON: next->type = mktype(loc); next->type->storage = STORAGE_ALIAS; next->type->unwrap = false; parse_identifier(lexer, &next->type->alias, false); struct identifier *i = &next->type->alias; while (i->ns != NULL) { i = i->ns; } i->ns = xcalloc(1, sizeof(struct identifier)); i->ns->name = name; break; default: unlex(lexer, &tok); next->type = mktype(loc); next->type->storage = STORAGE_ALIAS; next->type->alias.name = name; next->type->unwrap = false; break; } break; case T_STRUCT: case T_UNION: unlex(lexer, &tok); next->name = NULL; next->type = parse_type(lexer); break; default: synerr(&tok, T_NAME, T_STRUCT, T_UNION, T_EOF); } switch (lex(lexer, &tok)) { case T_COMMA: if (lex(lexer, &tok) != T_RBRACE) { unlex(lexer, &tok); next->next = xcalloc(1, sizeof(struct ast_struct_union_field)); next = next->next; } break; case T_RBRACE: break; default: synerr(&tok, T_COMMA, T_RBRACE, T_EOF); } } return type; } static struct ast_type * parse_tagged_type(struct lexer *lexer, struct ast_type *first) { struct ast_type *type = mktype(first->loc); type->storage = STORAGE_TAGGED; struct ast_tagged_union_type *next = &type->tagged; next->type = first; struct token tok = {0}; while (tok.token != T_RPAREN) { next->next = xcalloc(1, sizeof(struct ast_tagged_union_type)); next = next->next; next->type = parse_type(lexer); switch (lex(lexer, &tok)) { case T_BOR: if (lex(lexer, &tok) != T_RPAREN) { unlex(lexer, &tok); } break; case T_RPAREN: break; default: synerr(&tok, T_BOR, T_RPAREN, T_EOF); } } return type; } static struct ast_type * parse_tuple_type(struct lexer *lexer, struct ast_type *first) { struct ast_type *type = mktype(first->loc); type->storage = STORAGE_TUPLE; struct ast_tuple_type *next = &type->tuple; next->type = first; struct token tok = {0}; while (tok.token != T_RPAREN) { next->next = xcalloc(1, sizeof(struct ast_tuple_type)); next = next->next; next->type = parse_type(lexer); switch (lex(lexer, &tok)) { case T_COMMA: if (lex(lexer, &tok) != T_RPAREN) { unlex(lexer, &tok); } break; case T_RPAREN: break; default: synerr(&tok, T_COMMA, T_RPAREN, T_EOF); } } return type; } static struct ast_type * parse_tagged_or_tuple_type(struct lexer *lexer) { struct ast_type *type = parse_type(lexer); struct token tok = {0}; switch (lex(lexer, &tok)) { case T_BOR: return parse_tagged_type(lexer, type); case T_COMMA: return parse_tuple_type(lexer, type); default: synerr(&tok, T_BOR, T_COMMA, T_EOF); } assert(0); // Unreachable } struct ast_type * parse_type(struct lexer *lexer) { struct token tok = {0}; uint32_t flags = 0; switch (lex(lexer, &tok)) { case T_CONST: flags |= TYPE_CONST; break; default: unlex(lexer, &tok); break; } switch (lex(lexer, &tok)) { case T_LNOT: flags |= TYPE_ERROR; break; default: unlex(lexer, &tok); break; } struct ast_type *type = NULL; bool nullable = false, unwrap = false; switch (lex(lexer, &tok)) { case T_BOOL: case T_F32: case T_F64: case T_I16: case T_I32: case T_I64: case T_I8: case T_INT: case T_NEVER: case T_OPAQUE: case T_RUNE: case T_SIZE: case T_STR: case T_U16: case T_U32: case T_U64: case T_U8: case T_UINT: case T_UINTPTR: case T_VALIST: case T_VOID: case T_DONE: unlex(lexer, &tok); type = parse_primitive_type(lexer); break; case T_NULLABLE: nullable = true; want(lexer, T_TIMES, NULL); /* fallthrough */ case T_TIMES: type = mktype(lexer->loc); type->storage = STORAGE_POINTER; type->pointer.referent = parse_type(lexer); if (nullable) { type->pointer.flags |= PTR_NULLABLE; } break; case T_STRUCT: case T_UNION: unlex(lexer, &tok); type = parse_struct_union_type(lexer); break; case T_LPAREN: type = parse_tagged_or_tuple_type(lexer); break; case T_LBRACKET: type = mktype(lexer->loc); switch (lex(lexer, &tok)) { case T_RBRACKET: type->storage = STORAGE_SLICE; type->slice.members = parse_type(lexer); break; case T_TIMES: type->storage = STORAGE_ARRAY; type->array.length = NULL; want(lexer, T_RBRACKET, NULL); type->array.members = parse_type(lexer); break; case T_UNDERSCORE: type->storage = STORAGE_ARRAY; type->array.length = NULL; type->array.contextual = true; want(lexer, T_RBRACKET, NULL); type->array.members = parse_type(lexer); break; default: type->storage = STORAGE_ARRAY; unlex(lexer, &tok); type->array.length = parse_expression(lexer); want(lexer, T_RBRACKET, NULL); type->array.members = parse_type(lexer); break; } break; case T_FN: type = mktype(lexer->loc); type->storage = STORAGE_FUNCTION; parse_prototype(lexer, &type->func); break; case T_ELLIPSIS: unwrap = true; want(lexer, T_NAME, &tok); // Fallthrough case T_NAME: unlex(lexer, &tok); type = mktype(lexer->loc); type->storage = STORAGE_ALIAS; type->unwrap = unwrap; parse_identifier(lexer, &type->alias, false); break; default: synassert_msg(false, "expected type", &tok); break; } type->flags |= flags; return type; } static struct ast_expression * parse_access(struct lexer *lexer, struct identifier ident) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_ACCESS; exp->access.type = ACCESS_IDENTIFIER; exp->access.ident = ident; return exp; } static struct ast_expression * parse_literal(struct lexer *lexer) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_LITERAL; struct token tok = {0}; switch (lex(lexer, &tok)) { case T_TRUE: exp->literal.storage = STORAGE_BOOL; exp->literal.bval = true; return exp; case T_FALSE: exp->literal.storage = STORAGE_BOOL; exp->literal.bval = false; return exp; case T_NULL: exp->literal.storage = STORAGE_NULL; return exp; case T_VOID: exp->literal.storage = STORAGE_VOID; return exp; case T_DONE: exp->literal.storage = STORAGE_DONE; return exp; case T_NUMBER: exp->literal.storage = tok.storage; break; default: synerr(&tok, T_NUMBER, T_TRUE, T_FALSE, T_NULL, T_VOID, T_EOF); break; } struct location loc = tok.loc; switch (tok.storage) { case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_SIZE: exp->literal.uval = (uint64_t)tok.uval; break; case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_ICONST: case STORAGE_INT: exp->literal.ival = (int64_t)tok.ival; break; case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: exp->literal.fval = tok.fval; break; case STORAGE_RCONST: exp->literal.rune = tok.rune; break; case STORAGE_STRING: exp->literal.string.len = tok.string.len; exp->literal.string.value = tok.string.value; while (lex(lexer, &tok) == T_NUMBER && tok.storage == STORAGE_STRING) { size_t len = exp->literal.string.len; exp->literal.string.value = xrealloc( exp->literal.string.value, len + tok.string.len); memcpy(exp->literal.string.value + len, tok.string.value, tok.string.len); exp->literal.string.len += tok.string.len; } unlex(lexer, &tok); // check for invalid UTF-8 (possible when \x is used) const char *s = exp->literal.string.value; size_t len = exp->literal.string.len; while (s - exp->literal.string.value < (ptrdiff_t)len) { if (utf8_decode(&s) == UTF8_INVALID) { error(loc, "invalid UTF-8 in string literal"); } } break; case STORAGE_BOOL: case STORAGE_NULL: case STORAGE_VOID: case STORAGE_DONE: assert(0); // Handled above case STORAGE_ALIAS: case STORAGE_ARRAY: case STORAGE_ENUM: case STORAGE_FUNCTION: case STORAGE_POINTER: case STORAGE_RUNE: case STORAGE_SLICE: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: assert(0); // Handled in a different nonterminal case STORAGE_ERROR: case STORAGE_NEVER: case STORAGE_OPAQUE: assert(0); // Invariant } return exp; } static struct ast_expression * parse_array_literal(struct lexer *lexer) { struct token tok; want(lexer, T_LBRACKET, &tok); struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_LITERAL; exp->literal.storage = STORAGE_ARRAY; struct ast_expression_list *item, **next = &exp->literal.array.exprs; while (lex(lexer, &tok) != T_RBRACKET) { unlex(lexer, &tok); item = *next = xcalloc(1, sizeof(struct ast_expression_list)); item->expr = parse_expression(lexer); next = &item->next; switch (lex(lexer, &tok)) { case T_ELLIPSIS: exp->literal.array.expand = true; want(lexer, T_RBRACKET, &tok); // fallthrough case T_RBRACKET: unlex(lexer, &tok); break; case T_COMMA: // Move on break; default: synerr(&tok, T_ELLIPSIS, T_COMMA, T_RBRACKET, T_EOF); } } return exp; } static struct ast_expression *parse_struct_literal(struct lexer *lexer, struct identifier ident); static struct ast_field_value * parse_field_value(struct lexer *lexer) { struct ast_field_value *exp = xcalloc(1, sizeof(struct ast_field_value)); char *name; struct token tok = {0}; struct identifier ident = {0}; struct identifier *i; switch (lex(lexer, &tok)) { case T_NAME: name = tok.name; switch (lex(lexer, &tok)) { case T_COLON: exp->name = name; exp->type = parse_type(lexer); want(lexer, T_EQUAL, NULL); exp->initializer = parse_expression(lexer); break; case T_EQUAL: exp->name = name; exp->initializer = parse_expression(lexer); break; case T_DOUBLE_COLON: i = &ident; parse_identifier(lexer, i, false); while (i->ns != NULL) { i = i->ns; } i->ns = xcalloc(1, sizeof(struct identifier)); i->ns->name = name; exp->initializer = parse_struct_literal(lexer, ident); break; default: unlex(lexer, &tok); ident.name = name; ident.ns = NULL; exp->initializer = parse_struct_literal(lexer, ident); break; } break; case T_STRUCT: exp->initializer = parse_struct_literal(lexer, ident); break; default: assert(0); } return exp; } static struct ast_expression * parse_struct_literal(struct lexer *lexer, struct identifier ident) { want(lexer, T_LBRACE, NULL); struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_STRUCT; exp->_struct.type = ident; struct ast_field_value **next = &exp->_struct.fields; struct token tok = {0}; while (tok.token != T_RBRACE) { switch (lex(lexer, &tok)) { case T_ELLIPSIS: synassert(ident.name != NULL, &tok, T_RBRACE, T_EOF); exp->_struct.autofill = true; want(lexer, T_RBRACE, &tok); unlex(lexer, &tok); break; case T_NAME: case T_STRUCT: unlex(lexer, &tok); *next = parse_field_value(lexer); next = &(*next)->next; break; default: synerr(&tok, T_ELLIPSIS, T_NAME, T_RBRACE, T_STRUCT, T_EOF); break; } switch (lex(lexer, &tok)) { case T_COMMA: if (lex(lexer, &tok) != T_RBRACE) { unlex(lexer, &tok); } break; case T_RBRACE: break; default: synerr(&tok, T_COMMA, T_RBRACE, T_EOF); } } return exp; } static struct ast_expression * parse_tuple_expression(struct lexer *lexer, struct ast_expression *first) { struct ast_expression *exp = mkexpr(first->loc); exp->type = EXPR_TUPLE; bool more = true; struct token tok = {0}; struct ast_expression_tuple *tuple = &exp->tuple; tuple->expr = first; tuple->next = xcalloc(1, sizeof(struct ast_expression_tuple)); tuple = tuple->next; while (more) { tuple->expr = parse_expression(lexer); switch (lex(lexer, &tok)) { case T_RPAREN: more = false; break; case T_COMMA: if (lex(lexer, &tok) == T_RPAREN) { more = false; } else { unlex(lexer, &tok); tuple->next = xcalloc(1, sizeof(struct ast_expression_tuple)); tuple = tuple->next; } break; default: synerr(&tok, T_RPAREN, T_COMMA, T_EOF); } } return exp; } static struct ast_expression * parse_plain_expression(struct lexer *lexer) { struct token tok = {0}; struct ast_expression *exp; switch (lex(lexer, &tok)) { // plain-expression case T_NUMBER: case T_TRUE: case T_FALSE: case T_NULL: case T_VOID: case T_DONE: unlex(lexer, &tok); return parse_literal(lexer); case T_NAME: unlex(lexer, &tok); struct identifier ident = {0}; parse_identifier(lexer, &ident, false); switch (lex(lexer, &tok)) { case T_LBRACE: unlex(lexer, &tok); return parse_struct_literal(lexer, ident); default: unlex(lexer, &tok); return parse_access(lexer, ident); } assert(0); case T_LBRACKET: unlex(lexer, &tok); return parse_array_literal(lexer); case T_STRUCT: ident.name = NULL; ident.ns = NULL; return parse_struct_literal(lexer, ident); // nested-expression case T_LPAREN: exp = parse_expression(lexer); switch (lex(lexer, &tok)) { case T_RPAREN: return exp; case T_COMMA: return parse_tuple_expression(lexer, exp); default: synerr(&tok, T_RPAREN, T_COMMA, T_EOF); } assert(0); // Unreachable // empty block case T_RBRACE: error(tok.loc, "syntax error: cannot have empty block"); default: synerr(&tok, T_NUMBER, T_NAME, T_LBRACKET, T_STRUCT, T_LPAREN, T_EOF); } assert(0); // Unreachable } static void parse_assertion(struct lexer *lexer, bool is_static, struct ast_expression_assert *exp) { exp->is_static = is_static; struct token tok; switch (lex(lexer, &tok)) { case T_ASSERT: case T_ABORT: break; default: synerr(&tok, T_ASSERT, T_ABORT, T_EOF); } switch (tok.token) { case T_ASSERT: want(lexer, T_LPAREN, &tok); exp->cond = parse_expression(lexer); if (lex(lexer, &tok) == T_COMMA) { exp->message = parse_expression(lexer); } else { unlex(lexer, &tok); } want(lexer, T_RPAREN, &tok); break; case T_ABORT: want(lexer, T_LPAREN, &tok); if (lex(lexer, &tok) != T_RPAREN) { unlex(lexer, &tok); exp->message = parse_expression(lexer); want(lexer, T_RPAREN, &tok); } break; default: assert(0); // Invariant } } static struct ast_expression * parse_assertion_expression(struct lexer *lexer, bool is_static) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_ASSERT; parse_assertion(lexer, is_static, &exp->assert); return exp; } static struct ast_expression * parse_measurement_expression(struct lexer *lexer) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_MEASURE; struct token tok; lex(lexer, &tok); want(lexer, T_LPAREN, NULL); switch (tok.token) { case T_ALIGN: exp->measure.op = M_ALIGN; exp->measure.type = parse_type(lexer); break; case T_SIZE: exp->measure.op = M_SIZE; exp->measure.type = parse_type(lexer); break; case T_LEN: exp->measure.op = M_LEN; exp->measure.value = parse_expression(lexer); break; case T_OFFSET: exp->measure.op = M_OFFSET; // Let check error out on non-field-accesses exp->measure.value = parse_expression(lexer); break; default: synerr(&tok, T_SIZE, T_LEN, T_OFFSET, T_EOF); } want(lexer, T_RPAREN, NULL); return exp; } static struct ast_expression * parse_call_expression(struct lexer *lexer, struct ast_expression *lvalue) { struct token tok; want(lexer, T_LPAREN, &tok); struct ast_expression *expr = mkexpr(lexer->loc); expr->type = EXPR_CALL; expr->call.lvalue = lvalue; struct ast_expression_list *arg, **next = &expr->call.args; while (lex(lexer, &tok) != T_RPAREN) { unlex(lexer, &tok); arg = *next = xcalloc(1, sizeof(struct ast_expression_list)); arg->expr = parse_expression(lexer); switch (lex(lexer, &tok)) { case T_COMMA: break; case T_ELLIPSIS: expr->call.variadic = true; want(lexer, T_RPAREN, &tok); // fallthrough case T_RPAREN: unlex(lexer, &tok); break; default: synerr(&tok, T_COMMA, T_RPAREN, T_ELLIPSIS, T_EOF); } next = &arg->next; } return expr; } static struct ast_expression * parse_index_slice_expression(struct lexer *lexer, struct ast_expression *lvalue) { struct ast_expression *exp = mkexpr(lexer->loc); struct ast_expression *start = NULL, *end = NULL; struct token tok; want(lexer, T_LBRACKET, &tok); bool is_slice = false; switch (lex(lexer, &tok)) { case T_DOUBLE_DOT: is_slice = true; break; default: unlex(lexer, &tok); start = parse_expression(lexer); break; } switch (lex(lexer, &tok)) { case T_DOUBLE_DOT: is_slice = true; break; case T_RBRACKET: break; default: if (is_slice) { unlex(lexer, &tok); break; } synerr(&tok, T_DOUBLE_DOT, T_RBRACKET, T_EOF); break; } if (!is_slice) { exp->type = EXPR_ACCESS; exp->access.type = ACCESS_INDEX; exp->access.array = lvalue; exp->access.index = start; return exp; } else if (tok.token == T_RBRACKET) { unlex(lexer, &tok); } switch (lex(lexer, &tok)) { case T_RBRACKET: break; default: unlex(lexer, &tok); end = parse_expression(lexer); want(lexer, T_RBRACKET, &tok); break; } exp->type = EXPR_SLICE; exp->slice.object = lvalue; exp->slice.start = start; exp->slice.end = end; return exp; } static struct ast_expression * parse_allocation_expression(struct lexer *lexer) { struct ast_expression *exp = NULL; struct token tok = {0}; switch (lex(lexer, &tok)) { case T_ALLOC: exp = mkexpr(tok.loc); exp->type = EXPR_ALLOC; exp->alloc.kind = ALLOC_OBJECT; want(lexer, T_LPAREN, NULL); exp->alloc.init = parse_expression(lexer); switch (lex(lexer, &tok)) { case T_COMMA: // alloc(init, cap) exp->alloc.cap = parse_expression(lexer); exp->alloc.kind = ALLOC_CAP; want(lexer, T_RPAREN, NULL); break; case T_ELLIPSIS: // alloc(init...) exp->alloc.kind = ALLOC_COPY; want(lexer, T_RPAREN, NULL); break; case T_RPAREN: // alloc(init) break; default: synerr(&tok, T_COMMA, T_RPAREN, T_ELLIPSIS, T_EOF); } break; case T_FREE: exp = mkexpr(tok.loc); exp->type = EXPR_FREE; want(lexer, T_LPAREN, NULL); exp->free.expr = parse_expression(lexer); want(lexer, T_RPAREN, NULL); break; default: assert(0); } return exp; } static struct ast_expression * parse_append_insert(struct lexer *lexer, struct location loc, bool is_static, enum expr_type etype) { struct token tok = {0}; struct ast_expression *expr = mkexpr(loc); expr->type = etype; want(lexer, T_LPAREN, NULL); expr->append.object = parse_object_selector(lexer); if (etype == EXPR_INSERT) { synassert_msg(expr->append.object->access.type == ACCESS_INDEX, "expected indexing expression", &tok); } want(lexer, T_COMMA, NULL); expr->append.value = parse_expression(lexer); expr->append.is_static = is_static; switch (lex(lexer, &tok)) { case T_RPAREN: // This space deliberately left blank break; case T_ELLIPSIS: expr->append.is_multi = true; want(lexer, T_RPAREN, NULL); break; case T_COMMA: expr->append.length = parse_expression(lexer); want(lexer, T_RPAREN, NULL); break; default: synerr(&tok, T_RPAREN, T_ELLIPSIS, T_COMMA, T_EOF); } return expr; } static struct ast_expression * parse_delete(struct lexer *lexer, struct location loc, bool is_static) { struct ast_expression *exp = mkexpr(loc); exp->type = EXPR_DELETE; want(lexer, T_LPAREN, NULL); exp->delete.expr = parse_expression(lexer); exp->delete.is_static = is_static; want(lexer, T_RPAREN, NULL); return exp; } static struct ast_expression * parse_slice_mutation(struct lexer *lexer, bool is_static) { struct token tok = {0}; switch (lex(lexer, &tok)) { case T_APPEND: case T_INSERT: return parse_append_insert(lexer, tok.loc, is_static, tok.token == T_APPEND ? EXPR_APPEND : EXPR_INSERT); case T_DELETE: return parse_delete(lexer, tok.loc, is_static); default: abort(); // Invariant } } static struct ast_expression * parse_static_expression(struct lexer *lexer, bool allowbinding) { struct token tok = {0}; switch (lex(lexer, &tok)) { case T_LET: case T_CONST: synassert(allowbinding, &tok, T_ABORT, T_ASSERT, T_APPEND, T_INSERT, T_DELETE, T_EOF); unlex(lexer, &tok); return parse_binding_list(lexer, true); case T_ABORT: case T_ASSERT: unlex(lexer, &tok); return parse_assertion_expression(lexer, true); case T_APPEND: case T_INSERT: case T_DELETE: unlex(lexer, &tok); return parse_slice_mutation(lexer, true); default: if (allowbinding) { synerr(&tok, T_LET, T_CONST, T_ABORT, T_ASSERT, T_APPEND, T_INSERT, T_DELETE, T_EOF); } else { synerr(&tok, T_ABORT, T_ASSERT, T_APPEND, T_INSERT, T_DELETE, T_EOF); } } assert(0); // Unreachable } static struct ast_expression * parse_postfix_expression(struct lexer *lexer, struct ast_expression *lvalue) { if (lvalue == NULL) { lvalue = parse_plain_expression(lexer); } struct token tok; struct ast_expression *exp; switch (lex(lexer, &tok)) { case T_LPAREN: unlex(lexer, &tok); lvalue = parse_call_expression(lexer, lvalue); break; case T_DOT: exp = mkexpr(lexer->loc); exp->type = EXPR_ACCESS; switch (lex(lexer, &tok)) { case T_NAME: exp->access.type = ACCESS_FIELD; exp->access._struct = lvalue; exp->access.field = tok.name; break; case T_NUMBER: exp->access.type = ACCESS_TUPLE; exp->access.tuple = lvalue; unlex(lexer, &tok); exp->access.value = parse_literal(lexer); break; default: synerr(&tok, T_NAME, T_NUMBER, T_EOF); } lvalue = exp; break; case T_LBRACKET: unlex(lexer, &tok); lvalue = parse_index_slice_expression(lexer, lvalue); break; case T_QUESTION: case T_LNOT: exp = mkexpr(lexer->loc); exp->type = EXPR_PROPAGATE; exp->propagate.value = lvalue; exp->propagate.abort = tok.token == T_LNOT; lvalue = exp; break; default: unlex(lexer, &tok); return lvalue; } return parse_postfix_expression(lexer, lvalue); } static enum unarithm_operator unop_for_token(enum lexical_token tok) { switch (tok) { case T_MINUS: // - return UN_MINUS; case T_BNOT: // ~ return UN_BNOT; case T_LNOT: // ! return UN_LNOT; case T_TIMES: // * return UN_DEREF; case T_BAND: // & return UN_ADDRESS; default: assert(0); // Invariant } assert(0); // Unreachable } static struct ast_expression * parse_object_selector(struct lexer *lexer) { struct token tok; lex(lexer, &tok); unlex(lexer, &tok); struct ast_expression *exp = parse_postfix_expression(lexer, NULL); synassert_msg(exp->type == EXPR_ACCESS, "Expected object selector (identifier, indexing, or field access)", &tok); return exp; } static struct ast_expression * parse_va_expression(struct lexer *lexer) { struct ast_expression *expr; struct token tok; switch (lex(lexer, &tok)) { case T_VASTART: expr = mkexpr(lexer->loc); expr->type = EXPR_VASTART; want(lexer, T_LPAREN, NULL); want(lexer, T_RPAREN, NULL); return expr; case T_VAARG: expr = mkexpr(lexer->loc); expr->type = EXPR_VAARG; want(lexer, T_LPAREN, NULL); expr->vaarg.ap = parse_object_selector(lexer); want(lexer, T_RPAREN, NULL); return expr; case T_VAEND: expr = mkexpr(lexer->loc); expr->type = EXPR_VAEND; want(lexer, T_LPAREN, NULL); expr->vaarg.ap = parse_object_selector(lexer); want(lexer, T_RPAREN, NULL); return expr; default: assert(0); } } static struct ast_expression * parse_builtin_expression(struct lexer *lexer) { struct token tok; switch (lex(lexer, &tok)) { case T_ALLOC: case T_FREE: unlex(lexer, &tok); return parse_allocation_expression(lexer); case T_APPEND: case T_DELETE: case T_INSERT: unlex(lexer, &tok); return parse_slice_mutation(lexer, false); case T_STATIC: return parse_static_expression(lexer, false); case T_ABORT: case T_ASSERT: unlex(lexer, &tok); return parse_assertion_expression(lexer, false); case T_ALIGN: case T_SIZE: case T_LEN: case T_OFFSET: unlex(lexer, &tok); return parse_measurement_expression(lexer); case T_VAARG: case T_VAEND: case T_VASTART: unlex(lexer, &tok); return parse_va_expression(lexer); default: unlex(lexer, &tok); break; } return parse_postfix_expression(lexer, NULL); } static struct ast_expression *parse_compound_expression(struct lexer *lexer); static struct ast_expression *parse_match_expression(struct lexer *lexer); static struct ast_expression *parse_switch_expression(struct lexer *lexer); static struct ast_expression * parse_unary_expression(struct lexer *lexer) { struct token tok; struct ast_expression *exp; switch (lex(lexer, &tok)) { case T_MINUS: // - case T_BNOT: // ~ case T_LNOT: // ! case T_TIMES: // * case T_BAND: // & exp = mkexpr(lexer->loc); exp->type = EXPR_UNARITHM; exp->unarithm.op = unop_for_token(tok.token); exp->unarithm.operand = parse_unary_expression(lexer); return exp; case T_COLON: case T_LBRACE: unlex(lexer, &tok); return parse_compound_expression(lexer); case T_MATCH: return parse_match_expression(lexer); case T_SWITCH: return parse_switch_expression(lexer); default: unlex(lexer, &tok); return parse_builtin_expression(lexer); } } static struct ast_expression * parse_cast_expression(struct lexer *lexer, struct ast_expression *value) { if (value == NULL) { value = parse_unary_expression(lexer); } enum cast_kind kind; struct token tok; switch (lex(lexer, &tok)) { case T_COLON: kind = C_CAST; break; case T_AS: kind = C_ASSERTION; break; case T_IS: kind = C_TEST; break; default: unlex(lexer, &tok); return value; } struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_CAST; exp->cast.kind = kind; exp->cast.value = value; if (kind == C_CAST) { exp->cast.type = parse_type(lexer); } else { if (lex(lexer, &tok) == T_NULL) { exp->cast.type = mktype(tok.loc); exp->cast.type->storage = STORAGE_NULL; } else { unlex(lexer, &tok); exp->cast.type = parse_type(lexer); } } return parse_cast_expression(lexer, exp); } static int precedence(enum lexical_token token) { switch (token) { case T_LOR: return 0; case T_LXOR: return 1; case T_LAND: return 2; case T_LEQUAL: case T_NEQUAL: return 3; case T_LESS: case T_LESSEQ: case T_GREATER: case T_GREATEREQ: return 4; case T_BOR: return 5; case T_BXOR: return 6; case T_BAND: return 7; case T_LSHIFT: case T_RSHIFT: return 8; case T_PLUS: case T_MINUS: return 9; case T_TIMES: case T_DIV: case T_MODULO: return 10; default: return -1; } assert(0); // Unreachable } static enum binarithm_operator binop_for_token(enum lexical_token tok) { switch (tok) { case T_LOR: return BIN_LOR; case T_LAND: return BIN_LAND; case T_LXOR: return BIN_LXOR; case T_BOR: return BIN_BOR; case T_BXOR: return BIN_BXOR; case T_BAND: return BIN_BAND; case T_LEQUAL: return BIN_LEQUAL; case T_NEQUAL: return BIN_NEQUAL; case T_LESS: return BIN_LESS; case T_LESSEQ: return BIN_LESSEQ; case T_GREATER: return BIN_GREATER; case T_GREATEREQ: return BIN_GREATEREQ; case T_LSHIFT: return BIN_LSHIFT; case T_RSHIFT: return BIN_RSHIFT; case T_PLUS: return BIN_PLUS; case T_MINUS: return BIN_MINUS; case T_TIMES: return BIN_TIMES; case T_DIV: return BIN_DIV; case T_MODULO: return BIN_MODULO; default: assert(0); // Invariant } assert(0); // Unreachable } static struct ast_expression * parse_bin_expression(struct lexer *lexer, struct ast_expression *lvalue, int i) { struct token tok; lex(lexer, &tok); int j; while ((j = precedence(tok.token)) >= i) { enum binarithm_operator op = binop_for_token(tok.token); struct ast_expression *rvalue = parse_cast_expression(lexer, NULL); lex(lexer, &tok); int k; while ((k = precedence(tok.token)) > j) { unlex(lexer, &tok); rvalue = parse_bin_expression(lexer, rvalue, k); lex(lexer, &tok); } struct ast_expression *e = mkexpr(lexer->loc); e->type = EXPR_BINARITHM; e->binarithm.op = op; e->binarithm.lvalue = lvalue; e->binarithm.rvalue = rvalue; lvalue = e; } unlex(lexer, &tok); return lvalue; } static struct ast_expression * parse_if_expression(struct lexer *lexer) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_IF; struct token tok = {0}; want(lexer, T_LPAREN, &tok); exp->_if.cond = parse_expression(lexer); want(lexer, T_RPAREN, &tok); exp->_if.true_branch = parse_expression(lexer); switch (lex(lexer, &tok)) { case T_ELSE: exp->_if.false_branch = parse_expression(lexer); break; default: unlex(lexer, &tok); break; } return exp; } static void parse_for_predicate(struct lexer *lexer, struct ast_expression_for *for_exp) { struct token tok = {0}; unsigned int flags = 0; switch (lex(lexer, &tok)) { case T_CONST: flags = TYPE_CONST; break; case T_LET: break; default: unlex(lexer, &tok); for_exp->kind = FOR_ACCUMULATOR; for_exp->cond = parse_expression(lexer); break; } if (for_exp->cond == NULL) { for_exp->bindings = mkexpr(lexer->loc); for_exp->bindings->type = EXPR_BINDING; struct ast_expression_binding *binding = &for_exp->bindings->binding; bool for_kind_found = false; while (true) { binding->flags = flags; switch (lex(lexer, &tok)) { case T_NAME: binding->name = tok.name; break; case T_LPAREN: parse_binding_unpack(lexer, &binding->unpack); break; default: synerr(&tok, T_NAME, T_LPAREN, T_EOF); } if (lex(lexer, &tok) == T_COLON) { binding->type = parse_type(lexer); binding->type->flags |= flags; } else { unlex(lexer, &tok); } if (for_kind_found) { want(lexer, T_EQUAL, &tok); } else { for_kind_found = true; switch (lex(lexer, &tok)) { case T_DOUBLE_DOT: for_exp->kind = FOR_EACH_VALUE; break; case T_BAND: want(lexer, T_DOUBLE_DOT, &tok); for_exp->kind = FOR_EACH_POINTER; break; case T_ARROW: for_exp->kind = FOR_EACH_ITERATOR; break; case T_EQUAL: for_exp->kind = FOR_ACCUMULATOR; break; default: synerr(&tok, T_DOUBLE_DOT, T_BAND, T_ARROW, T_EQUAL, T_EOF); } } binding->initializer = parse_expression(lexer); if (for_exp->kind != FOR_ACCUMULATOR) { return; } if (lex(lexer, &tok) != T_COMMA) { unlex(lexer, &tok); break; } binding->next = xcalloc(1, sizeof(struct ast_expression_binding)); binding = binding->next; } want(lexer, T_SEMICOLON, &tok); for_exp->cond = parse_expression(lexer); } if (lex(lexer, &tok) != T_SEMICOLON) { unlex(lexer, &tok); return; } for_exp->afterthought = parse_expression(lexer); } static struct ast_expression * parse_for_expression(struct lexer *lexer) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_FOR; struct token tok = {0}; switch (lex(lexer, &tok)) { case T_COLON: want(lexer, T_NAME, &tok); exp->_for.label = tok.name; want(lexer, T_LPAREN, &tok); break; case T_LPAREN: break; default: synerr(&tok, T_LPAREN, T_COLON, T_EOF); break; } parse_for_predicate(lexer, &exp->_for); want(lexer, T_RPAREN, &tok); exp->_for.body = parse_expression(lexer); return exp; } static struct ast_case_option * parse_case_options(struct lexer *lexer) { struct token tok = {0}; switch (lex(lexer, &tok)) { case T_ARROW: return NULL; // Default case default: unlex(lexer, &tok); break; } bool more = true; struct ast_case_option *opt = xcalloc(1, sizeof(struct ast_case_option)); struct ast_case_option *opts = opt; struct ast_case_option **next = &opt->next; while (more) { opt->value = parse_expression(lexer); switch (lex(lexer, &tok)) { case T_COMMA: switch (lex(lexer, &tok)) { case T_ARROW: more = false; break; default: unlex(lexer, &tok); opt = xcalloc(1, sizeof(struct ast_case_option)); *next = opt; next = &opt->next; break; } break; case T_ARROW: more = false; break; default: synerr(&tok, T_COMMA, T_ARROW, T_EOF); break; } } return opts; } static struct ast_expression * parse_switch_expression(struct lexer *lexer) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_SWITCH; struct token tok = {0}; switch (lex(lexer, &tok)) { case T_COLON: want(lexer, T_NAME, &tok); exp->_switch.label = tok.name; want(lexer, T_LPAREN, &tok); break; case T_LPAREN: break; // no-op default: synerr(&tok, T_LPAREN, T_COLON, T_EOF); } exp->_switch.value = parse_expression(lexer); want(lexer, T_RPAREN, &tok); want(lexer, T_LBRACE, &tok); bool more = true; struct ast_switch_case **next_case = &exp->_switch.cases; while (more) { struct ast_switch_case *_case = *next_case = xcalloc(1, sizeof(struct ast_switch_case)); want(lexer, T_CASE, &tok); _case->options = parse_case_options(lexer); bool exprs = true; struct ast_expression_list *cur = &_case->exprs; struct ast_expression_list **next = &cur->next; while (exprs) { cur->expr = parse_statement(lexer); want(lexer, T_SEMICOLON, &tok); switch (lex(lexer, &tok)) { case T_CASE: case T_RBRACE: exprs = false; break; default: break; } unlex(lexer, &tok); if (exprs) { *next = xcalloc(1, sizeof(struct ast_expression_list)); cur = *next; next = &cur->next; } } switch (lex(lexer, &tok)) { case T_CASE: unlex(lexer, &tok); break; case T_RBRACE: more = false; break; default: synerr(&tok, T_CASE, T_RBRACE, T_EOF); } next_case = &_case->next; } return exp; } static struct ast_expression * parse_match_expression(struct lexer *lexer) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_MATCH; struct token tok = {0}; switch (lex(lexer, &tok)) { case T_COLON: want(lexer, T_NAME, &tok); exp->match.label = tok.name; want(lexer, T_LPAREN, &tok); break; case T_LPAREN: break; // no-op default: synerr(&tok, T_LPAREN, T_COLON, T_EOF); } exp->match.value = parse_expression(lexer); want(lexer, T_RPAREN, &tok); want(lexer, T_LBRACE, &tok); bool more = true; struct ast_match_case **next_case = &exp->match.cases; while (more) { struct ast_match_case *_case = *next_case = xcalloc(1, sizeof(struct ast_match_case)); want(lexer, T_CASE, &tok); struct ast_type *type = NULL; switch (lex(lexer, &tok)) { case T_LET: want(lexer, T_NAME, &tok); _case->name = tok.name; want(lexer, T_COLON, NULL); _case->type = parse_type(lexer); break; case T_ARROW: // Default case unlex(lexer, &tok); break; case T_NULL: type = mktype(tok.loc); type->storage = STORAGE_NULL; _case->type = type; break; default: unlex(lexer, &tok); _case->type = parse_type(lexer); break; } want(lexer, T_ARROW, &tok); bool exprs = true; struct ast_expression_list *cur = &_case->exprs; struct ast_expression_list **next = &cur->next; while (exprs) { cur->expr = parse_statement(lexer); want(lexer, T_SEMICOLON, &tok); switch (lex(lexer, &tok)) { case T_CASE: case T_RBRACE: exprs = false; break; default: break; } unlex(lexer, &tok); if (exprs) { *next = xcalloc(1, sizeof(struct ast_expression_list)); cur = *next; next = &cur->next; } } switch (lex(lexer, &tok)) { case T_CASE: unlex(lexer, &tok); break; case T_RBRACE: more = false; break; default: synerr(&tok, T_CASE, T_RBRACE, T_EOF); } next_case = &_case->next; } return exp; } static void parse_binding_unpack(struct lexer *lexer, struct ast_binding_unpack **next) { struct token tok = {0}; bool more = true; while (more) { char *name = NULL; switch (lex(lexer, &tok)) { case T_NAME: name = tok.name; break; case T_UNDERSCORE: break; default: synerr(&tok, T_NAME, T_UNDERSCORE, T_EOF); } struct ast_binding_unpack *new = xcalloc(1, sizeof *new); *next = new; next = &new->next; new->name = name; switch (lex(lexer, &tok)) { case T_COMMA: break; case T_RPAREN: more = false; break; default: synerr(&tok, T_COMMA, T_RPAREN, T_EOF); } } } static struct ast_expression * parse_binding_list(struct lexer *lexer, bool is_static) { struct ast_expression *exp = mkexpr(lexer->loc); unsigned int flags = 0; struct token tok = {0}; switch (lex(lexer, &tok)) { case T_DEF: exp->type = EXPR_DEFINE; break; case T_CONST: flags = TYPE_CONST; // fallthrough case T_LET: exp->type = EXPR_BINDING; break; default: synerr(&tok, T_LET, T_CONST, T_DEF, T_EOF); } struct ast_expression_binding *binding = &exp->binding; struct ast_expression_binding **next = &exp->binding.next; bool more = true; while (more) { switch (lex(lexer, &tok)) { case T_NAME: binding->name = tok.name; break; case T_LPAREN: if (exp->type == EXPR_BINDING) { parse_binding_unpack(lexer, &binding->unpack); break; } // fallthrough default: synerr(&tok, T_NAME, T_LPAREN, T_EOF); } binding->initializer = mkexpr(lexer->loc); binding->flags = flags; binding->is_static = is_static; switch (lex(lexer, &tok)) { case T_COLON: binding->type = parse_type(lexer); binding->type->flags |= flags; want(lexer, T_EQUAL, &tok); break; case T_EQUAL: break; default: synerr(&tok, T_COLON, T_EQUAL, T_EOF); } binding->initializer = parse_expression(lexer); switch (lex(lexer, &tok)) { case T_COMMA: *next = xcalloc(1, sizeof(struct ast_expression_binding)); binding = *next; next = &binding->next; break; default: unlex(lexer, &tok); more = false; break; } } return exp; } static struct ast_expression * parse_assignment(struct lexer *lexer, struct ast_expression *object, enum binarithm_operator op) { struct ast_expression *value = parse_expression(lexer); struct ast_expression *expr = mkexpr(lexer->loc); expr->type = EXPR_ASSIGN; expr->assign.op = op; expr->assign.object = object; expr->assign.value = value; return expr; } static struct ast_expression * parse_deferred_expression(struct lexer *lexer) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_DEFER; exp->defer.deferred = parse_expression(lexer); return exp; } static struct ast_expression * parse_control_expression(struct lexer *lexer) { struct ast_expression *exp = mkexpr(lexer->loc); struct token tok; switch (lex(lexer, &tok)) { case T_BREAK: case T_CONTINUE: exp->type = tok.token == T_BREAK ? EXPR_BREAK : EXPR_CONTINUE; exp->control.label = NULL; switch (lex(lexer, &tok)) { case T_COLON: want(lexer, T_NAME, &tok); exp->control.label = tok.name; break; default: unlex(lexer, &tok); break; } break; case T_RETURN: exp->type = EXPR_RETURN; exp->_return.value = NULL; switch (lex(lexer, &tok)) { case T_COMMA: case T_ELSE: case T_RBRACE: case T_RBRACKET: case T_RPAREN: case T_SEMICOLON: unlex(lexer, &tok); break; default: unlex(lexer, &tok); exp->_return.value = parse_expression(lexer); break; } break; case T_YIELD: exp->type = EXPR_YIELD; exp->control.value = NULL; switch (lex(lexer, &tok)) { case T_COMMA: case T_ELSE: case T_RBRACE: case T_RBRACKET: case T_RPAREN: case T_SEMICOLON: unlex(lexer, &tok); break; case T_COLON: want(lexer, T_NAME, &tok); exp->control.label = tok.name; switch (lex(lexer, &tok)) { case T_COMMA: exp->control.value = parse_expression(lexer); break; default: unlex(lexer, &tok); break; } break; default: unlex(lexer, &tok); exp->control.value = parse_expression(lexer); break; } break; default: synerr(&tok, T_BREAK, T_CONTINUE, T_RETURN, T_YIELD, T_EOF); } return exp; } static struct ast_expression * parse_compound_expression(struct lexer *lexer) { struct ast_expression *exp = mkexpr(lexer->loc); exp->type = EXPR_COMPOUND; struct ast_expression_list *cur = &exp->compound.list; struct ast_expression_list **next = &cur->next; struct token tok = {0}; switch (lex(lexer, &tok)) { case T_COLON: want(lexer, T_NAME, &tok); exp->compound.label = tok.name; want(lexer, T_LBRACE, &tok); break; case T_LBRACE: break; // no-op default: synerr(&tok, T_LBRACE, T_COLON, T_EOF); break; } while (true) { cur->expr = parse_statement(lexer); want(lexer, T_SEMICOLON, &tok); lex(lexer, &tok); if (tok.token == T_RBRACE) { break; } unlex(lexer, &tok); *next = xcalloc(1, sizeof(struct ast_expression_list)); cur = *next; next = &cur->next; } return exp; } struct ast_expression * parse_expression(struct lexer *lexer) { struct token tok; switch (lex(lexer, &tok)) { case T_STATIC: return parse_static_expression(lexer, false); case T_BREAK: case T_CONTINUE: case T_RETURN: case T_YIELD: unlex(lexer, &tok); return parse_control_expression(lexer); case T_FOR: return parse_for_expression(lexer); case T_IF: return parse_if_expression(lexer); default: break; } unlex(lexer, &tok); struct ast_expression *value = parse_unary_expression(lexer); if (value->type != EXPR_ACCESS && value->type != EXPR_SLICE && (value->type != EXPR_UNARITHM || value->unarithm.op != UN_DEREF)) { value = parse_cast_expression(lexer, value); return parse_bin_expression(lexer, value, 0); } // Is object-selector, try for assignment switch (lex(lexer, &tok)) { case T_EQUAL: return parse_assignment(lexer, value, BIN_LEQUAL); case T_BANDEQ: return parse_assignment(lexer, value, BIN_BAND); case T_LANDEQ: return parse_assignment(lexer, value, BIN_LAND); case T_DIVEQ: return parse_assignment(lexer, value, BIN_DIV); case T_LSHIFTEQ: return parse_assignment(lexer, value, BIN_LSHIFT); case T_MINUSEQ: return parse_assignment(lexer, value, BIN_MINUS); case T_MODEQ: return parse_assignment(lexer, value, BIN_MODULO); case T_BOREQ: return parse_assignment(lexer, value, BIN_BOR); case T_LOREQ: return parse_assignment(lexer, value, BIN_LOR); case T_PLUSEQ: return parse_assignment(lexer, value, BIN_PLUS); case T_RSHIFTEQ: return parse_assignment(lexer, value, BIN_RSHIFT); case T_TIMESEQ: return parse_assignment(lexer, value, BIN_TIMES); case T_BXOREQ: return parse_assignment(lexer, value, BIN_BXOR); case T_LXOREQ: return parse_assignment(lexer, value, BIN_LXOR); default: unlex(lexer, &tok); value = parse_cast_expression(lexer, value); value = parse_bin_expression(lexer, value, 0); return value; } } static struct ast_expression * parse_statement(struct lexer *lexer) { struct token tok; switch (lex(lexer, &tok)) { case T_LET: case T_CONST: case T_DEF: unlex(lexer, &tok); return parse_binding_list(lexer, false); case T_STATIC: return parse_static_expression(lexer, true); case T_DEFER: return parse_deferred_expression(lexer); default: unlex(lexer, &tok); return parse_expression(lexer); } } static char * parse_attr_symbol(struct lexer *lexer) { struct token tok = {0}; want(lexer, T_LPAREN, NULL); want(lexer, T_NUMBER, &tok); synassert_msg(tok.storage == STORAGE_STRING, "expected string literal", &tok); synassert_msg(tok.string.len > 0, "invalid symbol", &tok); for (size_t i = 0; i < tok.string.len; i++) { uint32_t c = tok.string.value[i]; synassert_msg(c <= 0x7F && (isalnum(c) || c == '_' || c == '$' || c == '.'), "invalid symbol", &tok); synassert_msg(i != 0 || (!isdigit(c) && c != '$'), "invalid symbol", &tok); } want(lexer, T_RPAREN, NULL); return tok.string.value; } static void parse_global_decl(struct lexer *lexer, enum lexical_token mode, struct ast_global_decl *decl) { struct token tok = {0}; struct ast_global_decl *i = decl; assert(mode == T_LET || mode == T_CONST || mode == T_DEF); bool more = true; while (more) { if (mode == T_LET || mode == T_CONST) { switch (lex(lexer, &tok)) { case T_ATTR_SYMBOL: i->symbol = parse_attr_symbol(lexer); break; default: unlex(lexer, &tok); break; } switch (lex(lexer, &tok)) { case T_ATTR_THREADLOCAL: i->threadlocal = true; break; default: unlex(lexer, &tok); break; } } parse_identifier(lexer, &i->ident, false); switch (lex(lexer, &tok)) { case T_COLON: i->type = parse_type(lexer); if (mode == T_CONST) { i->type->flags |= TYPE_CONST; } if (lex(lexer, &tok) != T_EQUAL) { synassert(mode != T_DEF, &tok, T_EQUAL, T_EOF); unlex(lexer, &tok); break; } /* fallthrough */ case T_EQUAL: i->init = parse_expression(lexer); break; default: synerr(&tok, T_EQUAL, T_COLON, T_EOF); } switch (lex(lexer, &tok)) { case T_COMMA: lex(lexer, &tok); if (tok.token == T_NAME || tok.token == T_ATTR_SYMBOL) { i->next = xcalloc(1, sizeof(struct ast_global_decl)); i = i->next; unlex(lexer, &tok); break; } /* fallthrough */ default: more = false; unlex(lexer, &tok); break; } } } static void parse_type_decl(struct lexer *lexer, struct ast_type_decl *decl) { struct token tok = {0}; struct ast_type_decl *i = decl; bool more = true; while (more) { parse_identifier(lexer, &i->ident, false); want(lexer, T_EQUAL, NULL); switch (lex(lexer, &tok)) { case T_ENUM: i->type = parse_enum_type(&i->ident, lexer); break; default: unlex(lexer, &tok); i->type = parse_type(lexer); } switch (lex(lexer, &tok)) { case T_COMMA: if (lex(lexer, &tok) == T_NAME) { i->next = xcalloc(1, sizeof(struct ast_type_decl)); i = i->next; unlex(lexer, &tok); break; } /* fallthrough */ default: more = false; unlex(lexer, &tok); break; } } } static void parse_fn_decl(struct lexer *lexer, struct ast_function_decl *decl) { struct token tok = {0}; bool more = true; while (more) { switch (lex(lexer, &tok)) { case T_ATTR_FINI: decl->flags |= FN_FINI; break; case T_ATTR_INIT: decl->flags |= FN_INIT; break; case T_ATTR_SYMBOL: decl->symbol = parse_attr_symbol(lexer); break; case T_ATTR_TEST: decl->flags |= FN_TEST; break; default: more = false; unlex(lexer, &tok); break; } } want(lexer, T_FN, NULL); parse_identifier(lexer, &decl->ident, false); parse_prototype(lexer, &decl->prototype); switch (lex(lexer, &tok)) { case T_EQUAL: decl->body = parse_expression(lexer); break; case T_SEMICOLON: unlex(lexer, &tok); decl->body = NULL; // Prototype break; default: synerr(&tok, T_EQUAL, T_SEMICOLON, T_EOF); } } static void parse_decl(struct lexer *lexer, struct ast_decl *decl) { struct token tok = {0}; decl->loc = lexer->loc; switch (lex(lexer, &tok)) { case T_CONST: case T_LET: decl->decl_type = ADECL_GLOBAL; parse_global_decl(lexer, tok.token, &decl->global); break; case T_DEF: decl->decl_type = ADECL_CONST; parse_global_decl(lexer, tok.token, &decl->constant); break; case T_TYPE: decl->decl_type = ADECL_TYPE; parse_type_decl(lexer, &decl->type); break; default: unlex(lexer, &tok); decl->decl_type = ADECL_FUNC; parse_fn_decl(lexer, &decl->function); break; } } static void parse_decls(struct lexer *lexer, struct ast_decls **decls) { struct token tok = {0}; struct ast_decls **next = decls; while (tok.token != T_EOF) { struct ast_decls *decl = *next = xcalloc(1, sizeof(struct ast_decls)); switch (lex(lexer, &tok)) { case T_EXPORT: decl->decl.exported = true; break; case T_STATIC: decl->decl.decl_type = ADECL_ASSERT; parse_assertion(lexer, true, &decl->decl.assert); next = &decl->next; want(lexer, T_SEMICOLON, NULL); continue; default: unlex(lexer, &tok); break; } if (tok.token == T_EOF) { break; } parse_decl(lexer, &decl->decl); next = &decl->next; want(lexer, T_SEMICOLON, NULL); } free(*next); *next = 0; } void parse(struct lexer *lexer, struct ast_subunit *subunit) { parse_imports(lexer, subunit); parse_decls(lexer, &subunit->decls); want(lexer, T_EOF, NULL); } harec-0.24.2/src/qbe.c000066400000000000000000000123361464473277600144140ustar00rootroot00000000000000#include #include #include #include #include #include "qbe.h" #include "util.h" // Simple type singletons const struct qbe_type qbe_byte = { .stype = Q_BYTE, .size = 1, }, qbe_half = { .stype = Q_HALF, .size = 2, }, qbe_word = { .stype = Q_WORD, .size = 4, }, qbe_long = { .stype = Q_LONG, .size = 8, }, qbe_single = { .stype = Q_SINGLE, .size = 4, }, qbe_double = { .stype = Q_DOUBLE, .size = 8, }, qbe_void = { .stype = Q__VOID, }, qbe_aggregate = { .stype = Q__AGGREGATE, }; const struct qbe_value variadic_sigil = {0}; const char *qbe_instr[Q_LAST_INSTR] = { [Q_ADD] = "add", [Q_ALLOC16] = "alloc16", [Q_ALLOC4] = "alloc4", [Q_ALLOC8] = "alloc8", [Q_AND] = "and", [Q_BLIT] = "blit", [Q_CALL] = "call", [Q_CAST] = "cast", [Q_CEQD] = "ceqd", [Q_CEQL] = "ceql", [Q_CEQS] = "ceqs", [Q_CEQW] = "ceqw", [Q_CGED] = "cged", [Q_CGES] = "cges", [Q_CGTD] = "cgtd", [Q_CGTS] = "cgts", [Q_CLED] = "cled", [Q_CLES] = "cles", [Q_CLTD] = "cltd", [Q_CLTS] = "clts", [Q_CNED] = "cned", [Q_CNEL] = "cnel", [Q_CNES] = "cnes", [Q_CNEW] = "cnew", [Q_COD] = "cod", [Q_COPY] = "copy", [Q_COS] = "cos", [Q_CSGEL] = "csgel", [Q_CSGEW] = "csgew", [Q_CSGTL] = "csgtl", [Q_CSGTW] = "csgtw", [Q_CSLEL] = "cslel", [Q_CSLEW] = "cslew", [Q_CSLTL] = "csltl", [Q_CSLTW] = "csltw", [Q_CUGEL] = "cugel", [Q_CUGEW] = "cugew", [Q_CUGTL] = "cugtl", [Q_CUGTW] = "cugtw", [Q_CULEL] = "culel", [Q_CULEW] = "culew", [Q_CULTL] = "cultl", [Q_CULTW] = "cultw", [Q_CUOD] = "cuod", [Q_CUOS] = "cuos", [Q_DBGLOC] = "dbgloc", [Q_DIV] = "div", [Q_DTOSI] = "dtosi", [Q_DTOUI] = "dtoui", [Q_EXTS] = "exts", [Q_EXTSB] = "extsb", [Q_EXTSH] = "extsh", [Q_EXTSW] = "extsw", [Q_EXTUB] = "extub", [Q_EXTUH] = "extuh", [Q_EXTUW] = "extuw", [Q_HLT] = "hlt", [Q_JMP] = "jmp", [Q_JNZ] = "jnz", [Q_LOADD] = "loadd", [Q_LOADL] = "loadl", [Q_LOADS] = "loads", [Q_LOADSB] = "loadsb", [Q_LOADSH] = "loadsh", [Q_LOADSW] = "loadsw", [Q_LOADUB] = "loadub", [Q_LOADUH] = "loaduh", [Q_LOADUW] = "loaduw", [Q_MUL] = "mul", [Q_NEG] = "neg", [Q_OR] = "or", [Q_REM] = "rem", [Q_RET] = "ret", [Q_SAR] = "sar", [Q_SHL] = "shl", [Q_SHR] = "shr", [Q_SLTOF] = "sltof", [Q_STOREB] = "storeb", [Q_STORED] = "stored", [Q_STOREH] = "storeh", [Q_STOREL] = "storel", [Q_STORES] = "stores", [Q_STOREW] = "storew", [Q_STOSI] = "stosi", [Q_STOUI] = "stoui", [Q_SUB] = "sub", [Q_SWTOF] = "swtof", [Q_TRUNCD] = "truncd", [Q_UDIV] = "udiv", [Q_ULTOF] = "ultof", [Q_UREM] = "urem", [Q_UWTOF] = "uwtof", [Q_VAARG] = "vaarg", [Q_VASTART] = "vastart", [Q_XOR] = "xor", }; void qbe_append_def(struct qbe_program *prog, struct qbe_def *def) { *prog->next = def; prog->next = &def->next; } static struct qbe_value * qval_dup(const struct qbe_value *val) { struct qbe_value *new = xcalloc(1, sizeof(struct qbe_value)); *new = *val; if (val->kind != QV_CONST) { new->name = xstrdup(val->name); } return new; } static void va_geni(struct qbe_statement *stmt, enum qbe_instr instr, const struct qbe_value *out, va_list ap) { stmt->type = Q_INSTR; stmt->instr = instr; if (out) { assert(out->kind == QV_TEMPORARY); stmt->out = qval_dup(out); } struct qbe_arguments **next = &stmt->args; struct qbe_value *val; while ((val = va_arg(ap, struct qbe_value *))) { struct qbe_arguments *arg = xcalloc(1, sizeof(struct qbe_arguments)); arg->value = *val; *next = arg; next = &arg->next; } } void push(struct qbe_statements *stmts, struct qbe_statement *stmt) { if (!stmts->stmts) { stmts->sz = 256; stmts->ln = 0; stmts->stmts = xcalloc(stmts->sz, sizeof(struct qbe_statement)); } if (stmts->ln + 1 >= stmts->sz) { stmts->sz *= 2; stmts->stmts = xrealloc(stmts->stmts, sizeof(struct qbe_statement) * stmts->sz); } stmts->stmts[stmts->ln++] = *stmt; } void pushi(struct qbe_func *func, const struct qbe_value *out, enum qbe_instr instr, ...) { struct qbe_statement stmt = {0}; va_list ap; va_start(ap, instr); struct qbe_value hack; if (out && (out->type->stype == Q_BYTE || out->type->stype == Q_HALF)) { hack = *out; hack.type = &qbe_word; out = &hack; } va_geni(&stmt, instr, out, ap); va_end(ap); push(&func->body, &stmt); } void pushprei(struct qbe_func *func, const struct qbe_value *out, enum qbe_instr instr, ...) { struct qbe_statement stmt = {0}; va_list ap; va_start(ap, instr); va_geni(&stmt, instr, out, ap); va_end(ap); push(&func->prelude, &stmt); } void pushc(struct qbe_func *func, const char *fmt, ...) { struct qbe_statement stmt = {0}; va_list ap; va_start(ap, fmt); int n = vsnprintf(NULL, 0, fmt, ap); va_end(ap); char *str = xcalloc(1, n + 1); va_start(ap, fmt); vsnprintf(str, n + 1, fmt, ap); va_end(ap); stmt.comment = str; push(&func->body, &stmt); } struct qbe_value constl(uint64_t l) { return (struct qbe_value){ .kind = QV_CONST, .type = &qbe_long, .lval = l, }; } struct qbe_value constw(uint32_t w) { return (struct qbe_value){ .kind = QV_CONST, .type = &qbe_word, .wval = w, }; } struct qbe_value consts(float s) { return (struct qbe_value){ .kind = QV_CONST, .type = &qbe_single, .sval = s, }; } struct qbe_value constd(double d) { return (struct qbe_value){ .kind = QV_CONST, .type = &qbe_double, .dval = d, }; } harec-0.24.2/src/qinstr.c000066400000000000000000000121511464473277600151600ustar00rootroot00000000000000#include #include #include "gen.h" #include "qbe.h" #include "types.h" enum qbe_instr alloc_for_align(size_t align) { switch (align) { case 1: case 2: case 4: return Q_ALLOC4; case 8: return Q_ALLOC8; case 16: return Q_ALLOC16; default: abort(); } } enum qbe_instr store_for_type(struct gen_context *ctx, const struct type *type) { switch (type->storage) { case STORAGE_I8: case STORAGE_U8: case STORAGE_BOOL: return Q_STOREB; case STORAGE_I16: case STORAGE_U16: return Q_STOREH; case STORAGE_I32: case STORAGE_U32: case STORAGE_INT: case STORAGE_UINT: case STORAGE_RCONST: case STORAGE_RUNE: return Q_STOREW; case STORAGE_I64: case STORAGE_U64: return Q_STOREL; case STORAGE_F32: return Q_STORES; case STORAGE_F64: return Q_STORED; case STORAGE_SIZE: switch (ctx->arch.sz->stype) { case Q_LONG: return Q_STOREL; default: assert(0); } break; case STORAGE_POINTER: case STORAGE_UINTPTR: switch (ctx->arch.ptr->stype) { case Q_LONG: return Q_STOREL; default: assert(0); } break; case STORAGE_ENUM: case STORAGE_ALIAS: return store_for_type(ctx, type->alias.type); case STORAGE_ARRAY: case STORAGE_ERROR: case STORAGE_FCONST: case STORAGE_FUNCTION: case STORAGE_ICONST: case STORAGE_NEVER: case STORAGE_NULL: case STORAGE_OPAQUE: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: abort(); // Invariant } abort(); // Unreachable } enum qbe_instr load_for_type(struct gen_context *ctx, const struct type *type) { switch (type->storage) { case STORAGE_I8: return Q_LOADSB; case STORAGE_U8: case STORAGE_BOOL: return Q_LOADUB; case STORAGE_I16: return Q_LOADSH; case STORAGE_U16: return Q_LOADUH; case STORAGE_U32: case STORAGE_UINT: case STORAGE_RCONST: case STORAGE_RUNE: return Q_LOADUW; case STORAGE_I32: case STORAGE_INT: return Q_LOADSW; case STORAGE_I64: case STORAGE_U64: return Q_LOADL; case STORAGE_F32: return Q_LOADS; case STORAGE_F64: return Q_LOADD; case STORAGE_SIZE: switch (ctx->arch.sz->stype) { case Q_LONG: return Q_LOADL; default: assert(0); } break; case STORAGE_POINTER: case STORAGE_UINTPTR: switch (ctx->arch.ptr->stype) { case Q_LONG: return Q_LOADL; default: assert(0); } break; case STORAGE_ENUM: case STORAGE_ALIAS: return load_for_type(ctx, type->alias.type); case STORAGE_ARRAY: case STORAGE_ERROR: case STORAGE_FCONST: case STORAGE_FUNCTION: case STORAGE_ICONST: case STORAGE_NEVER: case STORAGE_NULL: case STORAGE_OPAQUE: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: abort(); // Invariant } abort(); // Unreachable } enum qbe_instr binarithm_for_op(struct gen_context *ctx, enum binarithm_operator op, const struct type *type) { // TODO: NaN, udiv et al bool is_signed = type_is_signed(NULL, type); enum qbe_stype stype = qtype_lookup(ctx, type, false)->stype; assert(stype != Q__AGGREGATE && stype != Q__VOID); switch (op) { case BIN_PLUS: return Q_ADD; case BIN_BAND: return Q_AND; case BIN_DIV: return is_signed ? Q_DIV : Q_UDIV; case BIN_MINUS: return Q_SUB; case BIN_TIMES: return Q_MUL; case BIN_MODULO: return is_signed ? Q_REM : Q_UREM; case BIN_BOR: return Q_OR; case BIN_BXOR: return Q_XOR; case BIN_LSHIFT: return Q_SHL; case BIN_RSHIFT: return is_signed ? Q_SAR : Q_SHR; case BIN_LEQUAL: switch (stype) { case Q_WORD: return Q_CEQW; case Q_LONG: return Q_CEQL; case Q_SINGLE: return Q_CEQS; case Q_DOUBLE: return Q_CEQD; default: assert(0); } break; case BIN_NEQUAL: case BIN_LXOR: switch (stype) { case Q_WORD: return Q_CNEW; case Q_LONG: return Q_CNEL; case Q_SINGLE: return Q_CNES; case Q_DOUBLE: return Q_CNED; default: assert(0); } break; case BIN_GREATER: switch (stype) { case Q_WORD: return is_signed ? Q_CSGTW : Q_CUGTW; case Q_LONG: return is_signed ? Q_CSGTL : Q_CUGTL; case Q_SINGLE: return Q_CGTS; case Q_DOUBLE: return Q_CGTD; default: assert(0); } case BIN_GREATEREQ: switch (stype) { case Q_WORD: return is_signed ? Q_CSGEW : Q_CUGEW; case Q_LONG: return is_signed ? Q_CSGEL : Q_CUGEL; case Q_SINGLE: return Q_CGES; case Q_DOUBLE: return Q_CGED; default: assert(0); } break; case BIN_LESS: switch (stype) { case Q_WORD: return is_signed ? Q_CSLTW : Q_CULTW; case Q_LONG: return is_signed ? Q_CSLTL : Q_CULTL; case Q_SINGLE: return Q_CLTS; case Q_DOUBLE: return Q_CLTD; default: assert(0); } break; case BIN_LESSEQ: switch (stype) { case Q_WORD: return is_signed ? Q_CSLEW : Q_CULEW; case Q_LONG: return is_signed ? Q_CSLEL : Q_CULEL; case Q_SINGLE: return Q_CLES; case Q_DOUBLE: return Q_CLED; default: assert(0); } break; case BIN_LAND: case BIN_LOR: assert(0); // Handled elsewhere to address short circuiting } assert(0); // Unreachable } harec-0.24.2/src/qtype.c000066400000000000000000000214161464473277600150060ustar00rootroot00000000000000#include #include #include #include "gen.h" #include "qbe.h" #include "types.h" #include "util.h" static const struct qbe_type * tagged_qtype(struct gen_context *ctx, const struct type *type, struct qbe_def *def) { def->type.stype = Q__UNION; // Identify maximum alignment among members size_t maxalign = 0, minalign = SIZE_MAX; for (const struct type_tagged_union *tu = &type->tagged; tu; tu = tu->next) { if (maxalign < tu->type->align) { maxalign = tu->type->align; } if (minalign > tu->type->align) { minalign = tu->type->align; } } // Create union members for each batch of alignments struct qbe_field *field = &def->type.fields; for (size_t align = 1; align <= 8; align <<= 1) { size_t nalign = 0; for (const struct type_tagged_union *tu = &type->tagged; tu; tu = tu->next) { if (tu->type->align != align || tu->type->size == 0) { continue; } ++nalign; } if (nalign == 0) { // No members of this alignment continue; } const char *valuesname; switch (align) { case 1: valuesname = "values.align1.%d"; break; case 2: valuesname = "values.align2.%d"; break; case 4: valuesname = "values.align4.%d"; break; case 8: valuesname = "values.align8.%d"; break; default: abort(); } // Produces type :values = { { x, y, z } } struct qbe_def *values = xcalloc(1, sizeof(struct qbe_def)); values->kind = Q_TYPE; values->name = gen_name(&ctx->id, valuesname); values->exported = false; values->type.stype = Q__UNION; values->type.base = NULL; values->type.name = values->name; values->type.size = type->size - type->align; size_t nfield = 0; struct qbe_field *bfield = &values->type.fields; for (const struct type_tagged_union *tu = &type->tagged; tu; tu = tu->next) { if (tu->type->align != align || tu->type->size == 0) { continue; } bfield->type = qtype_lookup(ctx, tu->type, true); bfield->count = 1; if (++nfield < nalign) { bfield->next = xcalloc(1, sizeof(struct qbe_field)); bfield = bfield->next; } } qbe_append_def(ctx->out, values); const char *batchname; switch (align) { case 1: batchname = "tagged.align1.%d"; break; case 2: batchname = "tagged.align2.%d"; break; case 4: batchname = "tagged.align4.%d"; break; case 8: batchname = "tagged.align8.%d"; break; default: abort(); } // Produces type :batch = { w 1, :values } struct qbe_def *batch = xcalloc(1, sizeof(struct qbe_def)); batch->kind = Q_TYPE; batch->name = gen_name(&ctx->id, batchname); batch->exported = false; batch->type.stype = Q__AGGREGATE; batch->type.base = NULL; batch->type.name = batch->name; batch->type.size = type->size - type->align; bfield = &batch->type.fields; bfield->type = &qbe_word; bfield->count = 1; bfield->next = xcalloc(1, sizeof(struct qbe_field)); bfield = bfield->next; bfield->type = &values->type; bfield->count = 1; qbe_append_def(ctx->out, batch); // And adds it to the tagged union type: // type :tagged = { :batch, :batch, ... } field->type = &batch->type; field->count = 1; if (align < maxalign) { field->next = xcalloc(1, sizeof(struct qbe_field)); field = field->next; } } if (minalign != 0) { return &def->type; } // Add a case for values of size zero field->next = xcalloc(1, sizeof(struct qbe_field)); field = field->next; field->type = &qbe_word; field->count = 1; return &def->type; } static const struct qbe_type * aggregate_lookup(struct gen_context *ctx, const struct type *type) { for (struct qbe_def *def = ctx->out->defs; def; def = def->next) { if (def->kind == Q_TYPE && def->type.base == type) { return &def->type; } } struct qbe_def *def = xcalloc(1, sizeof(struct qbe_def)); def->kind = Q_TYPE; def->name = gen_name(&ctx->id, "type.%d"); def->type.stype = Q__AGGREGATE; def->type.base = type; def->type.name = def->name; const struct type *final = type_dealias(NULL, type); assert((final->storage == STORAGE_STRUCT && final->struct_union.packed) || type->size == SIZE_UNDEFINED || type->size == 0 || type->size % type->align == 0); struct qbe_field *field = &def->type.fields; switch (type->storage) { case STORAGE_ARRAY: if (type->array.length == SIZE_UNDEFINED) { free(def->name); free(def); return &qbe_long; // Special case } field->count = type->array.length; field->type = qtype_lookup(ctx, type->array.members, true); break; case STORAGE_STRING: // XXX: This assertion does not hold for all architectures assert(ctx->arch.ptr->stype == ctx->arch.sz->stype); field->type = ctx->arch.ptr; field->count = 3; break; case STORAGE_SLICE: // XXX: This assertion does not hold for all architectures assert(ctx->arch.ptr->stype == ctx->arch.sz->stype); field->type = ctx->arch.ptr; field->count = 3; break; case STORAGE_UNION: def->type.stype = Q__UNION; // fallthrough case STORAGE_STRUCT: if (!type->struct_union.c_compat) { field->type = NULL; field->count = type->size; break; } for (struct struct_field *tfield = type->struct_union.fields; tfield; tfield = tfield->next) { if (tfield->type->size != 0) { field->type = qtype_lookup(ctx, tfield->type, true); field->count = 1; } if (tfield->next && tfield->next->type->size != 0) { field->next = xcalloc(1, sizeof(struct qbe_field)); field = field->next; } } break; case STORAGE_TUPLE: for (const struct type_tuple *tuple = &type->tuple; tuple; tuple = tuple->next) { if (tuple->type->size == 0) { continue; } field->type = qtype_lookup(ctx, tuple->type, true); field->count = 1; if (tuple->next) { field->next = xcalloc(1, sizeof(struct qbe_field)); field = field->next; } } break; case STORAGE_TAGGED: tagged_qtype(ctx, type, def); break; case STORAGE_ENUM: case STORAGE_ERROR: case STORAGE_ALIAS: case STORAGE_I8: case STORAGE_U8: case STORAGE_I16: case STORAGE_U16: case STORAGE_BOOL: case STORAGE_I32: case STORAGE_U32: case STORAGE_RCONST: case STORAGE_RUNE: case STORAGE_INT: case STORAGE_UINT: case STORAGE_I64: case STORAGE_U64: case STORAGE_ICONST: case STORAGE_SIZE: case STORAGE_UINTPTR: case STORAGE_POINTER: case STORAGE_NULL: case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: case STORAGE_FUNCTION: case STORAGE_OPAQUE: case STORAGE_NEVER: abort(); // Invariant } qbe_append_def(ctx->out, def); return &def->type; } const struct qbe_type * qtype_lookup(struct gen_context *ctx, const struct type *type, bool xtype) { switch (type->storage) { case STORAGE_U8: case STORAGE_I8: case STORAGE_BOOL: return xtype ? &qbe_byte : &qbe_word; case STORAGE_I16: case STORAGE_U16: return xtype ? &qbe_half : &qbe_word; case STORAGE_I32: case STORAGE_U32: case STORAGE_INT: case STORAGE_UINT: case STORAGE_RUNE: return &qbe_word; case STORAGE_U64: case STORAGE_I64: return &qbe_long; case STORAGE_SIZE: return ctx->arch.sz; case STORAGE_UINTPTR: case STORAGE_POINTER: case STORAGE_NULL: return ctx->arch.ptr; case STORAGE_F32: return &qbe_single; case STORAGE_F64: return &qbe_double; case STORAGE_ENUM: case STORAGE_ALIAS: return qtype_lookup(ctx, type->alias.type, xtype); case STORAGE_ARRAY: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: return aggregate_lookup(ctx, type); case STORAGE_FUNCTION: return ctx->arch.ptr; case STORAGE_VALIST: return ctx->arch.ptr; case STORAGE_ERROR: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_VOID: case STORAGE_DONE: abort(); // Invariant case STORAGE_FCONST: case STORAGE_ICONST: case STORAGE_RCONST: return qtype_lookup(ctx, lower_flexible(NULL, type, NULL), xtype); } abort(); // Invariant } bool type_is_aggregate(const struct type *type) { switch (type->storage) { case STORAGE_BOOL: case STORAGE_ENUM: case STORAGE_F32: case STORAGE_F64: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_I8: case STORAGE_INT: case STORAGE_POINTER: case STORAGE_NULL: case STORAGE_RUNE: case STORAGE_SIZE: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_U8: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_VOID: case STORAGE_DONE: return false; case STORAGE_FUNCTION: // Special case return false; case STORAGE_ALIAS: return type_is_aggregate(type->alias.type); case STORAGE_ARRAY: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_VALIST: return true; case STORAGE_FCONST: case STORAGE_ICONST: case STORAGE_RCONST: lower_flexible(NULL, type, NULL); return false; case STORAGE_ERROR: case STORAGE_NEVER: case STORAGE_OPAQUE: assert(0); // Invariant } assert(0); // Unreachable } harec-0.24.2/src/scope.c000066400000000000000000000057651464473277600147660ustar00rootroot00000000000000#include #include #include #include "expr.h" #include "identifier.h" #include "scope.h" #include "util.h" static uint32_t name_hash(uint32_t init, const struct identifier *ident) { return fnv1a_s(init, ident->name); } struct scope * scope_push(struct scope **stack, enum scope_class class) { struct scope *new = xcalloc(1, sizeof(struct scope)); new->class = class; new->next = &new->objects; new->parent = *stack; *stack = new; return new; } struct scope * scope_pop(struct scope **stack) { struct scope *prev = *stack; assert(prev); *stack = prev->parent; return prev; } struct scope * scope_lookup_class(struct scope *scope, enum scope_class class) { while (scope) { if (scope->class == class) { break; } scope = scope->parent; } return scope; } struct scope * scope_lookup_label(struct scope *scope, const char *label) { while (scope) { if (scope->label && strcmp(scope->label, label) == 0) { break; } scope = scope->parent; } return scope; } void scope_free(struct scope *scope) { if (!scope) { return; } struct scope_object *obj = scope->objects; while (obj) { struct scope_object *next = obj->lnext; free(obj); obj = next; } free(scope); } void scope_free_all(struct scopes *scopes) { while (scopes) { struct scopes *next = scopes->next; scope_free(scopes->scope); free(scopes); scopes = next; } } void scope_object_init(struct scope_object *object, enum object_type otype, const struct identifier *ident, const struct identifier *name, const struct type *type, struct expression *value) { identifier_dup(&object->ident, ident); identifier_dup(&object->name, name); object->otype = otype; if (type) { object->type = type; } else if (value) { object->value = value; assert(otype == O_CONST); assert(value->type == EXPR_LITERAL); } flexible_refer(type, &object->type); } void scope_insert_from_object(struct scope *scope, struct scope_object *object) { // Linked list *scope->next = object; scope->next = &object->lnext; // Hash map uint32_t hash = name_hash(FNV1A_INIT, &object->name); struct scope_object **bucket = &scope->buckets[hash % SCOPE_BUCKETS]; if (*bucket) { object->mnext = *bucket; } *bucket = object; } struct scope_object * scope_insert(struct scope *scope, enum object_type otype, const struct identifier *ident, const struct identifier *name, const struct type *type, struct expression *value) { assert(!type != !value); struct scope_object *o = xcalloc(1, sizeof(struct scope_object)); scope_object_init(o, otype, ident, name, type, value); scope_insert_from_object(scope, o); return o; } struct scope_object * scope_lookup(struct scope *scope, const struct identifier *ident) { uint32_t hash = name_hash(FNV1A_INIT, ident); struct scope_object *bucket = scope->buckets[hash % SCOPE_BUCKETS]; while (bucket) { if (identifier_eq(&bucket->name, ident)) { return bucket; } bucket = bucket->mnext; } if (scope->parent) { return scope_lookup(scope->parent, ident); } return NULL; } harec-0.24.2/src/type_store.c000066400000000000000000001172321464473277600160430ustar00rootroot00000000000000#include #include #include #include #include "check.h" #include "eval.h" #include "scope.h" #include "type_store.h" #include "types.h" #include "util.h" static struct dimensions lookup_atype_with_dimensions(struct context *ctx, const struct type **type, const struct ast_type *atype); static const struct type * lookup_atype(struct context *ctx, const struct ast_type *atype); static size_t ast_array_len(struct context *ctx, const struct ast_type *atype) { // TODO: Maybe we should cache these struct expression in, out; if (atype->array.length == NULL) { return SIZE_UNDEFINED; } check_expression(ctx, atype->array.length, &in, NULL); if (!eval_expr(ctx, &in, &out)) { error(ctx, atype->loc, NULL, "Cannot evaluate array length at compile time"); return SIZE_UNDEFINED; } if (!type_is_integer(ctx, out.result)) { error(ctx, atype->loc, NULL, "Array length must be an integer"); return SIZE_UNDEFINED; } if (type_is_signed(ctx, out.result) && out.literal.ival < 0) { error(ctx, atype->loc, NULL, "Array length must be non-negative"); return SIZE_UNDEFINED; } return (size_t)out.literal.uval; } const struct type * builtin_type_for_storage(enum type_storage storage, bool is_const) { switch (storage) { case STORAGE_BOOL: return is_const ? &builtin_type_const_bool : &builtin_type_bool; case STORAGE_ERROR: return &builtin_type_error; case STORAGE_F32: return is_const ? &builtin_type_const_f32 : &builtin_type_f32; case STORAGE_F64: return is_const ? &builtin_type_const_f64 : &builtin_type_f64; case STORAGE_I8: return is_const ? &builtin_type_const_i8 : &builtin_type_i8; case STORAGE_I16: return is_const ? &builtin_type_const_i16 : &builtin_type_i16; case STORAGE_I32: return is_const ? &builtin_type_const_i32 : &builtin_type_i32; case STORAGE_I64: return is_const ? &builtin_type_const_i64 : &builtin_type_i64; case STORAGE_INT: return is_const ? &builtin_type_const_int : &builtin_type_int; case STORAGE_NEVER: return is_const ? &builtin_type_const_never : &builtin_type_never; case STORAGE_OPAQUE: return is_const ? &builtin_type_const_opaque : &builtin_type_opaque; case STORAGE_RUNE: return is_const ? &builtin_type_const_rune : &builtin_type_rune; case STORAGE_SIZE: return is_const ? &builtin_type_const_size : &builtin_type_size; case STORAGE_U8: return is_const ? &builtin_type_const_u8 : &builtin_type_u8; case STORAGE_U16: return is_const ? &builtin_type_const_u16 : &builtin_type_u16; case STORAGE_U32: return is_const ? &builtin_type_const_u32 : &builtin_type_u32; case STORAGE_U64: return is_const ? &builtin_type_const_u64 : &builtin_type_u64; case STORAGE_UINT: return is_const ? &builtin_type_const_uint : &builtin_type_uint; case STORAGE_UINTPTR: return is_const ? &builtin_type_const_uintptr : &builtin_type_uintptr; case STORAGE_VALIST: return &builtin_type_valist; case STORAGE_VOID: return is_const ? &builtin_type_const_void : &builtin_type_void; case STORAGE_DONE: return is_const ? &builtin_type_const_done : &builtin_type_done; case STORAGE_NULL: return &builtin_type_null; // const null and null are the same type case STORAGE_STRING: return is_const ? &builtin_type_const_str : &builtin_type_str; case STORAGE_ALIAS: case STORAGE_ARRAY: case STORAGE_FUNCTION: case STORAGE_FCONST: case STORAGE_ICONST: case STORAGE_RCONST: case STORAGE_POINTER: case STORAGE_SLICE: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_ENUM: return NULL; } assert(0); // Unreachable } static const struct type * builtin_for_type(const struct type *type) { if (type->flags & TYPE_ERROR) { return NULL; } bool is_const = (type->flags & TYPE_CONST) != 0; return builtin_type_for_storage(type->storage, is_const); } static bool struct_union_has_field(struct context *ctx, const char *name, const struct struct_field *fields) { for (; fields; fields = fields->next) { if (fields->name != NULL) { if (strcmp(fields->name, name) == 0) { return true; } continue; } assert(fields->type != NULL); const struct type *type = type_dealias(ctx, fields->type); if (struct_union_has_field(ctx, name, type->struct_union.fields)) { return true; } } return false; } static struct struct_field * struct_new_field(struct context *ctx, struct type *type, const struct ast_struct_union_field *afield, size_t *offset, bool size_only) { if (afield->name != NULL && !size_only) { if (struct_union_has_field(ctx, afield->name, type->struct_union.fields)) { error(ctx, afield->type->loc, NULL, "Duplicate struct/union member '%s'", afield->name); return NULL; } } struct struct_field *field = xcalloc(1, sizeof(struct struct_field)); if (afield->name && !size_only) { field->name = xstrdup(afield->name); } struct dimensions dim = {0}; if (size_only) { dim = lookup_atype_with_dimensions(ctx, NULL, afield->type); } else { dim = lookup_atype_with_dimensions(ctx, &field->type, afield->type); } if (afield->next != NULL && dim.size == SIZE_UNDEFINED) { error(ctx, afield->type->loc, NULL, "Type of undefined size is not a valid struct/union member"); return NULL; } if (dim.align == ALIGN_UNDEFINED) { error(ctx, afield->type->loc, NULL, "Type of undefined alignment is not a valid struct/union member"); return NULL; } type->align = dim.align > type->align ? dim.align : type->align; field->size = dim.size; if (type->storage == STORAGE_UNION) { if (afield->offset) { error(ctx, afield->type->loc, NULL, "Union fields cannot be given explicit offset"); } field->offset = 0; if (dim.size == SIZE_UNDEFINED || type->size == SIZE_UNDEFINED) { type->size = SIZE_UNDEFINED; } else { type->size = dim.size > type->size ? dim.size : type->size; } return field; } if (afield->offset) { type->struct_union.c_compat = false; bool err = true; struct expression in, out; check_expression(ctx, afield->offset, &in, NULL); field->offset = *offset; if (!eval_expr(ctx, &in, &out)) { error(ctx, in.loc, NULL, "Cannot evaluate field offset at compile time"); } else if (!type_is_integer(ctx, out.result)) { error(ctx, in.loc, NULL, "Field offset must be an integer"); } else if (type_is_signed(ctx, out.result) && out.literal.ival < 0) { error(ctx, in.loc, NULL, "Field offset must not be less than 0"); } else if (out.literal.uval < *offset) { error(ctx, in.loc, NULL, "Field offset must be greater than or equal to previous field's offset"); } else if (out.literal.uval < type->size) { error(ctx, in.loc, NULL, "Fields must not have overlapping storage"); } else { err = false; field->offset = *offset = (size_t)out.literal.uval; } if (err) { return NULL; } } else if (type->struct_union.packed) { field->offset = *offset = type->size; } else { *offset = type->size; if (dim.align != 0 && *offset % dim.align) { *offset += dim.align - (*offset % dim.align); } field->offset = *offset; assert(dim.align == 0 || field->offset % dim.align == 0); } if (dim.size == SIZE_UNDEFINED || type->size == SIZE_UNDEFINED) { type->size = SIZE_UNDEFINED; } else { type->size = field->offset + dim.size; } return field; } static const struct type *type_store_lookup_type(struct context *ctx, const struct type *type); bool check_embedded_member(struct context *ctx, const struct ast_struct_union_field *afield, struct struct_field *member, const struct struct_field *fields) { assert(member->type != NULL); const struct type *dealiased = type_dealias(ctx, member->type); if (dealiased->storage != STORAGE_STRUCT && dealiased->storage != STORAGE_UNION) { error(ctx, afield->type->loc, NULL, "Cannot embed non-struct non-union alias"); member->type = &builtin_type_error; return false; } for (struct struct_field *field = dealiased->struct_union.fields; field; field = field->next) { if (field->name != NULL) { if (struct_union_has_field(ctx, field->name, fields)) { // XXX: the location could be better error(ctx, afield->type->loc, NULL, "Duplicate struct/union member '%s'", field->name); return false; } } else { if (!check_embedded_member(ctx, afield, field, fields)) { return false; } } } return true; } void shift_fields(struct context *ctx, const struct ast_struct_union_field *afield, struct struct_field *parent) { if (parent->offset == 0) { // We need to return early here in order to avoid dealiasing an // embedded alias. This is acceptable at nonzero offsets, but we // need to keep the alias if it's at offset 0 because of // subtyping. return; } const struct type *type = type_dealias(ctx, parent->type); assert(type->storage == STORAGE_STRUCT || type->storage == STORAGE_UNION); struct type new = { .storage = type->storage, .flags = type->flags, .size = type->size, .align = type->align, .struct_union.c_compat = type->struct_union.c_compat, .struct_union.packed = type->struct_union.packed, }; struct struct_field **next = &new.struct_union.fields; for (struct struct_field *field = type->struct_union.fields; field; field = field->next) { struct struct_field *new = *next = xcalloc(1, sizeof(struct struct_field)); next = &new->next; new->type = field->type; new->offset = parent->offset; if (field->name) { new->name = xstrdup(field->name); } else { shift_fields(ctx, NULL, new); } // Sub-subfields are shifted by field->offset in the recursive // shift_fields call, delay adding it to new->offset to avoid // shifting by field->offset twice new->offset += field->offset; } parent->type = type_store_lookup_type(ctx, &new); } static bool struct_init_from_atype(struct context *ctx, struct type *type, const struct ast_type *atype, bool size_only) { // TODO: fields with size SIZE_UNDEFINED if (type->storage == STORAGE_UNION && atype->struct_union.packed) { error(ctx, atype->loc, NULL, "Cannot use @packed attribute for union type"); return false; } type->struct_union.packed = atype->struct_union.packed; type->struct_union.c_compat = !atype->struct_union.packed; size_t offset = 0; assert(type->storage == STORAGE_STRUCT || type->storage == STORAGE_UNION); struct struct_field **next = &type->struct_union.fields; for (const struct ast_struct_union_field *afield = &atype->struct_union.fields; afield; afield = afield->next) { struct struct_field *field = struct_new_field(ctx, type, afield, &offset, size_only); if (field == NULL) { return false; } if (size_only) { free(field); continue; } else if (!field->name) { if (!check_embedded_member(ctx, afield, field, type->struct_union.fields)) { return false; } // We need to shift the embedded struct/union's fields // so that their offsets are from the start of the // parent type. This is a bit of a hack, but it makes // type_get_field far easier to implement and doesn't // cause any trouble in gen since offsets are only used // there for sorting fields. shift_fields(ctx, afield, field); } *next = field; next = &field->next; } return true; } static bool enforce_tagged_invariants(struct context *ctx, struct location loc, const struct type *type) { int i; const struct type_tagged_union *tu; for (i = 0, tu = &type->tagged; tu; i++, tu = tu->next) { if (tu->type->storage == STORAGE_NULL) { error(ctx, loc, NULL, "Null type not allowed in this context"); return false; } if (tu->type->size == SIZE_UNDEFINED) { error(ctx, loc, NULL, "Type of undefined size is not a valid tagged union member"); return false; } assert(tu->type->align != ALIGN_UNDEFINED); } if (i <= 1) { error(ctx, loc, NULL, "Tagged unions must have at least two distinct members"); return false; } return true; } static size_t sum_tagged_memb(struct context *ctx, const struct type_tagged_union *u) { size_t nmemb = 0; for (; u; u = u->next) { const struct type *type = u->type; if (type->storage == STORAGE_TAGGED) { nmemb += sum_tagged_memb(ctx, &type->tagged); } else { ++nmemb; } } return nmemb; } // get next member of an incomplete tagged union without completing it static void tagged_or_atagged_member(struct context *ctx, const struct ast_type **atype, const struct type **type) { const struct ast_type *_atype = *atype; while (_atype->storage == STORAGE_ALIAS && _atype->unwrap) { const struct scope_object *obj = scope_lookup( ctx->scope, &_atype->alias); if (!obj) { char *ident = identifier_unparse(&_atype->alias); error(ctx, _atype->loc, NULL, "Unknown object '%s'", ident); free(ident); *type = &builtin_type_error; return; } if (obj->otype != O_SCAN) { if (obj->otype == O_TYPE) { *type = type_dealias(ctx, obj->type); return; } else { char *ident = identifier_unparse(&obj->ident); error(ctx, _atype->loc, NULL, "Object '%s' is not a type", ident); free(ident); *type = &builtin_type_error; return; } } struct incomplete_declaration *idecl = (struct incomplete_declaration *)obj; if (idecl->type != IDECL_DECL || idecl->decl.decl_type != ADECL_TYPE) { char *ident = identifier_unparse(&obj->ident); error(ctx, _atype->loc, NULL, "Object '%s' is not a type", ident); free(ident); *type = &builtin_type_error; return; } _atype = idecl->decl.type.type; } *type = NULL; *atype = _atype; } static size_t sum_atagged_memb(struct context *ctx, const struct ast_tagged_union_type *u) { size_t nmemb = 0; for (; u; u = u->next) { const struct type *type = NULL; const struct ast_type *atype = u->type; tagged_or_atagged_member(ctx, &atype, &type); if (type != NULL && type->storage == STORAGE_TAGGED) { nmemb += sum_tagged_memb(ctx, &type->tagged); } else if (atype->storage == STORAGE_TAGGED) { nmemb += sum_atagged_memb(ctx, &atype->tagged); } else { ++nmemb; } } return nmemb; } static void collect_tagged_memb(struct context *ctx, struct type_tagged_union **ta, const struct type_tagged_union *src, size_t *i) { for (; src; src = src->next) { const struct type *type = src->type; if (type->storage == STORAGE_TAGGED) { collect_tagged_memb(ctx, ta, &type->tagged, i); continue; } struct type_tagged_union *tu; ta[*i] = tu = xcalloc(1, sizeof(struct type_tagged_union)); tu->type = lower_flexible(ctx, type, NULL); *i += 1; } } static void collect_atagged_memb(struct context *ctx, struct type_tagged_union **ta, const struct ast_tagged_union_type *atu, size_t *i) { for (; atu; atu = atu->next) { const struct type *type = lookup_atype(ctx, atu->type); if (type->storage == STORAGE_TAGGED) { collect_tagged_memb(ctx, ta, &type->tagged, i); continue; } struct type_tagged_union *tu; ta[*i] = tu = xcalloc(1, sizeof(struct type_tagged_union)); tu->type = lower_flexible(ctx, type, NULL); *i += 1; } } static int tagged_cmp(const void *ptr_a, const void *ptr_b) { const struct type_tagged_union **a = (const struct type_tagged_union **)ptr_a; const struct type_tagged_union **b = (const struct type_tagged_union **)ptr_b; return (*a)->type->id < (*b)->type->id ? -1 : (*a)->type->id > (*b)->type->id ? 1 : 0; } static void tagged_init(struct type *type, struct type_tagged_union **tu, size_t nmemb) { // Sort by ID qsort(tu, nmemb, sizeof(tu[0]), tagged_cmp); // Prune duplicates size_t nmemb_dedup = 1; for (size_t i = 1; i < nmemb; ++i) { if (tu[i]->type->id != tu[nmemb_dedup - 1]->type->id) { tu[nmemb_dedup++] = tu[i]; } } nmemb = nmemb_dedup; // First one free type->tagged = *tu[0]; free(tu[0]); type->size = type->tagged.type->size; type->align = type->tagged.type->align; struct type_tagged_union **next = &type->tagged.next; for (size_t i = 1; i < nmemb; ++i) { if (tu[i]->type->size > type->size) { type->size = tu[i]->type->size; } if (tu[i]->type->align > type->align) { type->align = tu[i]->type->align; } *next = tu[i]; next = &tu[i]->next; } if (type->align < builtin_type_u32.align) { type->align = builtin_type_u32.align; } type->size += builtin_type_u32.size % type->align + builtin_type_u32.align; } static void tagged_init_from_atype(struct context *ctx, struct type *type, const struct ast_type *atype) { size_t nmemb = sum_atagged_memb(ctx, &atype->tagged); struct type_tagged_union **tu = xcalloc(nmemb, sizeof(struct type_tagged_union *)); size_t i = 0; collect_atagged_memb(ctx, tu, &atype->tagged, &i); tagged_init(type, tu, i); if (!enforce_tagged_invariants(ctx, atype->loc, type)) { *type = builtin_type_error; } } static struct dimensions _tagged_size(struct context *ctx, const struct ast_tagged_union_type *u) { struct dimensions dim = {0}; for (; u; u = u->next) { struct dimensions memb = {0}; const struct type *type = NULL; const struct ast_type *atype = u->type; tagged_or_atagged_member(ctx, &atype, &type); if (type != NULL && type->storage == STORAGE_TAGGED) { for (const struct type_tagged_union *u = &type->tagged; u; u = u->next) { if (memb.size < u->type->size) { memb.size = u->type->size; } if (memb.align < u->type->align) { memb.align = u->type->align; } } } else if (atype->storage == STORAGE_TAGGED) { memb = _tagged_size(ctx, &atype->tagged); } else { memb = lookup_atype_with_dimensions(ctx, NULL, atype); } if (memb.size == SIZE_UNDEFINED) { error(ctx, atype->loc, NULL, "Type of undefined size is not a valid tagged union member"); return (struct dimensions){0}; } if (dim.size < memb.size) { dim.size = memb.size; } if (dim.align < memb.align) { dim.align = memb.align; } } return dim; } // compute the dimensions of an incomplete tagged union without completing it static struct dimensions tagged_size(struct context *ctx, const struct ast_type *atype) { struct dimensions dim = _tagged_size(ctx, &atype->tagged); if (dim.align < builtin_type_u32.align) { dim.align = builtin_type_u32.align; } dim.size += builtin_type_u32.size % dim.align + builtin_type_u32.align; return dim; } static struct dimensions tuple_init_from_atype(struct context *ctx, struct type *type, const struct ast_type *atype) { const struct ast_tuple_type *atuple = &atype->tuple; struct type_tuple *cur = NULL; if (type) { type->size = 0, type->align = 0; cur = &type->tuple; } struct dimensions dim = {0}; while (atuple) { struct dimensions memb = {0}; size_t offset = 0; if (type) { memb = lookup_atype_with_dimensions(ctx, &cur->type, atuple->type); } else { memb = lookup_atype_with_dimensions(ctx, NULL, atuple->type); } if (memb.size == SIZE_UNDEFINED) { error(ctx, atype->loc, NULL, "Type of undefined size is not a valid tuple member"); if (type) { *type = builtin_type_error; } return (struct dimensions){0}; } if (memb.align != 0) { offset = dim.size; if (dim.size % memb.align) { offset += memb.align - dim.size % memb.align; } dim.size = offset + memb.size; } if (dim.align < memb.align) { dim.align = memb.align; } atuple = atuple->next; if (type) { if (memb.align != 0) { cur->offset = offset; } if (atuple) { cur->next = xcalloc(1, sizeof(struct type_tuple)); cur = cur->next; } } } if (type) { type->size = dim.size; type->align = dim.align; } return dim; } static void add_padding(size_t *size, size_t align) { if (*size != SIZE_UNDEFINED && *size != 0 && *size % align != 0) { *size += align - *size % align; } } static bool default_param_from_atype(struct context *ctx, const struct ast_function_parameters *aparam, struct type_func_param *param) { // This is leaked. check_expression makes a flexible ref that may be // updated later, so it cannot be on the stack. struct expression *in = xcalloc(1, sizeof(struct expression)); check_expression(ctx, aparam->default_value, in, param->type); if (!type_is_assignable(ctx, param->type, in->result)) { char *restypename = gen_typename(in->result); char *partypename = gen_typename(param->type); error(ctx, aparam->loc, NULL, "Result value %s is not assignable to parameter type %s", restypename, partypename); free(restypename); free(partypename); return false; } param->default_value = xcalloc(1, sizeof(struct expression)); struct expression *cast = lower_implicit_cast(ctx, param->type, in); if (!eval_expr(ctx, cast, param->default_value)) { error(ctx, aparam->loc, NULL, "Unable to evaluate default parameter at compile time"); return false; } // TODO remove this check once it works if (param->default_value->result->storage == STORAGE_POINTER && param->default_value->literal.object != NULL) { error(ctx, aparam->loc, NULL, "Non-null pointer optional parameters are not currently supported. Will fix."); return false; } return true; } static struct dimensions type_init_from_atype(struct context *ctx, struct type *type, const struct ast_type *atype) { struct type tmp = {0}; bool size_only = false; if (type == NULL) { type = &tmp; size_only = true; } type->storage = atype->storage; type->flags = atype->flags; struct scope_object *obj = NULL; const struct type *builtin; switch (type->storage) { case STORAGE_ERROR: case STORAGE_FCONST: case STORAGE_ICONST: case STORAGE_RCONST: case STORAGE_ENUM: case STORAGE_NULL: assert(0); // Invariant case STORAGE_BOOL: case STORAGE_F32: case STORAGE_F64: case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_RUNE: case STORAGE_SIZE: case STORAGE_STRING: case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: builtin = builtin_type_for_storage(type->storage, false); type->size = builtin->size; type->align = builtin->align; break; case STORAGE_ALIAS: obj = scope_lookup(ctx->scope, &atype->alias); if (!obj) { char *ident = identifier_unparse(&atype->alias); error(ctx, atype->loc, NULL, "Unresolvable identifier '%s'", ident); free(ident); *type = builtin_type_error; return (struct dimensions){0}; } if (obj->otype == O_SCAN) { // an incomplete declaration was encountered struct incomplete_declaration *idecl = (struct incomplete_declaration *)obj; if (size_only && idecl->type == IDECL_DECL) { wrap_resolver(ctx, obj, resolve_dimensions); type->size = obj->type->size; type->align = obj->type->align; break; } // complete it first and then proceed normally wrap_resolver(ctx, obj, resolve_type); } if (obj->otype != O_TYPE) { char *ident = identifier_unparse(&obj->ident); error(ctx, atype->loc, NULL, "Object '%s' is not a type", ident); free(ident); *type = builtin_type_error; return (struct dimensions){0}; } type->storage = obj->type->storage; if (obj->type->storage == STORAGE_ENUM) { type->_enum = obj->type->_enum; } else if (atype->unwrap) { *type = *type_dealias(ctx, obj->type); break; } identifier_dup(&type->alias.ident, &obj->ident); identifier_dup(&type->alias.name, &obj->name); type->alias.type = obj->type->alias.type; type->alias.exported = obj->type->alias.exported; type->size = obj->type->size; type->align = obj->type->align; break; case STORAGE_ARRAY: type->array.length = ast_array_len(ctx, atype); struct dimensions memb = {0}; if (size_only) { memb = lookup_atype_with_dimensions(ctx, NULL, atype->array.members); } else { memb = lookup_atype_with_dimensions(ctx, &type->array.members, atype->array.members); if (type->array.members->storage == STORAGE_ERROR) { *type = builtin_type_error; return (struct dimensions){0}; } } if (memb.size == 0) { error(ctx, atype->loc, NULL, "Type of size 0 is not a valid array member"); *type = builtin_type_error; return (struct dimensions){0}; } if (memb.size == SIZE_UNDEFINED) { error(ctx, atype->loc, NULL, "Type of undefined size is not a valid array member"); *type = builtin_type_error; return (struct dimensions){0}; } type->align = memb.align; if (type->array.length == SIZE_UNDEFINED) { type->size = SIZE_UNDEFINED; } else { type->size = memb.size * type->array.length; } break; case STORAGE_FUNCTION: type->size = SIZE_UNDEFINED; type->align = ALIGN_UNDEFINED; if (size_only) { break; } type->func.result = lookup_atype(ctx, atype->func.result); type->func.variadism = atype->func.variadism; struct type_func_param *param, **next = &type->func.params; bool has_optional = false; for (struct ast_function_parameters *aparam = atype->func.params; aparam; aparam = aparam->next) { param = *next = xcalloc(1, sizeof(struct type_func_param)); param->type = lookup_atype(ctx, aparam->type); if (param->type->size == SIZE_UNDEFINED) { error(ctx, atype->loc, NULL, "Function parameter types must have defined size"); *type = builtin_type_error; return (struct dimensions){0}; } if (aparam->default_value != NULL) { has_optional = true; if (!default_param_from_atype(ctx, aparam, param)) { *type = builtin_type_error; return (struct dimensions){0}; } } else if (atype->func.variadism == VARIADISM_HARE && !aparam->next) { param->type = type_store_lookup_slice( ctx, aparam->loc, param->type); } else if (has_optional) { error(ctx, atype->loc, NULL, "Required function parameter may not follow optional parameters"); *type = builtin_type_error; return (struct dimensions){0}; } next = ¶m->next; } break; case STORAGE_POINTER: type->size = builtin_type_uintptr.size; type->align = builtin_type_uintptr.align; if (size_only) { break; } type->pointer.flags = atype->pointer.flags; type->pointer.referent = lookup_atype( ctx, atype->pointer.referent); if (type->pointer.referent->storage == STORAGE_ERROR) { *type = builtin_type_error; return (struct dimensions){0}; } if (type->pointer.referent->size == 0) { error(ctx, atype->loc, NULL, "Can't have pointer to zero-sized type"); *type = builtin_type_error; return (struct dimensions){0}; } if (type->pointer.referent->storage == STORAGE_NEVER) { error(ctx, atype->loc, NULL, "Can't have pointer to never"); *type = builtin_type_error; return (struct dimensions){0}; } break; case STORAGE_SLICE: type->size = builtin_type_uintptr.size + 2 * builtin_type_size.size; type->align = builtin_type_uintptr.align; if (size_only) { break; } type->array.members = lookup_atype(ctx, atype->slice.members); if (type->array.members->storage == STORAGE_ERROR) { *type = builtin_type_error; return (struct dimensions){0}; } if (type->array.members->size == 0) { error(ctx, atype->loc, NULL, "Type of size 0 is not a valid slice member"); *type = builtin_type_error; return (struct dimensions){0}; } if (type->array.members->storage == STORAGE_NEVER) { error(ctx, atype->loc, NULL, "Never is not a valid slice member"); *type = builtin_type_error; return (struct dimensions){0}; } type->array.length = SIZE_UNDEFINED; break; case STORAGE_STRUCT: case STORAGE_UNION: if (!struct_init_from_atype(ctx, type, atype, size_only)) { *type = builtin_type_error; return (struct dimensions){0}; } break; case STORAGE_TAGGED: if (size_only) { struct dimensions tagged = tagged_size(ctx, atype); type->size = tagged.size; type->align = tagged.align; } else { tagged_init_from_atype(ctx, type, atype); } break; case STORAGE_TUPLE: if (size_only) { struct dimensions tup; tup = tuple_init_from_atype(ctx, NULL, atype); type->size = tup.size; type->align = tup.align; } else { tuple_init_from_atype(ctx, type, atype); } break; } struct dimensions dim = { .size = type->size, .align = type->align, }; if (type->storage != STORAGE_STRUCT || !type->struct_union.packed) { // padding an alias can only break packed structs if (type->storage != STORAGE_ALIAS) { add_padding(&dim.size, dim.align); } } return dim; } static const struct type * _type_store_lookup_type( struct context *ctx, const struct type *type, const struct dimensions *dims) { const struct type *builtin = builtin_for_type(type); if (builtin) { return builtin; } uint32_t hash = type_hash(type); struct type_bucket **next = &(*ctx->store)[hash % TYPE_STORE_BUCKETS], *bucket = NULL; while (*next) { bucket = *next; if (bucket->type.id == hash) { if (bucket->type.storage == STORAGE_ALIAS) { type = type->alias.type; bucket->type.alias.type = type; if (type && type->storage == STORAGE_ERROR) { return &builtin_type_error; } } return &bucket->type; } next = &bucket->next; } bucket = *next = xcalloc(1, sizeof(struct type_bucket)); bucket->type = *type; bucket->type.id = hash; if (dims == NULL) { add_padding(&bucket->type.size, type->align); } return &bucket->type; } static const struct type * type_store_lookup_type(struct context *ctx, const struct type *type) { if (type->storage != STORAGE_ALIAS) { return _type_store_lookup_type(ctx, type, NULL); } // References to type aliases always inherit the flags that the // alias was defined with struct type psuedotype = *type; const struct scope_object *obj = scope_lookup( ctx->scope, &type->alias.name); psuedotype.flags |= obj->type->flags; return type_store_lookup_alias(ctx, &psuedotype, NULL); } static struct dimensions lookup_atype_with_dimensions(struct context *ctx, const struct type **type, const struct ast_type *atype) { struct type temp = {0}; struct dimensions dim = {0}; if (type) { dim = type_init_from_atype(ctx, &temp, atype); *type = type_store_lookup_type(ctx, &temp); } else { dim = type_init_from_atype(ctx, NULL, atype); } return dim; } static const struct type * lookup_atype(struct context *ctx, const struct ast_type *atype) { const struct type *type = NULL; lookup_atype_with_dimensions(ctx, &type, atype); return type; } const struct type * type_store_lookup_atype(struct context *ctx, const struct ast_type *atype) { if (atype->storage == STORAGE_NULL) { return &builtin_type_null; } return lookup_atype(ctx, atype); } // Compute dimensions of an incomplete type without completing it struct dimensions type_store_lookup_dimensions(struct context *ctx, const struct ast_type *atype) { return type_init_from_atype(ctx, NULL, atype); } const struct type * type_store_lookup_with_flags(struct context *ctx, const struct type *type, unsigned int flags) { if (type->flags == flags) { return type; } struct type new = *type; new.flags = flags; return _type_store_lookup_type(ctx, &new, NULL); } const struct type * type_store_lookup_pointer(struct context *ctx, struct location loc, const struct type *referent, unsigned int ptrflags) { if (referent->storage == STORAGE_ERROR) { return &builtin_type_error; } if (referent->storage == STORAGE_NULL) { error(ctx, loc, NULL, "Null type not allowed in this context"); return &builtin_type_error; } if (referent->size == 0) { error(ctx, loc, NULL, "Can't have pointer to zero-sized type"); return &builtin_type_error; } if (referent->storage == STORAGE_NEVER) { error(ctx, loc, NULL, "Can't have pointer to never"); return &builtin_type_error; } referent = lower_flexible(ctx, referent, NULL); struct type ptr = { .storage = STORAGE_POINTER, .pointer = { .referent = referent, .flags = ptrflags, }, .size = builtin_type_uintptr.size, .align = builtin_type_uintptr.align, }; return type_store_lookup_type(ctx, &ptr); } const struct type * type_store_lookup_array(struct context *ctx, struct location loc, const struct type *members, size_t len, bool expandable) { if (members->storage == STORAGE_ERROR) { return &builtin_type_error; } if (members->storage == STORAGE_NULL) { error(ctx, loc, NULL, "Null type not allowed in this context"); return &builtin_type_error; } members = lower_flexible(ctx, members, NULL); if (members->size == 0) { error(ctx, loc, NULL, "Type of size 0 is not a valid array member"); return &builtin_type_error; } if (members->size == SIZE_UNDEFINED) { error(ctx, loc, NULL, "Type of undefined size is not a valid member of a bounded array"); return &builtin_type_error; } assert(members->align != 0); assert(members->align != ALIGN_UNDEFINED); struct type array = { .storage = STORAGE_ARRAY, .array = { .members = members, .length = len, // TODO: Define expandable semantics better in spec .expandable = expandable, }, .size = len == SIZE_UNDEFINED ? SIZE_UNDEFINED : members->size * len, .align = members->align, }; return type_store_lookup_type(ctx, &array); } const struct type * type_store_lookup_slice(struct context *ctx, struct location loc, const struct type *members) { if (members->storage == STORAGE_ERROR) { return &builtin_type_error; } if (members->storage == STORAGE_NULL) { error(ctx, loc, NULL, "Null type not allowed in this context"); return &builtin_type_error; } members = lower_flexible(ctx, members, NULL); if (members->size == 0) { error(ctx, loc, NULL, "Type of size 0 is not a valid slice member"); return &builtin_type_error; } assert(members->align != 0); struct type slice = { .storage = STORAGE_SLICE, .array = { .members = members, .length = SIZE_UNDEFINED, }, .size = builtin_type_uintptr.size + 2 * builtin_type_size.size, .align = builtin_type_uintptr.align, }; return type_store_lookup_type(ctx, &slice); } const struct type * type_store_lookup_alias(struct context *ctx, const struct type *type, const struct dimensions *dims) { return _type_store_lookup_type(ctx, type, dims); } // Sorts members by id and deduplicates entries. Does not enforce usual tagged // union invariants. The returned type is not a singleton. static const struct type * lookup_tagged(struct context *ctx, struct type_tagged_union *tags) { struct type type = { .storage = STORAGE_TAGGED, }; size_t nmemb = sum_tagged_memb(ctx, tags); struct type_tagged_union **tu = xcalloc(nmemb, sizeof(struct type_tagged_union *)); size_t i = 0; collect_tagged_memb(ctx, tu, tags, &i); tagged_init(&type, tu, nmemb); struct type *ret = xcalloc(1, sizeof(struct type)); *ret = type; return ret; } const struct type * type_store_lookup_tagged(struct context *ctx, struct location loc, struct type_tagged_union *tags) { const struct type *type = lookup_tagged(ctx, tags); if (!enforce_tagged_invariants(ctx, loc, type)) { return &builtin_type_error; } return type_store_lookup_type(ctx, type); } const struct type * type_store_tagged_to_union(struct context *ctx, const struct type *tagged) { assert(tagged->storage == STORAGE_TAGGED); struct type type = { .storage = STORAGE_UNION, .flags = tagged->flags, }; struct struct_field **next = &type.struct_union.fields; for (const struct type_tagged_union *tu = &tagged->tagged; tu; tu = tu->next) { if (tu->type->size == 0) { continue; } if (tu->type->size > type.size) { type.size = tu->type->size; } if (tu->type->align > type.align) { type.align = tu->type->align; } struct struct_field *sf = xcalloc(1, sizeof(struct struct_field)); sf->name = "unnamed"; sf->type = tu->type; sf->next = *next, *next = sf; next = &sf->next; } type.struct_union.c_compat = true; // XXX: Unsure about this return _type_store_lookup_type(ctx, &type, NULL); } const struct type * type_store_lookup_tuple(struct context *ctx, struct location loc, struct type_tuple *values) { struct type type = { .storage = STORAGE_TUPLE, }; for (struct type_tuple *t = values; t; t = t->next) { if (t->type->storage == STORAGE_ERROR) { return &builtin_type_error; } if (t->type->storage == STORAGE_NULL) { error(ctx, loc, NULL, "Null type not allowed in this context"); return &builtin_type_error; } t->type = lower_flexible(ctx, t->type, NULL); if (t->type->size == SIZE_UNDEFINED) { error(ctx, loc, NULL, "Type of undefined size is not a valid tuple member"); return &builtin_type_error; } assert(t->type->align != ALIGN_UNDEFINED); if (t->type->align > type.align) { type.align = t->type->align; } if (t->type->align != 0) { t->offset = type.size; if (type.size % t->type->align) { t->offset += t->type->align - type.size % t->type->align; } type.size = t->offset + t->type->size; } } type.tuple = *values; return type_store_lookup_type(ctx, &type); } const struct type * type_store_lookup_enum(struct context *ctx, const struct ast_type *atype, bool exported) { struct type type = {0}; type.storage = STORAGE_ENUM; type.flags = atype->flags; mkident(ctx, &type.alias.ident, &atype->alias, NULL); identifier_dup(&type.alias.name, &atype->alias); type.alias.exported = exported; type.alias.type = builtin_type_for_storage(atype->_enum.storage, false); if (!type_is_integer(ctx, type.alias.type) && type.alias.type->storage != STORAGE_RUNE) { error(ctx, atype->loc, NULL, "Enum storage must be an integer or rune"); return &builtin_type_error; } type.size = type.alias.type->size; type.align = type.alias.type->size; return type_store_lookup_type(ctx, &type); } // Algorithm: // - Deduplicate and collect nested unions // - Remove never // - Merge *type with nullable *type // - If one of the types is null: // - If there's more than one pointer type, error out // - If there's one pointer type, make it nullable and drop the null // - If there are no pointer types, keep the null // - If the resulting union only has one type, return that type // - Otherwise, if no types remain, return never // - Otherwise, return a tagged union of all the selected types const struct type * type_store_reduce_result(struct context *ctx, struct location loc, struct type_tagged_union *in) { if (!in) { return &builtin_type_never; } else if (!in->next) { return in->type; } const struct type *type = lookup_tagged(ctx, in); struct type_tagged_union _in = type->tagged; in = &_in; struct type_tagged_union **null = NULL; struct type_tagged_union *ptr = NULL; bool multiple_ptrs = false; struct type_tagged_union **tu = ∈ while (*tu != NULL) { struct type_tagged_union *i = *tu; bool dropped = false; const struct type *it = i->type; if (it->flags & TYPE_CONST) { struct type_tagged_union **j = ∈ while (*j) { const struct type *jt = (*j)->type; if (jt == it) { j = &(*j)->next; continue; } jt = type_store_lookup_with_flags(ctx, jt, jt->flags | TYPE_CONST); if (jt == it) { *j = (*j)->next; } else { j = &(*j)->next; } } } if (it->storage == STORAGE_NEVER || it->storage == STORAGE_ERROR) { *tu = i->next; continue; } for (struct type_tagged_union *j = in; j != i; j = j->next) { const struct type *jt = j->type; assert(it->id != jt->id); if (it->storage != STORAGE_POINTER || jt->storage != STORAGE_POINTER) { continue; } if (it->pointer.referent->id != jt->pointer.referent->id) { continue; } if (it->flags != jt->flags) { continue; } if ((it->pointer.flags & PTR_NULLABLE) || (jt->pointer.flags & PTR_NULLABLE)) { it = type_store_lookup_pointer(ctx, loc, it->pointer.referent, PTR_NULLABLE); jt = type_store_lookup_pointer(ctx, loc, jt->pointer.referent, PTR_NULLABLE); if (it == jt) { dropped = true; *tu = i->next; j->type = jt; break; } } } if (i->type->storage == STORAGE_NULL) { null = tu; } if (!dropped) { if (i->type->storage == STORAGE_POINTER) { if (ptr != NULL) { multiple_ptrs = true; } ptr = i; } tu = &i->next; } } if (null != NULL && (multiple_ptrs || ptr == NULL)) { return NULL; } if (null != NULL && ptr != NULL) { *null = (*null)->next; ptr->type = type_store_lookup_pointer(ctx, loc, ptr->type->pointer.referent, PTR_NULLABLE); } if (in == NULL) { return &builtin_type_never; } else if (in->next == NULL) { return in->type; } return type_store_lookup_tagged(ctx, loc, in); } harec-0.24.2/src/typedef.c000066400000000000000000000273201464473277600153040ustar00rootroot00000000000000#include #include #include #include #include #include #include "check.h" #include "expr.h" #include "identifier.h" #include "typedef.h" #include "util.h" static const char * storage_to_suffix(enum type_storage storage) { switch (storage) { case STORAGE_F32: return "f32"; case STORAGE_F64: return "f64"; case STORAGE_FCONST: return ""; case STORAGE_I16: return "i16"; case STORAGE_I32: return "i32"; case STORAGE_I64: return "i64"; case STORAGE_I8: return "i8"; case STORAGE_ICONST: return ""; case STORAGE_INT: return "i"; case STORAGE_SIZE: return "z"; case STORAGE_U16: return "u16"; case STORAGE_U32: return "u32"; case STORAGE_U64: return "u64"; case STORAGE_U8: return "u8"; case STORAGE_UINT: return "u"; case STORAGE_UINTPTR: return "u64: uintptr"; case STORAGE_VALIST: return "valist"; default: assert(0); } } static void emit_literal(const struct expression *expr, FILE *out) { assert(expr->type == EXPR_LITERAL); const struct expression_literal *val = &expr->literal; assert(!val->object); const struct type *t = type_dealias(NULL, expr->result); switch (t->storage) { case STORAGE_BOOL: xfprintf(out, "%s", val->bval ? "true" : "false"); break; case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST:; const char *suffix = storage_to_suffix(expr->result->storage); if (isnan(val->fval)) { xfprintf(out, "0.0%s / 0.0%s", suffix, suffix); } else if (isinf(val->fval)) { xfprintf(out, "%s1.0%s / 0.0%s", (val->fval > 0) ? "" : "-", suffix, suffix); } else { xfprintf(out, "%a%s", val->fval, suffix); } break; case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_I8: case STORAGE_ICONST: case STORAGE_INT: xfprintf(out, "%" PRIi64 "%s", val->ival, storage_to_suffix(type_dealias(NULL, expr->result)->storage)); break; case STORAGE_POINTER: // TODO case STORAGE_NULL: xfprintf(out, "null"); break; case STORAGE_SIZE: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_U8: case STORAGE_UINT: case STORAGE_UINTPTR: xfprintf(out, "%" PRIu64 "%s", val->uval, storage_to_suffix(type_dealias(NULL, expr->result)->storage)); break; case STORAGE_VOID: xfprintf(out, "void"); break; case STORAGE_DONE: xfprintf(out, "done"); break; case STORAGE_RCONST: case STORAGE_RUNE: xfprintf(out, "\'\\U%08" PRIx32 "\'", val->rune); break; case STORAGE_STRING: xfprintf(out, "\""); for (size_t i = 0; i < val->string.len; i += 1) { char c = val->string.value[i]; if (isalnum((unsigned char)c)) { xfprintf(out, "%c", c); } else { xfprintf(out, "\\x%02X", c); } }; xfprintf(out, "\""); break; case STORAGE_ENUM:; char *ident = identifier_unparse(&expr->result->alias.ident); if (t->alias.type->storage == STORAGE_UINTPTR) { xfprintf(out, "%" PRIu64 ": uintptr", val->uval); } else if (type_is_signed(NULL, t->alias.type)) { xfprintf(out, "%" PRIi64 "%s: %s", val->ival, storage_to_suffix(t->alias.type->storage), ident); } else if (t->alias.type->storage == STORAGE_RUNE) { xfprintf(out, "\'\\U%08" PRIx32 "\'", val->rune); } else { xfprintf(out, "%" PRIu64 "%s: %s", val->uval, storage_to_suffix(t->alias.type->storage), ident); } free(ident); break; case STORAGE_TAGGED: emit_literal(expr->literal.tagged.value, out); xfprintf(out, ": "); emit_type(expr->literal.tagged.tag, out); break; case STORAGE_SLICE: case STORAGE_ARRAY: xfprintf(out, "["); for (const struct array_literal *item = val->array; item; item = item->next) { emit_literal(item->value, out); if (item->next) { xfprintf(out, ", "); } } if (t->array.expandable) { xfprintf(out, "..."); } xfprintf(out, "]"); break; case STORAGE_TUPLE: xfprintf(out, "("); for (const struct tuple_literal *item = val->tuple; item; item = item->next) { emit_literal(item->value, out); if (item->next) { xfprintf(out, ", "); } } xfprintf(out, ")"); break; case STORAGE_STRUCT: if (expr->result->storage == STORAGE_ALIAS) { char *ident = identifier_unparse(&expr->result->alias.ident); xfprintf(out, "%s", ident); free(ident); } else { xfprintf(out, "struct"); } xfprintf(out, " { "); for (struct struct_literal *cur = val->_struct; cur; cur = cur->next) { xfprintf(out, "%s: ", cur->field->name); emit_type(cur->field->type, out); xfprintf(out, " = "); emit_literal(cur->value, out); xfprintf(out, ", "); } xfprintf(out, "}"); break; case STORAGE_UNION: assert(0); // TODO, blocked on union support in eval case STORAGE_ALIAS: case STORAGE_ERROR: case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_VALIST: assert(0); // Invariant } } static void emit_struct(const struct type *type, FILE *out) { xfprintf(out, "%s %s{ ", type->storage == STORAGE_STRUCT ? "struct" : "union", type->struct_union.packed ? "@packed " : ""); for (const struct struct_field *f = type->struct_union.fields; f; f = f->next) { if (!type->struct_union.c_compat) { xfprintf(out, "@offset(%zu) ", f->offset); } if (f->name) { xfprintf(out, "%s: ", f->name); } emit_type(f->type, out); xfprintf(out, ", "); } xfprintf(out, "}"); } void emit_type(const struct type *type, FILE *out) { if (type->flags & TYPE_CONST) { xfprintf(out, "const "); } if (type->flags & TYPE_ERROR) { xfprintf(out, "!"); } char *ident; switch (type->storage) { case STORAGE_BOOL: case STORAGE_ERROR: case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_I8: case STORAGE_INT: case STORAGE_NEVER: case STORAGE_NULL: case STORAGE_OPAQUE: case STORAGE_RCONST: case STORAGE_RUNE: case STORAGE_SIZE: case STORAGE_STRING: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_U8: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: xfprintf(out, "%s", type_storage_unparse(type->storage)); break; case STORAGE_POINTER: xfprintf(out, "%s*", type->pointer.flags & PTR_NULLABLE ? "nullable " : ""); emit_type(type->pointer.referent, out); break; case STORAGE_ARRAY: if (type->array.length == SIZE_UNDEFINED) { xfprintf(out, "[*]"); } else { xfprintf(out, "[%zu]", type->array.length); } emit_type(type->array.members, out); break; case STORAGE_SLICE: xfprintf(out, "[]"); emit_type(type->array.members, out); break; case STORAGE_ALIAS: ident = identifier_unparse(&type->alias.ident); xfprintf(out, "%s", ident); free(ident); break; case STORAGE_TAGGED: xfprintf(out, "("); for (const struct type_tagged_union *tu = &type->tagged; tu; tu = tu->next) { emit_type(tu->type, out); if (tu->next) { xfprintf(out, " | "); } } xfprintf(out, ")"); break; case STORAGE_STRUCT: case STORAGE_UNION: emit_struct(type, out); break; case STORAGE_FUNCTION: xfprintf(out, "fn("); for (const struct type_func_param *param = type->func.params; param; param = param->next) { if (param->next) { emit_type(param->type, out); xfprintf(out, ", "); } else if (type->func.variadism == VARIADISM_HARE) { emit_type(param->type->array.members, out); xfprintf(out, "..."); } else if (type->func.variadism == VARIADISM_C) { emit_type(param->type, out); xfprintf(out, ", ..."); } else { emit_type(param->type, out); } } xfprintf(out, ") "); emit_type(type->func.result, out); break; case STORAGE_ENUM: ident = identifier_unparse(&type->alias.ident); xfprintf(out, "%s", ident); free(ident); break; case STORAGE_TUPLE: xfprintf(out, "("); for (const struct type_tuple *tuple = &type->tuple; tuple; tuple = tuple->next) { emit_type(tuple->type, out); if (tuple->next) { xfprintf(out, ", "); } } xfprintf(out, ")"); break; case STORAGE_ICONST: xfprintf(out, "[iconst min=%" PRIi64 " max=%" PRIi64 "]", type->flexible.min, type->flexible.max); break; } } static void emit_decl_const(const struct declaration *decl, FILE *out) { char *ident = identifier_unparse(&decl->ident); xfprintf(out, "export def %s", ident); assert(decl->constant.type); if (decl->constant.type->size != SIZE_UNDEFINED) { xfprintf(out, ": "); emit_type(decl->constant.type, out); } free(ident); xfprintf(out, " = "); emit_literal(decl->constant.value, out); xfprintf(out, ";\n"); } static void emit_decl_func(const struct declaration *decl, FILE *out) { char *ident = identifier_unparse(&decl->ident); const struct type *fntype = decl->func.type; xfprintf(out, "export "); if (decl->symbol) { xfprintf(out, "@symbol(\"%s\") ", decl->symbol); } xfprintf(out, "fn %s(", ident); for (const struct type_func_param *param = fntype->func.params; param; param = param->next) { if (param->next) { emit_type(param->type, out); if (param->default_value) { xfprintf(out, " = "); emit_literal(param->default_value, out); } xfprintf(out, ", "); } else if (fntype->func.variadism == VARIADISM_HARE) { emit_type(param->type->array.members, out); xfprintf(out, "..."); } else if (fntype->func.variadism == VARIADISM_C) { emit_type(param->type, out); xfprintf(out, ", ..."); } else { emit_type(param->type, out); if (param->default_value) { xfprintf(out, " = "); emit_literal(param->default_value, out); } } } xfprintf(out, ") "); emit_type(fntype->func.result, out); xfprintf(out, ";\n"); free(ident); } static void emit_decl_global(const struct declaration *decl, FILE *out) { char *ident = identifier_unparse(&decl->ident); xfprintf(out, "export let "); if (decl->symbol) { xfprintf(out, "@symbol(\"%s\") ", decl->symbol); } if (decl->global.threadlocal) { xfprintf(out, "@threadlocal "); } xfprintf(out, "%s: ", ident); if (decl->constant.type) { emit_type(decl->global.type, out); } else { emit_type(decl->global.value->result, out); }; xfprintf(out, ";\n"); free(ident); } static void emit_decl_type(const struct declaration *decl, FILE *out) { char *ident = identifier_unparse(&decl->ident); xfprintf(out, "export type %s = ", ident); assert(decl->type->storage == STORAGE_ALIAS || decl->type->storage == STORAGE_ENUM); if (decl->type->storage == STORAGE_ENUM) { const struct type *type = decl->type; xfprintf(out, "enum %s { ", type_storage_unparse(type->alias.type->storage)); for (const struct scope_object *ev = type->_enum.values->objects; ev; ev = ev->lnext) { assert(ev->otype != O_SCAN); xfprintf(out, "%s = ", ev->name.name); emit_literal(ev->value, out); xfprintf(out, ", "); } xfprintf(out, "}"); } else { emit_type(decl->type->alias.type, out); } xfprintf(out, "; // size: "); if (decl->type->size == SIZE_UNDEFINED) { xfprintf(out, "undefined"); } else { xfprintf(out, "%zu", decl->type->size); } xfprintf(out, ", align: "); if (decl->type->align == ALIGN_UNDEFINED) { xfprintf(out, "undefined"); } else { xfprintf(out, "%zu", decl->type->align); } xfprintf(out, ", id: %" PRIu32 "\n", decl->type->id); free(ident); } void emit_typedefs(const struct unit *unit, FILE *out) { for (const struct identifiers *imports = unit->imports; imports; imports = imports->next) { char *ident = identifier_unparse(&imports->ident); xfprintf(out, "use %s;\n", ident); free(ident); } for (const struct declarations *decls = unit->declarations; decls; decls = decls->next) { const struct declaration *decl = &decls->decl; if (!decl->exported) { continue; } switch (decl->decl_type) { case DECL_CONST: emit_decl_const(decl, out); break; case DECL_FUNC: emit_decl_func(decl, out); break; case DECL_GLOBAL: emit_decl_global(decl, out); break; case DECL_TYPE: emit_decl_type(decl, out); break; } } } harec-0.24.2/src/types.c000066400000000000000000001037351464473277600150150ustar00rootroot00000000000000#include #include #include #include #include #include "check.h" #include "expr.h" #include "scope.h" #include "types.h" #include "util.h" const struct type * type_dereference(struct context *ctx, const struct type *type) { switch (type->storage) { case STORAGE_ALIAS: if (type_dealias(ctx, type)->storage != STORAGE_POINTER) { return type; } return type_dereference(ctx, type_dealias(ctx, type)); case STORAGE_POINTER: if (type->pointer.flags & PTR_NULLABLE) { return NULL; } return type_dereference(ctx, type->pointer.referent); default: return type; } } void complete_alias(struct context *ctx, struct type *type) { assert(type->storage == STORAGE_ALIAS); const struct scope_object *obj = scope_lookup(ctx->scope, &type->alias.name); assert(obj != NULL); assert(obj->otype == O_TYPE || obj->otype == O_SCAN); struct incomplete_declaration *idecl = (struct incomplete_declaration *)obj; assert(idecl->type == IDECL_DECL); if (idecl->dealias_in_progress) { char *identstr = identifier_unparse(&idecl->obj.name); error(ctx, idecl->decl.loc, NULL, "Circular dependency for '%s'", identstr); free(identstr); type->alias.type = &builtin_type_error; return; } idecl->dealias_in_progress = true; type->alias.type = type_store_lookup_atype(ctx, idecl->decl.type.type); idecl->dealias_in_progress = false; } const struct type * type_dealias(struct context *ctx, const struct type *type) { while (type->storage == STORAGE_ALIAS) { if (type->alias.type == NULL) { // gen et al. don't have access to the check context, // but by that point all aliases should already be fully // scanned assert(ctx != NULL); complete_alias(ctx, (struct type *)type); } type = type->alias.type; } return type; } const struct struct_field * type_get_field(struct context *ctx, const struct type *type, const char *name) { if (type->storage == STORAGE_ERROR) { return NULL; } assert(type->storage == STORAGE_STRUCT || type->storage == STORAGE_UNION); struct struct_field *field = type->struct_union.fields; while (field) { if (field->name) { if (strcmp(field->name, name) == 0) { return field; } } else { const struct struct_field *f = type_get_field(ctx, type_dealias(ctx, field->type), name); if (f != NULL) { return f; } } field = field->next; } return NULL; } const struct type_tuple * type_get_value(const struct type *type, uint64_t index) { assert(type->storage == STORAGE_TUPLE); const struct type_tuple *tuple = &type->tuple; while (tuple) { if (index == 0) { return tuple; } tuple = tuple->next; --index; } return NULL; } // Returns true if this type is or contains an error type bool type_has_error(struct context *ctx, const struct type *type) { if (type->flags & TYPE_ERROR) { return true; } type = type_dealias(ctx, type); if (type->storage != STORAGE_TAGGED) { return false; } const struct type_tagged_union *tu = &type->tagged; for (; tu; tu = tu->next) { if (tu->type->flags & TYPE_ERROR) { return true; } } return false; } const char * type_storage_unparse(enum type_storage storage) { switch (storage) { case STORAGE_ALIAS: return "alias"; case STORAGE_ARRAY: return "array"; case STORAGE_BOOL: return "bool"; case STORAGE_ENUM: return "enum"; case STORAGE_F32: return "f32"; case STORAGE_F64: return "f64"; case STORAGE_ERROR: return "invalid"; case STORAGE_FCONST: return "fconst"; case STORAGE_FUNCTION: return "function"; case STORAGE_I16: return "i16"; case STORAGE_I32: return "i32"; case STORAGE_I64: return "i64"; case STORAGE_I8: return "i8"; case STORAGE_ICONST: return "iconst"; case STORAGE_INT: return "int"; case STORAGE_NEVER: return "never"; case STORAGE_NULL: return "null"; case STORAGE_OPAQUE: return "opaque"; case STORAGE_POINTER: return "pointer"; case STORAGE_RCONST: return "rconst"; case STORAGE_RUNE: return "rune"; case STORAGE_SIZE: return "size"; case STORAGE_SLICE: return "slice"; case STORAGE_STRING: return "str"; case STORAGE_STRUCT: return "struct"; case STORAGE_TAGGED: return "tagged union"; case STORAGE_TUPLE: return "tuple"; case STORAGE_U16: return "u16"; case STORAGE_U32: return "u32"; case STORAGE_U64: return "u64"; case STORAGE_U8: return "u8"; case STORAGE_UINT: return "uint"; case STORAGE_UINTPTR: return "uintptr"; case STORAGE_UNION: return "union"; case STORAGE_VALIST: return "valist"; case STORAGE_VOID: return "void"; case STORAGE_DONE: return "done"; } assert(0); } bool type_is_integer(struct context *ctx, const struct type *type) { switch (type->storage) { case STORAGE_VOID: case STORAGE_DONE: case STORAGE_ARRAY: case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_POINTER: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_BOOL: case STORAGE_NULL: case STORAGE_RCONST: case STORAGE_RUNE: case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: case STORAGE_VALIST: return false; case STORAGE_ENUM: case STORAGE_ERROR: case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_ICONST: case STORAGE_INT: case STORAGE_SIZE: case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: return true; case STORAGE_ALIAS: return type_is_integer(ctx, type_dealias(ctx, type)); } assert(0); // Unreachable } bool type_is_numeric(struct context *ctx, const struct type *type) { switch (type->storage) { case STORAGE_VOID: case STORAGE_DONE: case STORAGE_ARRAY: case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_POINTER: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_BOOL: case STORAGE_RCONST: case STORAGE_RUNE: case STORAGE_NULL: case STORAGE_VALIST: return false; case STORAGE_ERROR: case STORAGE_ENUM: case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_ICONST: case STORAGE_INT: case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: case STORAGE_SIZE: case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: return true; case STORAGE_ALIAS: return type_is_numeric(ctx, type_dealias(ctx, type)); } assert(0); // Unreachable } bool type_is_float(struct context *ctx, const struct type *type) { type = type_dealias(ctx, type); return type->storage == STORAGE_F32 || type->storage == STORAGE_F64 || type->storage == STORAGE_FCONST || type->storage == STORAGE_ERROR; } bool type_is_signed(struct context *ctx, const struct type *type) { enum type_storage storage = type_dealias(ctx, type)->storage; if (storage == STORAGE_ENUM) { storage = type_dealias(ctx, type)->alias.type->storage; } switch (storage) { case STORAGE_VOID: case STORAGE_DONE: case STORAGE_ARRAY: case STORAGE_ENUM: case STORAGE_ERROR: // XXX? case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_OPAQUE: case STORAGE_POINTER: case STORAGE_SLICE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TAGGED: case STORAGE_TUPLE: case STORAGE_UNION: case STORAGE_BOOL: case STORAGE_RCONST: case STORAGE_RUNE: case STORAGE_NULL: case STORAGE_SIZE: case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_VALIST: return false; case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: case STORAGE_F32: case STORAGE_F64: case STORAGE_FCONST: return true; case STORAGE_ICONST: return type->flexible.min < 0; case STORAGE_ALIAS: assert(0); // Handled above } assert(0); // Unreachable } bool type_is_flexible(const struct type *type) { return type->storage == STORAGE_FCONST || type->storage == STORAGE_ICONST || type->storage == STORAGE_RCONST; } uint32_t type_hash(const struct type *type) { uint32_t hash = FNV1A_INIT; hash = fnv1a(hash, type->storage); hash = fnv1a(hash, type->flags); switch (type->storage) { case STORAGE_BOOL: case STORAGE_ERROR: case STORAGE_F32: case STORAGE_F64: case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: case STORAGE_NEVER: case STORAGE_NULL: case STORAGE_OPAQUE: case STORAGE_RUNE: case STORAGE_SIZE: case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: case STORAGE_UINTPTR: case STORAGE_VALIST: case STORAGE_VOID: case STORAGE_DONE: case STORAGE_STRING: break; // built-ins case STORAGE_ENUM: hash = fnv1a(hash, type->alias.type->storage); /* fallthrough */ case STORAGE_ALIAS: for (const struct identifier *ident = &type->alias.ident; ident; ident = ident->ns) { hash = fnv1a_s(hash, ident->name); hash = fnv1a(hash, 0); } break; case STORAGE_ARRAY: hash = fnv1a_u32(hash, type_hash(type->array.members)); hash = fnv1a_size(hash, type->array.length); hash = fnv1a_u32(hash, type->array.expandable); break; case STORAGE_FUNCTION: hash = fnv1a_u32(hash, type_hash(type->func.result)); hash = fnv1a(hash, type->func.variadism); for (struct type_func_param *param = type->func.params; param; param = param->next) { hash = fnv1a_u32(hash, type_hash(param->type)); if (param->default_value) { hash = fnv1a_u32(hash, expr_hash( param->default_value)); } } break; case STORAGE_FCONST: case STORAGE_ICONST: case STORAGE_RCONST: hash = fnv1a(hash, type->flexible.id); break; case STORAGE_POINTER: hash = fnv1a(hash, type->pointer.flags); hash = fnv1a_u32(hash, type_hash(type->pointer.referent)); break; case STORAGE_SLICE: hash = fnv1a_u32(hash, type_hash(type->array.members)); break; case STORAGE_STRUCT: case STORAGE_UNION: for (const struct struct_field *field = type->struct_union.fields; field; field = field->next) { if (field->name) { hash = fnv1a_s(hash, field->name); } hash = fnv1a_u32(hash, type_hash(field->type)); hash = fnv1a_size(hash, field->offset); } break; case STORAGE_TAGGED: // Invariant: subtypes must be sorted by ID and must not include // any other tagged union types, nor any duplicates. for (const struct type_tagged_union *tu = &type->tagged; tu; tu = tu->next) { hash = fnv1a_u32(hash, type_hash(tu->type)); } break; case STORAGE_TUPLE: for (const struct type_tuple *tuple = &type->tuple; tuple; tuple = tuple->next) { hash = fnv1a_u32(hash, type_hash(tuple->type)); } break; } return hash; } // Note that the type this returns is NOT a type singleton and cannot be treated // as such. static const struct type * strip_flags(const struct type *t, struct type *secondary) { if (!t->flags) { return t; } *secondary = *t; secondary->flags = 0; secondary->id = type_hash(secondary); return secondary; } // Duplicate and return the tags of a tagged union struct type_tagged_union * tagged_dup_tags(const struct type_tagged_union *tags) { struct type_tagged_union *ret = xcalloc(1, sizeof(struct type_tagged_union)); struct type_tagged_union *cur_ret = ret; for (const struct type_tagged_union *tu = tags; tu; tu = tu->next) { if (cur_ret->type != NULL) { cur_ret->next = xcalloc(1, sizeof(struct type_tagged_union)); cur_ret = cur_ret->next; } cur_ret->type = tu->type; } return ret; } const struct type * tagged_select_subtype(struct context *ctx, const struct type *tagged, const struct type *subtype, bool strip) { tagged = type_dealias(ctx, tagged); assert(tagged->storage == STORAGE_TAGGED); struct type _stripped; const struct type *stripped = strip_flags(subtype, &_stripped); size_t nassign = 0; const struct type *selected = NULL; for (const struct type_tagged_union *tu = &tagged->tagged; tu; tu = tu->next) { if (tu->type->id == subtype->id) { return tu->type; } if (type_dealias(ctx, tu->type)->storage == STORAGE_VOID) { continue; } if (type_is_assignable(ctx, tu->type, subtype)) { selected = tu->type; ++nassign; } } if (strip) { for (const struct type_tagged_union *tu = &tagged->tagged; tu; tu = tu->next) { struct type _tustripped; const struct type *tustripped = strip_flags(tu->type, &_tustripped); if (tustripped->id == stripped->id) { return tu->type; } } } if (nassign == 1) { return selected; } return NULL; } static int64_t min_value(struct context *ctx, const struct type *t) { assert(type_is_integer(ctx, t)); if (!type_is_signed(ctx, t)) { return 0; } if (t->size == sizeof(int64_t)) { return INT64_MIN; } return -((int64_t)1 << (t->size * 8 - 1)); } static uint64_t max_value(struct context *ctx, const struct type *t) { assert(type_is_integer(ctx, t)); size_t bits = t->size * 8; if (type_is_signed(ctx, t)) { bits--; } if (bits == sizeof(uint64_t) * 8) { return UINT64_MAX; } return ((uint64_t)1 << bits) - 1; } const struct type * type_create_flexible(enum type_storage storage, int64_t min, int64_t max) { // XXX: This'll be impossible to free. The right solution would be to // store iconsts in the type store, but that'd require passing the store // into type_is_assignable et al. An easier solution would be to keep // our own list of iconsts and free them separately. Whatever, it // doesn't really matter that much. static uint32_t id = 0; struct type *type = xcalloc(1, sizeof(struct type)); type->storage = storage; type->size = SIZE_UNDEFINED; type->align = ALIGN_UNDEFINED; type->flexible.min = min; type->flexible.max = max; type->flexible.id = id++; type->id = type_hash(type); assert(type_is_flexible(type)); return type; } // Register a reference to a flexible type. When `type` is lowered in // [[lower_flexible]], *ref will be updated to point to the new type. void flexible_refer(const struct type *type, const struct type **ref) { if (type == NULL || !type_is_flexible(type)) { return; } struct type_flexible *flex = (struct type_flexible *)&type->flexible; if (flex->nrefs >= flex->zrefs) { flex->zrefs *= 2; if (flex->zrefs == 0) { flex->zrefs++; } flex->refs = xrealloc(flex->refs, flex->zrefs * sizeof(const struct type **)); } flex->refs[flex->nrefs] = ref; flex->nrefs++; } // Sets the number of references for a flexible type to zero. void flexible_reset_refs(const struct type *type) { if (type == NULL || !type_is_flexible(type)) { return; } ((struct type *)type)->flexible.nrefs = 0; } // Lower a flexible type. If new == NULL, lower it to its default type. const struct type * lower_flexible(struct context *ctx, const struct type *old, const struct type *new) { if (!type_is_flexible(old)) { // If new != NULL, we're expected to always do something, and we // can't if it's not flexible assert(new == NULL); return old; } if (new == NULL) { switch (old->storage) { case STORAGE_FCONST: new = &builtin_type_f64; break; case STORAGE_ICONST: if (old->flexible.max <= (int64_t)max_value(ctx, &builtin_type_int) && old->flexible.min >= min_value(ctx, &builtin_type_int)) { new = &builtin_type_int; } else { new = &builtin_type_i64; } break; case STORAGE_RCONST: new = &builtin_type_rune; break; default: assert(0); } } for (size_t i = 0; i < old->flexible.nrefs; i++) { flexible_refer(new, old->flexible.refs[i]); *old->flexible.refs[i] = new; } // XXX: Can we free old? return new; } // Implements the flexible type promotion algorithm const struct type * promote_flexible(struct context *ctx, const struct type *a, const struct type *b) { if (a->storage == STORAGE_ICONST && b->storage == STORAGE_ICONST) { int64_t min = a->flexible.min < b->flexible.min ? a->flexible.min : b->flexible.min; int64_t max = a->flexible.max > b->flexible.max ? a->flexible.max : b->flexible.max; const struct type *l = type_create_flexible(STORAGE_ICONST, min, max); lower_flexible(ctx, a, l); lower_flexible(ctx, b, l); return l; } if (type_is_flexible(a)) { if (a->storage == b->storage) { const struct type *l = type_create_flexible(a->storage, 0, 0); lower_flexible(ctx, a, l); lower_flexible(ctx, b, l); return l; } if (type_is_flexible(b)) { return NULL; } return promote_flexible(ctx, b, a); } assert(!type_is_flexible(a) && type_is_flexible(b)); if (type_dealias(ctx, a)->storage == STORAGE_TAGGED) { const struct type *tag = NULL; for (const struct type_tagged_union *tu = &type_dealias(ctx, a)->tagged; tu; tu = tu->next) { const struct type *p = promote_flexible(ctx, tu->type, b); if (!p) { lower_flexible(ctx, b, tag); continue; } if (tag) { // Ambiguous b = lower_flexible(ctx, b, NULL); if (type_is_assignable(ctx, a, b)) { return b; } return NULL; } tag = p; } return tag; } switch (b->storage) { case STORAGE_FCONST: if (!type_is_float(ctx, a)) { return NULL; } lower_flexible(ctx, b, a); return a; case STORAGE_ICONST: if (!type_is_integer(ctx, a)) { return NULL; } if (type_is_signed(ctx, a) && min_value(ctx, a) > b->flexible.min) { return NULL; } if (b->flexible.max > 0 && max_value(ctx, a) < (uint64_t)b->flexible.max) { return NULL; } lower_flexible(ctx, b, a); return a; case STORAGE_RCONST: if (type_dealias(ctx, a)->storage == STORAGE_RUNE) { lower_flexible(ctx, b, a); return a; } if (!type_is_integer(ctx, a)) { return NULL; } if (max_value(ctx, a) < (uint64_t)b->flexible.max) { return NULL; } lower_flexible(ctx, b, a); return a; default: assert(0); // Invariant } } bool tagged_subset_compat(struct context *ctx, const struct type *superset, const struct type *subset) { // Note: this implementation depends on the invariant that tagged union // member types are sorted by their type ID. superset = type_dealias(ctx, superset), subset = type_dealias(ctx, subset); if (superset->storage != STORAGE_TAGGED || subset->storage != STORAGE_TAGGED) { return false; } const struct type_tagged_union *superset_tu = &superset->tagged, *subset_tu = &subset->tagged; while (subset_tu && superset_tu) { while (superset_tu) { if (superset_tu->type->id == subset_tu->type->id) { subset_tu = subset_tu->next; superset_tu = superset_tu->next; break; } superset_tu = superset_tu->next; } } return !subset_tu; } static bool struct_subtype(struct context *ctx, const struct type *to, const struct type *from) { from = type_dealias(ctx, from); if (from->storage != STORAGE_STRUCT) { return false; } for (struct struct_field *f = from->struct_union.fields; f; f = f->next) { if (f->offset == 0) { return f->type == to || struct_subtype(ctx, to, type_dealias(ctx, f->type)); } } return false; } bool type_is_assignable(struct context *ctx, const struct type *to, const struct type *from) { const struct type *to_orig = to, *from_orig = from; if (type_dealias(ctx, to)->storage != STORAGE_TAGGED) { to = type_dealias(ctx, to); from = type_dealias(ctx, from); } // const and non-const types are mutually assignable struct type _to, _from; to = strip_flags(to, &_to), from = strip_flags(from, &_from); if (to->id == from->id && to->storage != STORAGE_VOID && to->storage != STORAGE_DONE) { return true; } if (from->storage == STORAGE_ERROR || from->storage == STORAGE_NEVER) { return true; } if (type_is_flexible(from)) { return promote_flexible(ctx, to_orig, from_orig); } struct type _to_secondary, _from_secondary; const struct type *to_secondary, *from_secondary; switch (to->storage) { case STORAGE_FCONST: case STORAGE_ICONST: case STORAGE_RCONST: return promote_flexible(ctx, to_orig, from_orig); case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: return type_is_integer(ctx, from) && type_is_signed(ctx, from) && to->size >= from->size; case STORAGE_SIZE: case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: return type_is_integer(ctx, from) && !type_is_signed(ctx, from) && to->size >= from->size; case STORAGE_F64: return type_is_float(ctx, from); case STORAGE_POINTER: to_secondary = to->pointer.referent; to_secondary = strip_flags(to_secondary, &_to_secondary); switch (from->storage) { case STORAGE_NULL: return to->pointer.flags & PTR_NULLABLE; case STORAGE_POINTER: from_secondary = from->pointer.referent; from_secondary = strip_flags(from_secondary, &_from_secondary); if (struct_subtype(ctx, to_secondary, from_secondary)) { return true; } switch (to_secondary->storage) { case STORAGE_OPAQUE: break; case STORAGE_ARRAY: if (!type_is_assignable(ctx, to_secondary, from_secondary)) { return false; } break; default: if (to_secondary->id != from_secondary->id) { return false; } break; } if (from->pointer.flags & PTR_NULLABLE) { return to->pointer.flags & PTR_NULLABLE; } return true; default: return false; } assert(0); // Unreachable case STORAGE_ALIAS: assert(to->alias.type); return type_is_assignable(ctx, to->alias.type, from); case STORAGE_VOID: case STORAGE_DONE: return to_orig->id == from_orig->id && (from_orig->flags & TYPE_ERROR) == (to_orig->flags & TYPE_ERROR); case STORAGE_SLICE: if (from->storage == STORAGE_POINTER) { from = type_dealias(ctx, from->pointer.referent); if (from->storage != STORAGE_ARRAY) { return false; } } if (from->storage != STORAGE_SLICE && (from->storage != STORAGE_ARRAY || from->array.length == SIZE_UNDEFINED)) { return false; } to_secondary = strip_flags( to->array.members, &_to_secondary); from_secondary = strip_flags( from->array.members, &_from_secondary); if (to_secondary->storage == STORAGE_OPAQUE) { return true; } return to_secondary->id == from_secondary->id; case STORAGE_ARRAY: if (from->storage != STORAGE_ARRAY) { return false; } if (from->array.expandable) { return to->array.length != SIZE_UNDEFINED && to->array.length >= from->array.length && to->array.members == from->array.members; } else { return to->array.length == SIZE_UNDEFINED && to->array.members == from->array.members; } case STORAGE_TAGGED: return tagged_select_subtype(ctx, to, from_orig, true) != NULL || tagged_subset_compat(ctx, to, from); // The following types are only assignable from themselves, and are // handled above: case STORAGE_BOOL: case STORAGE_ENUM: case STORAGE_F32: case STORAGE_FUNCTION: case STORAGE_NEVER: case STORAGE_NULL: case STORAGE_OPAQUE: case STORAGE_RUNE: case STORAGE_STRING: case STORAGE_STRUCT: case STORAGE_TUPLE: case STORAGE_UINTPTR: case STORAGE_UNION: case STORAGE_VALIST: return false; case STORAGE_ERROR: return true; } assert(0); // Unreachable } static const struct type * is_castable_with_tagged(struct context *ctx, const struct type *to, const struct type *from) { if (type_dealias(ctx, from)->storage == STORAGE_TAGGED && type_dealias(ctx, to)->storage == STORAGE_TAGGED) { if (tagged_subset_compat(ctx, to, from) || tagged_subset_compat(ctx, from, to)) { return to; } } if (type_dealias(ctx, to)->storage == STORAGE_TAGGED) { const struct type *subtype = tagged_select_subtype(ctx, to, from, true); if (subtype != NULL) { return subtype; } } if (type_dealias(ctx, from)->storage == STORAGE_TAGGED) { const struct type *subtype = tagged_select_subtype(ctx, from, to, true); if (subtype != NULL) { return subtype; } } return NULL; } const struct type * type_is_castable(struct context *ctx, const struct type *to, const struct type *from) { if (to->storage == STORAGE_VOID || to->storage == STORAGE_DONE) { if (type_is_flexible(from)) { lower_flexible(ctx, from, NULL); } return to; } else if (to->storage == STORAGE_ERROR) { return to; } if (type_dealias(ctx, from)->storage == STORAGE_TAGGED || type_dealias(ctx, to)->storage == STORAGE_TAGGED) { return is_castable_with_tagged(ctx, to, from); } const struct type *to_orig = to, *from_orig = from; to = type_dealias(ctx, to), from = type_dealias(ctx, from); if (to == from) { return to_orig; } struct type _to, _from; to = strip_flags(to, &_to), from = strip_flags(from, &_from); if (to->id == from->id) { return to_orig; } if ((!type_is_flexible(from) && from->size == SIZE_UNDEFINED) || (!type_is_flexible(to) && to->size == SIZE_UNDEFINED)) { return NULL; } switch (from->storage) { case STORAGE_ICONST: switch (to->storage) { case STORAGE_F32: case STORAGE_F64: lower_flexible(ctx, from, NULL); return to_orig; case STORAGE_RUNE: lower_flexible(ctx, from, &builtin_type_u32); return to_orig; default: return promote_flexible(ctx, from_orig, to_orig); } break; case STORAGE_FCONST: if (type_is_integer(ctx, to)) { lower_flexible(ctx, from, NULL); return to_orig; } // fallthrough case STORAGE_RCONST: return promote_flexible(ctx, from_orig, to_orig); case STORAGE_I8: case STORAGE_I16: case STORAGE_I32: case STORAGE_I64: case STORAGE_INT: case STORAGE_SIZE: case STORAGE_U8: case STORAGE_U16: case STORAGE_U32: case STORAGE_U64: case STORAGE_UINT: return to->storage == STORAGE_ENUM || type_is_numeric(ctx, to) || to->storage == STORAGE_RUNE ? to_orig : NULL; case STORAGE_RUNE: return type_is_integer(ctx, to) ? to_orig : NULL; case STORAGE_ENUM: return to->storage == STORAGE_ENUM || type_is_integer(ctx, from) ? to_orig : NULL; case STORAGE_F32: case STORAGE_F64: return type_is_numeric(ctx, to) ? to_orig : NULL; case STORAGE_UINTPTR: return to->storage == STORAGE_POINTER || to->storage == STORAGE_NULL || type_is_numeric(ctx, to) || to->storage == STORAGE_ENUM ? to_orig : NULL; case STORAGE_POINTER: return to->storage == STORAGE_POINTER || to->storage == STORAGE_NULL || to->storage == STORAGE_UINTPTR ? to_orig : NULL; case STORAGE_NULL: return to->storage == STORAGE_POINTER || to->storage == STORAGE_UINTPTR ? to_orig : NULL; case STORAGE_SLICE: return to->storage == STORAGE_SLICE || (to->storage == STORAGE_POINTER && to->pointer.referent->storage == STORAGE_ARRAY) ? to_orig : NULL; case STORAGE_ARRAY: return to->storage == STORAGE_ARRAY || to->storage == STORAGE_SLICE ? to_orig : NULL; // Cannot be cast: case STORAGE_STRING: case STORAGE_BOOL: case STORAGE_VOID: case STORAGE_DONE: case STORAGE_OPAQUE: case STORAGE_FUNCTION: case STORAGE_TUPLE: case STORAGE_STRUCT: case STORAGE_UNION: case STORAGE_VALIST: case STORAGE_NEVER: return NULL; case STORAGE_ERROR: case STORAGE_TAGGED: case STORAGE_ALIAS: assert(0); // Handled above } assert(0); // Unreachable } void builtin_types_init(const char *target) { if (strcmp(target, "aarch64") == 0) { builtin_type_int.size = 4; builtin_type_int.align = 4; builtin_type_uint.size = 4; builtin_type_uint.align = 4; builtin_type_uintptr.size = 8; builtin_type_uintptr.align = 8; builtin_type_null.size = 8; builtin_type_null.align = 8; builtin_type_size.size = 8; builtin_type_size.align = 8; builtin_type_const_int.size = 4; builtin_type_const_int.align = 4; builtin_type_const_uint.size = 4; builtin_type_const_uint.align = 4; builtin_type_const_uintptr.size = 8; builtin_type_const_uintptr.align = 8; builtin_type_const_size.size = 8; builtin_type_const_size.align = 8; builtin_type_str.size = 24; builtin_type_str.align = 8; builtin_type_const_str.size = 24; builtin_type_const_str.align = 8; builtin_type_valist.size = 32; builtin_type_valist.align = 8; } else if (strcmp(target, "riscv64") == 0) { builtin_type_int.size = 4; builtin_type_int.align = 4; builtin_type_uint.size = 4; builtin_type_uint.align = 4; builtin_type_uintptr.size = 8; builtin_type_uintptr.align = 8; builtin_type_null.size = 8; builtin_type_null.align = 8; builtin_type_size.size = 8; builtin_type_size.align = 8; builtin_type_const_int.size = 4; builtin_type_const_int.align = 4; builtin_type_const_uint.size = 4; builtin_type_const_uint.align = 4; builtin_type_const_uintptr.size = 8; builtin_type_const_uintptr.align = 8; builtin_type_const_size.size = 8; builtin_type_const_size.align = 8; builtin_type_str.size = 24; builtin_type_str.align = 8; builtin_type_const_str.size = 24; builtin_type_const_str.align = 8; builtin_type_valist.size = 8; builtin_type_valist.align = 8; } else if (strcmp(target, "x86_64") == 0) { builtin_type_int.size = 4; builtin_type_int.align = 4; builtin_type_uint.size = 4; builtin_type_uint.align = 4; builtin_type_uintptr.size = 8; builtin_type_uintptr.align = 8; builtin_type_null.size = 8; builtin_type_null.align = 8; builtin_type_size.size = 8; builtin_type_size.align = 8; builtin_type_const_int.size = 4; builtin_type_const_int.align = 4; builtin_type_const_uint.size = 4; builtin_type_const_uint.align = 4; builtin_type_const_uintptr.size = 8; builtin_type_const_uintptr.align = 8; builtin_type_const_size.size = 8; builtin_type_const_size.align = 8; builtin_type_str.size = 24; builtin_type_str.align = 8; builtin_type_const_str.size = 24; builtin_type_const_str.align = 8; builtin_type_valist.size = 24; builtin_type_valist.align = 8; } else { xfprintf(stderr, "Unsupported or unrecognized target: %s", target); exit(EXIT_USER); } struct type *builtins[] = { &builtin_type_bool, &builtin_type_error, &builtin_type_f32, &builtin_type_f64, &builtin_type_i8, &builtin_type_i16, &builtin_type_i32, &builtin_type_i64, &builtin_type_int, &builtin_type_u8, &builtin_type_u16, &builtin_type_u32, &builtin_type_u64, &builtin_type_uint, &builtin_type_uintptr, &builtin_type_null, &builtin_type_rune, &builtin_type_size, &builtin_type_void, &builtin_type_done, &builtin_type_const_bool, &builtin_type_const_f32, &builtin_type_const_f64, &builtin_type_const_i8, &builtin_type_const_i16, &builtin_type_const_i32, &builtin_type_const_i64, &builtin_type_const_int, &builtin_type_const_u8, &builtin_type_const_u16, &builtin_type_const_u32, &builtin_type_const_u64, &builtin_type_const_uint, &builtin_type_const_uintptr, &builtin_type_const_rune, &builtin_type_const_size, &builtin_type_const_void, &builtin_type_const_done, &builtin_type_str, &builtin_type_const_str, &builtin_type_valist, }; for (size_t i = 0; i < sizeof(builtins) / sizeof(builtins[0]); ++i) { builtins[i]->id = type_hash(builtins[i]); } } // Built-in type singletons struct type builtin_type_bool = { .storage = STORAGE_BOOL, .size = 1, .align = 1, }, builtin_type_error = { .storage = STORAGE_ERROR, .size = 0, .align = 0, }, builtin_type_f32 = { .storage = STORAGE_F32, .size = 4, .align = 4, }, builtin_type_f64 = { .storage = STORAGE_F64, .size = 8, .align = 8, }, builtin_type_i8 = { .storage = STORAGE_I8, .size = 1, .align = 1, }, builtin_type_i16 = { .storage = STORAGE_I16, .size = 2, .align = 2, }, builtin_type_i32 = { .storage = STORAGE_I32, .size = 4, .align = 4, }, builtin_type_i64 = { .storage = STORAGE_I64, .size = 8, .align = 8, }, builtin_type_int = { .storage = STORAGE_INT, }, builtin_type_never = { .storage = STORAGE_NEVER, .size = SIZE_UNDEFINED, .align = ALIGN_UNDEFINED, }, builtin_type_opaque = { .storage = STORAGE_OPAQUE, .size = SIZE_UNDEFINED, .align = ALIGN_UNDEFINED, }, builtin_type_u8 = { .storage = STORAGE_U8, .size = 1, .align = 1, }, builtin_type_u16 = { .storage = STORAGE_U16, .size = 2, .align = 2, }, builtin_type_u32 = { .storage = STORAGE_U32, .size = 4, .align = 4, }, builtin_type_u64 = { .storage = STORAGE_U64, .size = 8, .align = 8, }, builtin_type_uint = { .storage = STORAGE_UINT, }, builtin_type_uintptr = { .storage = STORAGE_UINTPTR, }, builtin_type_null = { .storage = STORAGE_NULL, }, builtin_type_rune = { .storage = STORAGE_RUNE, .size = 4, .align = 4, }, builtin_type_size = { .storage = STORAGE_SIZE, }, builtin_type_void = { .storage = STORAGE_VOID, .size = 0, .align = 0, }, builtin_type_done = { .storage = STORAGE_DONE, .size = 0, .align = 0, }, builtin_type_const_bool = { .storage = STORAGE_BOOL, .flags = TYPE_CONST, .size = 1, .align = 1, }, builtin_type_const_f32 = { .storage = STORAGE_F32, .flags = TYPE_CONST, .size = 4, .align = 4, }, builtin_type_const_f64 = { .storage = STORAGE_F64, .flags = TYPE_CONST, .size = 8, .align = 8, }, builtin_type_const_i8 = { .storage = STORAGE_I8, .flags = TYPE_CONST, .size = 1, .align = 1, }, builtin_type_const_i16 = { .storage = STORAGE_I16, .flags = TYPE_CONST, .size = 2, .align = 2, }, builtin_type_const_i32 = { .storage = STORAGE_I32, .flags = TYPE_CONST, .size = 4, .align = 4, }, builtin_type_const_i64 = { .storage = STORAGE_I64, .flags = TYPE_CONST, .size = 8, .align = 8, }, builtin_type_const_int = { .storage = STORAGE_INT, .flags = TYPE_CONST, }, builtin_type_const_never = { .storage = STORAGE_NEVER, .flags = TYPE_CONST, .size = SIZE_UNDEFINED, .align = ALIGN_UNDEFINED, }, builtin_type_const_opaque = { .storage = STORAGE_OPAQUE, .flags = TYPE_CONST, .size = SIZE_UNDEFINED, .align = ALIGN_UNDEFINED, }, builtin_type_const_u8 = { .storage = STORAGE_U8, .flags = TYPE_CONST, .size = 1, .align = 1, }, builtin_type_const_u16 = { .storage = STORAGE_U16, .flags = TYPE_CONST, .size = 2, .align = 2, }, builtin_type_const_u32 = { .storage = STORAGE_U32, .flags = TYPE_CONST, .size = 4, .align = 4, }, builtin_type_const_u64 = { .storage = STORAGE_U64, .flags = TYPE_CONST, .size = 8, .align = 8, }, builtin_type_const_uint = { .storage = STORAGE_UINT, .flags = TYPE_CONST, }, builtin_type_const_uintptr = { .storage = STORAGE_UINTPTR, .flags = TYPE_CONST, }, builtin_type_const_rune = { .storage = STORAGE_RUNE, .flags = TYPE_CONST, .size = 4, .align = 4, }, builtin_type_const_size = { .storage = STORAGE_SIZE, .flags = TYPE_CONST, }, builtin_type_const_void = { .storage = STORAGE_VOID, .flags = TYPE_CONST, .size = 0, .align = 0, }, builtin_type_const_done = { .storage = STORAGE_DONE, .flags = TYPE_CONST, .size = 0, .align = 0, }; // Others struct type builtin_type_str = { .storage = STORAGE_STRING, }, builtin_type_const_str = { .storage = STORAGE_STRING, .flags = TYPE_CONST, }, builtin_type_valist = { .storage = STORAGE_VALIST, }; harec-0.24.2/src/utf8.c000066400000000000000000000034731464473277600145350ustar00rootroot00000000000000#include #include #include "utf8.h" static const uint8_t masks[] = { 0x7F, 0x1F, 0x0F, 0x07, 0x03, 0x01 }; static const struct { uint8_t mask; uint8_t result; int octets; } sizes[] = { { 0x80, 0x00, 1 }, { 0xE0, 0xC0, 2 }, { 0xF0, 0xE0, 3 }, { 0xF8, 0xF0, 4 }, { 0xFC, 0xF8, 5 }, { 0xFE, 0xFC, 6 }, { 0x80, 0x80, -1 }, }; static int utf8_size(uint8_t c) { for (size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); ++i) { if ((c & sizes[i].mask) == sizes[i].result) { return sizes[i].octets; } } return -1; } uint32_t utf8_decode(const char **char_str) { const uint8_t **s = (const uint8_t **)char_str; uint32_t cp = 0; if (**s < 128) { // shortcut cp = **s; ++*s; return cp; } int size = utf8_size(**s); if (size == -1) { ++*s; return UTF8_INVALID; } uint8_t mask = masks[size - 1]; cp = **s & mask; ++*s; while (--size) { uint8_t c = **s; ++*s; if ((c >> 6) != 0x02) return UTF8_INVALID; cp <<= 6; cp |= c & 0x3f; } return cp; } size_t utf8_encode(char *str, uint32_t ch) { size_t len = 0; uint8_t first; if (ch < 0x80) { first = 0; len = 1; } else if (ch < 0x800) { first = 0xc0; len = 2; } else if (ch < 0x10000) { first = 0xe0; len = 3; } else { first = 0xf0; len = 4; } for (size_t i = len - 1; i > 0; --i) { str[i] = (ch & 0x3f) | 0x80; ch >>= 6; } str[0] = ch | first; return len; } uint32_t utf8_get(FILE *f) { char buffer[UTF8_MAX_SIZE]; int c = fgetc(f); if (c == EOF) { return UTF8_INVALID; } buffer[0] = (char)c; int size = utf8_size(c); if (size > UTF8_MAX_SIZE) { fseek(f, size - 1, SEEK_CUR); return UTF8_INVALID; } if (size > 1) { int amt = fread(&buffer[1], 1, size - 1, f); if (amt != size - 1) { return UTF8_INVALID; } } const char *ptr = buffer; return utf8_decode(&ptr); } harec-0.24.2/src/util.c000066400000000000000000000061711464473277600146220ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "util.h" // Remove safety macros: #undef malloc #undef calloc #undef realloc #undef strdup const char **sources; size_t nsources; uint32_t fnv1a(uint32_t hash, unsigned char c) { return (hash ^ c) * 16777619; } uint32_t fnv1a_u32(uint32_t hash, uint32_t u32) { hash = fnv1a(hash, (u32) & 0xFF); hash = fnv1a(hash, (u32 >> 8) & 0xFF); hash = fnv1a(hash, (u32 >> 16) & 0xFF); hash = fnv1a(hash, (u32 >> 24) & 0xFF); return hash; } uint32_t fnv1a_u64(uint32_t hash, uint64_t u64) { hash = fnv1a(hash, (u64) & 0xFF); hash = fnv1a(hash, (u64 >> 8) & 0xFF); hash = fnv1a(hash, (u64 >> 16) & 0xFF); hash = fnv1a(hash, (u64 >> 24) & 0xFF); hash = fnv1a(hash, (u64 >> 32) & 0xFF); hash = fnv1a(hash, (u64 >> 40) & 0xFF); hash = fnv1a(hash, (u64 >> 48) & 0xFF); hash = fnv1a(hash, (u64 >> 56) & 0xFF); return hash; } uint32_t fnv1a_size(uint32_t hash, size_t sz) { for (size_t i = 0; i < sizeof(sz); i++) { hash = fnv1a(hash, sz & 0xFF); sz >>= 8; } return hash; } uint32_t fnv1a_s(uint32_t hash, const char *str) { unsigned char c; while ((c = *str++)) { hash = fnv1a(hash, c); } return hash; } int xfprintf(FILE *restrict f, const char *restrict fmt, ...) { va_list ap; va_start(ap, fmt); int n = xvfprintf(f, fmt, ap); va_end(ap); return n; } int xvfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) { int n = vfprintf(f, fmt, ap); if (f != stderr && n < 0) { perror("fprintf"); exit(EXIT_ABNORMAL); } return n; } void * xcalloc(size_t n, size_t s) { void *p = calloc(n, s); if (!p && s) { abort(); } return p; } void * xrealloc(void *p, size_t s) { p = realloc(p, s); if (!p && s) { abort(); } return p; } char * xstrdup(const char *s) { char *ret = strdup(s); if (!ret) { abort(); } return ret; } char * gen_name(int *id, const char *fmt) { int n = snprintf(NULL, 0, fmt, *id); char *str = xcalloc(1, n + 1); snprintf(str, n + 1, fmt, *id); ++*id; return str; } void errline(struct location loc) { const char *path = sources[loc.file]; struct stat filestat; if (stat(path, &filestat) == -1 || !S_ISREG(filestat.st_mode)) { return; } FILE *src = fopen(path, "r"); if (!src) { return; } char *line = NULL; size_t sz = 0; ssize_t len; int n = 0; while (n < loc.lineno) { if ((len = getline(&line, &sz, src)) == -1) { fclose(src); free(line); return; } n += 1; } if (line) { bool color = true; const char *no_color = getenv("NO_COLOR"); const char *harec_color = getenv("HAREC_COLOR"); if (harec_color) { color = strcmp(harec_color, "0") != 0; } else if ((no_color && *no_color != '\0') || !isatty(fileno(stderr))) { color = false; } xfprintf(stderr, "\n"); int lineno_width = xfprintf(stderr, "%d", loc.lineno); xfprintf(stderr, " |\t%s%s%*c |\t%*c", line, line[len - 1] == '\n' ? "" : "\n", lineno_width, ' ', loc.colno - 1, ' '); if (color) { xfprintf(stderr, "\x1b[31m^\x1b[0m\n\n"); } else { xfprintf(stderr, "^\n\n"); } free(line); } fclose(src); } harec-0.24.2/testmod/000077500000000000000000000000001464473277600143645ustar00rootroot00000000000000harec-0.24.2/testmod/measurement.ha000066400000000000000000000001451464473277600172230ustar00rootroot00000000000000use rt; // must take measurement of something in subunit scope def SIZE_RT_SLICE = size(rt::slice); harec-0.24.2/testmod/testmod.ha000066400000000000000000000015321464473277600163560ustar00rootroot00000000000000export type _enum = enum { ONE = 1, TWO = 2, THREE = 3, }; export type other = enum { // purposefully something that doesn't exist in _enum EIGHT = 8: _enum, }; export type enum_alias = _enum; export type error_enum = !_enum; export type rune_enum = enum rune { SEMICOLON = ';', }; // used for a test in tests/15-enums.ha // this is kinda a hack; it simulates a declaration in a module whose namespace // has multiple components (in this case testmod::x) // as of now this relies on unspecified details of harec export type testmod::x::namespaced_alias = _enum; export def val = 42; export def val2: int = 90; export def val3: enum_alias = 1: enum_alias; export let val4 = 69; export let @symbol("s_x") s_a: int; export let @symbol("s_y") s_b: int = 1; // ensure rt isn't imported in this subunit static assert(SIZE_RT_SLICE == size([]opaque)); harec-0.24.2/tests/000077500000000000000000000000001464473277600140475ustar00rootroot00000000000000harec-0.24.2/tests/00-literals.ha000066400000000000000000000426351464473277600164270ustar00rootroot00000000000000use rt::{compile, error, status, toutf8}; type my_enum = enum u8 { FOO, }; fn assignment() void = { let i = 0i8; let u = 0u64; let f = 0.0f64; let r = 'a'; let e = my_enum::FOO; // There are five cases that need to be tested for tagged unions: // - The default type for the literal is a member of the union // - A single non-default type the literal could assume is a member of // the union // - The default type for the literal along with at least one other // type the literal could assume are both members of the union // - At least two types the literal could assume are members of the // union, and the default type isn't a member of the union // - None of the types the literal could assume are members of the // union // All but the fourth and fifth case are valid, and the invalid cases // should error out gracefully. let itu1: (int | void) = void; let itu2: (u64 | void) = void; let itu3: (int | u64 | void) = void; let ftu1: (f64 | void) = void; let ftu2: (f32 | void) = void; let ftu3: (f32 | f64 | void) = void; let rtu1: (rune | void) = void; let rtu2: (u64 | void) = void; let rtu3: (u8 | void) = void; let rtu4: (rune | u64 | u8 | void) = void; i = 127; u = 18446744073709551615; e = 0; itu1 = 0; itu2 = 0; itu3 = 0; f = 0.0; ftu1 = 0.0; ftu2 = 0.0; ftu3 = 0.0; i = 'a'; u = 'a'; r = 'a'; e = 'a'; rtu1 = 'a'; rtu2 = '\u0100'; rtu3 = 'a'; rtu4 = 'a'; assert(rtu4 is rune); let u2: uint = 'a'; let u2: uintptr = 'a'; let z: size = 'a'; let v: void = void; v = void; v = v; let za: [0]int = []; za = []; let failures = [ "fn f() void = { let i = 0i8; i = 128; };", "fn f() void = { let u = 0u32; u = 4294967296; };", "fn f() void = { let f = 0.0f64; f = 0; };", "fn f() void = { let r = 'a'; r = 0; };", "type my_enum = enum u8 { FOO }; fn f() void = { let e: my_enum = my_enum::FOO; e = 256; };", "fn f() void = { let p: nullable *opaque = null; p = 0; };", "fn f() void = { let b = false; b = 0; };", "fn f() void = { let n = null; n = 0; };", "fn f() void = { let s: struct { i: int } = struct { i: int = 0 }; s = 0; };", "fn f() void = { let t = (0, 1); t = 0; };", "fn f() void = { let a = [0, 1]; a = 0; };", "fn f() void = { let s = \"\"; s = 0; };", "fn f() void = { let itu4: (u32 | u64 | void) = void; itu4 = 0; };", "fn f() void = { let itu5: (str | void) = void; itu5 = 0; };", "fn f() void = { let i = 0i8; i = 0.0; };", "fn f() void = { let u = 0u8; u = 0.0; };", "fn f() void = { let r = 'a'; r = 0.0; };", "type my_enum = enum u8 { FOO }; fn f() void = { let e: my_enum = my_enum::FOO; e = 0.0; };", "fn f() void = { let p: nullable *opaque = null; p = 0.0; };", "fn f() void = { let b = false; b = 0.0; };", "fn f() void = { let n = null; n = 0.0; };", "fn f() void = { let s: struct { i: int } = struct { i: int = 0 }; s = 0.0; };", "fn f() void = { let t = (0, 1); t = 0.0; };", "fn f() void = { let a = [0, 1]; a = 0.0; };", "fn f() void = { let s = \"\"; s = 0.0; };", "type my_f32 = f32; fn f() void = { let ftu4: (f32 | my_f32 | void) = void; ftu4 = 0.0; };", "fn f() void = { let ftu5: (str | void) = void; ftu5 = 0.0; };", "type my_f32 = f32; fn f() void = { let ftu4: (f32 | my_f32 | void) = void; ftu4 = 0.0; };", "fn f() void = { let ftu5: (str | void) = void; ftu5 = 0.0; };", "fn f() void = { let f = 0.0f64; f = 'a'; };", "fn f() void = { let p: nullable *opaque = null; p = 'a'; };", "fn f() void = { let b = false; b = 'a'; };", "fn f() void = { let n = null; n = 'a'; };", "fn f() void = { let s: struct { i: int } = struct { i: int = 0 }; s = 'a'; };", "fn f() void = { let t = (0, 1); t = 'a'; };", "fn f() void = { let a = [0, 1]; a = 'a'; };", "fn f() void = { let s = \"\"; s = 'a'; };", "fn f() void = { let rtu4: (u32 | u64 | void) = void; rtu4 = 'a'; };", "fn f() void = { let rtu5: (str | void) = void; rtu5 = 'a'; };", "fn f() void = { let i: u8 = '\u0100'; };", ]; for (let i = 0z; i < len(failures); i += 1) { compile(status::CHECK, failures[i])!; }; }; fn aggregates() void = { // Pointers // Kinda hacky way to verify that something has the expected type // The variables are necessary in order to avoid type hints, which would // avoid verifying that literals are lowered when entering aggregate // types let maxiptr = if (true) alloc(2147483647) else void; free(maxiptr as *int); let miniptr = if (true) alloc(-2147483648) else void; free(miniptr as *int); let smalli64ptr = if (true) alloc(2147483648) else void; free(smalli64ptr as *i64); let negi64ptr = if (true) alloc(-2147483649) else void; free(negi64ptr as *i64); let maxi64ptr = if (true) alloc(9223372036854775807) else void; free(maxi64ptr as *i64); // -9223372036854775808 can't be made to work without lots of hacks let mini64ptr = if (true) alloc(-9223372036854775807) else void; free(mini64ptr as *i64); let fptr = if (true) alloc(0.0) else void; free(fptr as *f64); let rptr = if (true) alloc('a') else void; free(rptr as *rune); // Tuples // The edge cases of the iconst lowering algorithm were already tested // above, and tuple items can't affect each other, so this suffices let tuple = if (true) (2147483647, 0.0, 'a') else void; tuple as (int, f64, rune); // Arrays let iarr = if (true) [0, 1, 2] else void; iarr as [3]int; let uarr = if (true) [0u8, 1, 2] else void; uarr as [3]u8; let u2arr = if (true) [0, 1u8, 2] else void; u2arr as [3]u8; }; fn numeric() void = { let want: [_]i64 = [ 42, 42, 42, 42, 42, 42, 42, 42, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 100, 100, 100, 100 ]; let i = [ // basics (42, 42i, 42i8, 42i16, 42i32, 42i64), // decimal (42e0, 42e0i, 42e0i8, 42e0i16, 42e0i32, 42e0i64), // with exp (42e00, 42e00i, 42e00i8, 42e00i16, 42e00i32, 42e00i64), // with leading zeros in exp (42e+0, 42e+0i, 42e+0i8, 42e+0i16, 42e+0i32, 42e+0i64), // with + in exp (42e+00, 42e+00i, 42e+00i8, 42e+00i16, 42e+00i32, 42e+00i64), // with + and leading zeros in exp (0b101010, 0b101010i, 0b101010i8, 0b101010i16, 0b101010i32, 0b101010i64), // binary (0o52, 0o52i, 0o52i8, 0o52i16, 0o52i32, 0o52i64), // octal (0x2a, 0x2ai, 0x2ai8, 0x2ai16, 0x2ai32, 0x2ai64), // hex // single digit (0, 0i, 0i8, 0i16, 0i32, 0i64), // zero (0b0, 0b0i, 0b0i8, 0b0i16, 0b0i32, 0b0i64), // binary (0o0, 0o0i, 0o0i8, 0o0i16, 0o0i32, 0o0i64), // octal (0x0, 0x0i, 0x0i8, 0x0i16, 0x0i32, 0x0i64), // hex (1, 1i, 1i8, 1i16, 1i32, 1i64), // nonzero (0b1, 0b1i, 0b1i8, 0b1i16, 0b1i32, 0b1i64), // binary (0o1, 0o1i, 0o1i8, 0o1i16, 0o1i32, 0o1i64), // octal (0x1, 0x1i, 0x1i8, 0x1i16, 0x1i32, 0x1i64), // hex // with leading zero (0b00, 0b00i, 0b00i8, 0b00i16, 0b00i32, 0b00i64), // binary (0o00, 0o00i, 0o00i8, 0o00i16, 0o00i32, 0o00i64), // octal (0x00, 0x00i, 0x00i8, 0x00i16, 0x00i32, 0x00i64), // hex (0b01, 0b01i, 0b01i8, 0b01i16, 0b01i32, 0b01i64), // binary with leading zero (0o01, 0o01i, 0o01i8, 0o01i16, 0o01i32, 0o01i64), // octal (0x01, 0x01i, 0x01i8, 0x01i16, 0x01i32, 0x01i64), // hex // exponents (1e2, 1e2i, 1e2i8, 1e2i16, 1e2i32, 1e2i64), (1e02, 1e02i, 1e02i8, 1e02i16, 1e02i32, 1e02i64), // with leading zeros in exp (1e+2, 1e+2i, 1e+2i8, 1e+2i16, 1e+2i32, 1e+2i64), // with + in exp (1e+02, 1e+02i, 1e+02i8, 1e+02i16, 1e+02i32, 1e+02i64), // with + and leading zeros in exp ]; for (let j = 0z; j < len(i); j += 1) { let t = &i[j]; assert(want[j] == t.0 && t.0 == t.1 && t.1 == t.2 && t.2 == t.3 && t.3 == t.4 && t.4 == t.5); }; let u = [ // basics (42z, 42u, 42u8, 42u16, 42u32, 42u64), // decimal (42e0z, 42e0u, 42e0u8, 42e0u16, 42e0u32, 42e0u64), // with exp (42e00z, 42e00u, 42e00u8, 42e00u16, 42e00u32, 42e00u64), // with leading zeros in exp (42e+0z, 42e+0u, 42e+0u8, 42e+0u16, 42e+0u32, 42e+0u64), // with + in exp (42e+00z, 42e+00u, 42e+00u8, 42e+00u16, 42e+00u32, 42e+00u64), // with + and leading zeros in exp (0b101010z, 0b101010u, 0b101010u8, 0b101010u16, 0b101010u32, 0b101010u64), // binary (0o52z, 0o52u, 0o52u8, 0o52u16, 0o52u32, 0o52u64), // octal (0x2az, 0x2au, 0x2au8, 0x2au16, 0x2au32, 0x2au64), // hex // single digit (0z, 0u, 0u8, 0u16, 0u32, 0u64), // zero (0b0z, 0b0u, 0b0u8, 0b0u16, 0b0u32, 0b0u64), // binary (0o0z, 0o0u, 0o0u8, 0o0u16, 0o0u32, 0o0u64), // octal (0x0z, 0x0u, 0x0u8, 0x0u16, 0x0u32, 0x0u64), // hex (1z, 1u, 1u8, 1u16, 1u32, 1u64), // nonzero (0b1z, 0b1u, 0b1u8, 0b1u16, 0b1u32, 0b1u64), // binary (0o1z, 0o1u, 0o1u8, 0o1u16, 0o1u32, 0o1u64), // octal (0x1z, 0x1u, 0x1u8, 0x1u16, 0x1u32, 0x1u64), // hex // with leading zero (0b00z, 0b00u, 0b00u8, 0b00u16, 0b00u32, 0b00u64), // binary (0o00z, 0o00u, 0o00u8, 0o00u16, 0o00u32, 0o00u64), // octal (0x00z, 0x00u, 0x00u8, 0x00u16, 0x00u32, 0x00u64), // hex (0b01z, 0b01u, 0b01u8, 0b01u16, 0b01u32, 0b01u64), // binary with leading zero (0o01z, 0o01u, 0o01u8, 0o01u16, 0o01u32, 0o01u64), // octal (0x01z, 0x01u, 0x01u8, 0x01u16, 0x01u32, 0x01u64), // hex // exponents (1e2z, 1e2u, 1e2u8, 1e2u16, 1e2u32, 1e2u64), (1e02z, 1e02u, 1e02u8, 1e02u16, 1e02u32, 1e02u64), // with leading zeros in exp (1e+2z, 1e+2u, 1e+2u8, 1e+2u16, 1e+2u32, 1e+2u64), // with + in exp (1e+02z, 1e+02u, 1e+02u8, 1e+02u16, 1e+02u32, 1e+02u64), // with + and leading zeros in exp ]; for (let j = 0z; j < len(u); j += 1) { let t = &u[j]; assert(want[j]: u64 == t.0: u64 && t.0: u64 == t.1 && t.1 == t.2 && t.2 == t.3 && t.3 == t.4 && t.4 == t.5); }; let f = [0.0, 0.00, 0.0e0, 0.00e0, 0.0e1, 0.00e1, 0.0e+0, 0.0e+1, 0.0e-0, 0.0e00, 0.0e01, 0.0e+01, 0.0e+00, 0.0e-00, 0e-0, 0e-00, 0e-1, 0e-01, 0x0p0, 0x0p1, 0x0p-1, 0x0p+1, 0x0.0p0, 0x0.00p0, 0x0.0p1, 0x0.00p1, 0x0.0p+0, 0x0.0p+1, 0x0.0p-0, 0x0.0p00, 0x0.0p01, 0x0.0p+01, 0x0.0p+00, 0x0.0p-00, 0x0p-0, 0x0p-00, 0x0p-1, 0x0p-01, 0.00_00]; for (let j = 0z; j < len(f); j+= 1) { assert(f[j] == 0.0); }; let _f32 = [0.0f32, 0.00f32, 0.0e0f32, 0.00e0f32, 0.0e1f32, 0.00e1f32, 0.0e+0f32, 0.0e+1f32, 0.0e-0f32, 0.0e00f32, 0.0e01f32, 0.0e+01f32, 0.0e+00f32, 0.0e-00, 0f32, 0e0f32, 0e1f32, 0e00f32, 0e01f32, 0e+0f32, 0e+00f32, 0e+1f32, 0e+01f32, 0e-0f32, 0e-00f32, 0e-1f32, 0e-01f32, 0x0p0f32, 0x0p1f32, 0x0p-1f32, 0x0p+1f32, 0x0.0p0f32, 0x0.00p0f32, 0x0.0p1f32, 0x0.00p1f32, 0x0.0p+0f32, 0x0.0p+1f32, 0x0.0p-0f32, 0x0.0p00f32, 0x0.0p01f32, 0x0.0p+01f32, 0x0.0p+00f32, 0x0.0p-00, 0x0p0f32, 0x0p1f32, 0x0p00f32, 0x0p01f32, 0x0p+0f32, 0x0p+00f32, 0x0p+1f32, 0x0p+01f32, 0x0p-0f32, 0x0p-00f32, 0x0p-1f32, 0x0p-01f32]; for (let j = 0z; j < len(_f32); j+= 1) { assert(_f32[j] == 0f32); }; let _f64 = [0.0f64, 0.00f64, 0.0e0f64, 0.00e0f64, 0.0e1f64, 0.00e1f64, 0.0e+0f64, 0.0e+1f64, 0.0e-0f64, 0.0e00f64, 0.0e01f64, 0.0e+01f64, 0.0e+00f64, 0.0e-00, 0f64, 0e0f64, 0e1f64, 0e00f64, 0e01f64, 0e+0f64, 0e+00f64, 0e+1f64, 0e+01f64, 0e-0f64, 0e-00f64, 0e-1f64, 0e-01f64, 0x0p0, 0x0p1, 0x0p-1, 0x0p+1, 0x0.0p0f64, 0x0.00p0f64, 0x0.0p1f64, 0x0.00p1f64, 0x0.0p+0f64, 0x0.0p+1f64, 0x0.0p-0f64, 0x0.0p00f64, 0x0.0p01f64, 0x0.0p+01f64, 0x0.0p+00f64, 0x0.0p-00, 0x0p0f64, 0x0p1f64, 0x0p00f64, 0x0p01f64, 0x0p+0f64, 0x0p+00f64, 0x0p+1f64, 0x0p+01f64, 0x0p-0f64, 0x0p-00f64, 0x0p-1f64, 0x0p-01f64]; for (let j = 0z; j < len(_f64); j+= 1) { assert(_f64[j] == 0f64); }; // capitalized exponent markers assert(0x0P0 == 0.0); assert(0E0 == 0); // separators assert(1_000 == 1000); assert(1_000_000 == 1000000); assert(1_0 == 10); assert(0xAB_CD == 0xABCD); assert(0b1_0_0_1 == 0b1001); assert(0o542_11 == 0o54211); assert(1_6e2 == 16e2); assert(1_000u32 == 1000u32); assert(0x1B_AD_C0_DEu32 == 0x1BADC0DE); assert(1_000.0f32 == 1000f32); assert(0.00_01 == 0.0001); assert(1_00.00_1 == 100.001); assert(1_6.0e2 == 16.0e2); assert(1_6e-2 == 16e-2); // double tuple subscript special case let tup = (('a', 'b'), 'c'); assert(tup.0.0 == 'a'); // exponents assert(tup.0e0.0 == 'a'); assert(tup.0.0e0 == 'a'); assert(tup.0e0.0e0 == 'a'); assert(tup.0e+0.0 == 'a'); assert(tup.0.0e+0 == 'a'); assert(tup.0e+0.0e+0 == 'a'); // signed assert(tup.0i.0 == 'a'); assert(tup.0.0i == 'a'); assert(tup.0i.0i == 'a'); assert(tup.0i32.0 == 'a'); assert(tup.0.0i32 == 'a'); assert(tup.0i32.0i32 == 'a'); // unsigned assert(tup.0u.0 == 'a'); assert(tup.0.0u == 'a'); assert(tup.0u.0u == 'a'); assert(tup.0u32.0 == 'a'); assert(tup.0.0u32 == 'a'); assert(tup.0u32.0u32 == 'a'); // bases assert(tup.0b0.0 == 'a'); assert(tup.0.0b0 == 'a'); assert(tup.0b0.0b0 == 'a'); assert(tup.0o0.0 == 'a'); assert(tup.0.0o0 == 'a'); assert(tup.0o0.0o0 == 'a'); assert(tup.0x0.0 == 'a'); assert(tup.0.0x0 == 'a'); assert(tup.0x0.0x0 == 'a'); // tuple with separator let tup = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'); assert(tup.1_0 == 'k'); // zero with large exponent assert(0e10000000 == 0); assert(0e010000000 == 0); assert(0e+10000000 == 0); assert(0e+010000000 == 0); // f32 and f64 are valid hex literals assert(0xf32 == 3890); assert(0xf64 == 3940); assert(0x1f32 == 7986); assert(0x1f64 == 8036); assert(0xf321 == 62241); assert(0xf641 == 63041); // e is a valid hex digit assert(0xe == 14); assert(0xe+1 == 15); assert(0xe-1 == 13); assert(0x1e == 30); assert(0x1e+1 == 31); assert(0x1e-1 == 29); assert(0x1e1 == 481); assert(0x1e1f32 == 1974066); let v = if (true) 5else 10; assert(v == 5); let invalid: [_]str = [ // invalid base "0b", "0o", "00b", "00o", "00x", "01b", "01o", "01x", "1b", "1o", "1x", "11b", "11o", "11x", // base with exponent "0b1e1", "0b1p1", "0o1e1", "0o1p1", "0be1", "0bp1" "0oe1", "0op1" "0xp1" // with +/- "0b1e+1", "0b1p+1", "0o1e+1", "0o1p+1", "0be+1", "0bp+1", "0oe+1", "0op+1", "0xp+1", "0b1e-1", "0b1p-1", "0o1e-1", "0o1p-1", "0be-1", "0bp-1", "0oe-1", "0op-1", "0xp-1", // invalid digits in smaller bases "0b41", "0b14", "0o82", "0o28", // leading zeroes "05", "00000010", "00.0", "01.0", "05e3", "00000010e3", "00.0e3", "01.0e3", "05e+3", "00000010e+3", "00.0e+3", "01.0e+3", "05e-3", "00000010e-3", "00.0e-3", "01.0e-3", "05p3", "00000010p3", "00.0p3", "01.0p3", "05p+3", "00000010p+3", "00.0p+3", "01.0p+3", "05p-3", "00000010p-3", "00.0p-3", "01.0p-3", "0_10", // invalid sequences of special characters "1.", "1..", "1..1", "1.1.", "1.1.1", "1e", "1e+", "1e-", "1p", "1p+", "1p-", "1e1+", "1e1-", "1p1+", "1p1-", "1ee", "1e+e", "1ee+", "1e+e+", "1e-e", "1ee-", "1e-e-", "1e+e-", "1e-e+", "1pp", "1p+p", "1pp+", "1p+p+", "1p-p", "1pp-", "1p-p-", "1p+p-", "1p-p+", "1ee1", "1e+e1", "1ee+1", "1e+e+1", "1e-e1", "1ee-1", "1e-e-1", "1e+e-1", "1e-e+1", "1pp1", "1p+p1", "1pp+1", "1p+p+1", "1p-p1", "1pp-1", "1p-p-1", "1p+p-1", "1p-p+1", "1e1e", "1e+1e", "1e1e+", "1e+1e+", "1e-1e", "1e1e-", "1e-1e-", "1e+1e-", "1e-1e+", "1p1p", "1p+1p", "1p1p+", "1p+1p+", "1p-1p", "1p1p-", "1p-1p-", "1p+1p-", "1p-1p+", "1e1e1", "1e+1e1", "1e1e+1", "1e+1e+1", "1e-1e1", "1e1e-1", "1e-1e-1", "1e+1e-1", "1e-1e+1", "1p1p1", "1p+1p1", "1p1p+1", "1p+1p+1", "1p-1p1", "1p1p-1", "1p-1p-1", "1p+1p-1", "1p-1p+1", "1.e", "1e.", "1.e1", "1e.1", "1.1e", "1e1.", "1e1.1", "1.p", "1p.", "1.p1", "1p.1", "1.1p", "1p1.", "1p1.1", "1.e+", "1e+.", "1.e+1", "1e+.1", "1.1e+", "1e+1.", "1e+1.1", "1.p+", "1p+.", "1.p+1", "1p+.1", "1.1p+", "1p+1.", "1p+1.1", "1.e-", "1e-.", "1.e-1", "1e-.1", "1.1e-", "1e-1.", "1e-1.1", "1.p-", "1p-.", "1.p-1", "1p-.1", "1.1p-", "1p-1.", "1p-1.1", // invalid digit separators "1_", "100_", "1_000_", "1__0", "1__000_0", "1_000__0", "1___0", "2e_8", "2_e8", "2e8_", "3e1__1", "2e+_5", "2e_+5", "0x_FFFF", "0b_1010", "0b1111_0000_", "0o6__6", "0_b1010", "0_o77", "0_xFF", "_0b1010", "_0o77", "_0xFF", "2e1_6", "0x2p1_0", "2e-1_0", ]; let extra: [_]str = [ "let t = 4e-0i;", "let t = 4e-1i;", "let t = 4e-0i8;", "let t = 4e-1i8;", "let t = 0b1e-1f32;", "let t = 0o1e-1f32;", "let t = 0x1e+1f32;", "let t = 0x1e-1f32;", "let t = 0b1p-1f32;", "let t = 0o1p-1f32;", // exponent overflow "let t: u64 = 1e1000;", "let t = 100u3_2;", "let t = 100u32_;", "let t = 100u_32;", "let t = 100_u32;", "let t = _100u32;", ]; let suffix = [";", "i;", "i8;", "f32;"]; let buf: [256]u8 = [0...]; for (let i = 0z; i < len(invalid); i += 1) { for (let j = 0z; j < len(suffix); j += 1) { let buf = buf[..0]; static append(buf, toutf8("let t = ")...); static append(buf, toutf8(invalid[i])...); static append(buf, toutf8(suffix[j])...); compile(void, *(&buf: *str))!; }; }; for (let i = 0z; i < len(extra); i += 1) { compile(void, extra[i])!; }; }; fn basics() void = { let b1 = true, b2 = false; let p1: nullable *int = null; let r1 = ['x', '\x0A', '\u1234', '\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\'', '\"', '\U00123456', '\u0080', '\U00000080']; static assert('a' == '\x61'); static assert('a' == '\u0061'); static assert('a' == '\U00000061'); static assert('a' == 0x61u32); static assert('à' == '\u00e0'); static assert('à' == '\U000000e0'); static assert('à' == 0xe0u32); compile(status::LEX, `let r = 'abc';`)!; compile(status::LEX, `let r = '\033';`)!; compile(status::LEX, `let r = '\xc3';`)!; compile(status::LEX, `let r = '\x123';`)!; compile(status::LEX, `let r = '\u69';`)!; compile(status::LEX, `let r = '\U1234567';`)!; compile(status::LEX, `let r = '\xah';`)!; compile(status::LEX, `let r = '\uahij';`)!; compile(status::LEX, `let r = '\Uahijklmn';`)!; }; export fn main() void = { // The interaction between literals and result type reduction is tested // in 30-reduction.c basics(); numeric(); assignment(); aggregates(); }; harec-0.24.2/tests/01-arrays.ha000066400000000000000000000103341464473277600161010ustar00rootroot00000000000000use rt::{compile, status}; fn indexing() void = { let x = [1, 2, 3]; let y = &x; let z = &y; assert(x[0] == 1 && x[1] == 2 && x[2] == 3); assert(y[0] == 1 && y[1] == 2 && y[2] == 3); assert(z[0] == 1 && z[1] == 2 && z[2] == 3); x[0] = 5; x[1] = 6; x[2] = 7; assert(x[0] == 5 && x[1] == 6 && x[2] == 7); assert(y[0] == 5 && y[1] == 6 && y[2] == 7); let q = &x[0]; *q = 1337; assert(x[0] == 1337 && y[0] == 1337); compile(status::CHECK, " export fn main() void = { let a = [1, 2, 3]; a[3]; }; ")!; }; type array_alias = [3]int; fn measurements() void = { let x = [1, 2, 3]; assert(len(x) == 3); assert(size([3]int) == size(int) * 3); assert(size([0]int) == 0); static assert(len(x) == 3); static assert(len(&x) == 3); static assert(len([1, 2, 3]) == 3); let y: array_alias = x; static assert(len(y) == 3); static assert(len(&y) == 3); assert(align([_]i8) == 1); assert(align([_]i16) == 2); assert(align([_]i32) == 4); assert(align([_]i64) == 8); assert(align([*]i8) == 1); assert(align([*]i16) == 2); assert(align([*]i32) == 4); assert(align([*]i64) == 8); assert(align([2]i8) == 1); assert(align([2]i16) == 2); assert(align([2]i32) == 4); assert(align([2]i64) == 8); assert(align([0]i32) == 4); }; fn storage() void = { let x = [1, 2, 3]; let y = &x: uintptr; assert(*((y + (size(int) * 0): uintptr): *int) == 1); assert(*((y + (size(int) * 1): uintptr): *int) == 2); assert(*((y + (size(int) * 2): uintptr): *int) == 3); }; fn assignment() void = { let x = [1, 2, 3]; let y = x; let z = [0, 0, 0]; z = y; let w = [1, 0]; w = [2, w[0]]; assert(y[0] == 1 && y[1] == 2 && y[2] == 3); assert(z[0] == 1 && z[1] == 2 && z[2] == 3); assert(w[0] == 2 && w[1] == 1); let v: *[*]int = &x; compile(status::CHECK, " export fn main() void = { let a: [3]uint = [1u,2u,3u]; let b: uint = 0; let ptr: *[3]uint = &a; ptr = &b; }; ")!; compile(status::CHECK, ` export fn main() void = { let a: *[*]uint = &[1u,2u,3u]; let b: [3]str = ["a", "b", "c"]; a = &b; }; `)!; compile(status::CHECK, ` fn f() void = { let a = [1u, 2u, 3u]; a += [3u, 2u, 1u]; }; `)!; }; fn param(x: [3]int) void = { assert(len(x) == 3); assert(x[0] == 1); assert(x[1] == 2); assert(x[2] == 3); }; fn nested() void = { let x = [[1, 2], [3, 4]]; assert(x[0][0] == 1 && x[0][1] == 2); assert(x[1][0] == 3 && x[1][1] == 4); assert(len(x[0]) == 2); x[1] = [5, 6]; assert(x[1][0] == 5 && x[1][1] == 6); }; fn expanded() void = { let a: [5]int = [1337...]; for (let i = 0z; i < len(a); i += 1) { assert(a[i] == 1337); }; let b: [5]struct { x: int, y: int } = [struct { x: int = 10, y: int = 20, }...]; for (let i = 0z; i < len(b); i += 1) { assert(b[i].x == 10 && b[i].y == 20); }; let c: [5]int = [1, 2, 3...]; let expected = [1, 2, 3, 3, 3]; for (let i = 0z; i < len(c); i += 1) { assert(c[i] == expected[i]); }; let q: [65535]int = [1, 2, 3...]; let expected = [1, 2, 3]; for (let i = 0z; i < len(expected); i += 1) { assert(q[i] == expected[i]); }; for (let i = 3z; i < len(q); i += 1) { assert(q[i] == 3); }; }; fn extype() void = { let x: [5]u8 = [42...]; for (let i = 0z; i < len(x); i += 1) { assert(x[i] == 42); }; x[0] = 24; assert(x[0] == 24); assert(x[1] == 42); }; fn eval_array() void = { static let a = [1, 2]; }; fn eval_access() void = { static assert([1, 2][0] == 1 && [1, 2][1] == 2); }; fn reject() void = { // unbounded arrays of values of undefined size compile(status::CHECK, "fn f() void = { let x = null: *[*][*]int; };")!; compile(status::CHECK, "fn f() void = { let x = null: *[*]fn ()int; };")!; // assignment to array of undefined size compile(status::CHECK, "let x: [*]int; fn f() void = { x = []; };")!; // compile-time evaluation of len with slice compile(status::CHECK, "let x: []int; let y = len(x);")!; // cast to array of undefined size compile(status::CHECK, "fn f() void = { let x = [1, 2, 3]: [*]int; };")!; compile(status::CHECK, "fn f() void = { let x = [1...]: [*]int; };")!; compile(status::CHECK, "fn f() void = { ([]: [*]int)[0]; };")!; }; export fn main() void = { indexing(); measurements(); storage(); assignment(); param([1, 2, 3]); nested(); expanded(); extype(); eval_array(); eval_access(); reject(); }; harec-0.24.2/tests/02-integers.ha000066400000000000000000000020501464473277600164150ustar00rootroot00000000000000fn dimensions() void = { // Fixed precision assert(size(i8) == 1 && align(i8) == 1 && size(i8) % align(i8) == 0); assert(size(i16) == 2 && align(i16) == 2 && size(i16) % align(i16) == 0); assert(size(i32) == 4 && align(i32) == 4 && size(i32) % align(i32) == 0); assert(size(i64) == 8 && align(i64) == 8 && size(i64) % align(i64) == 0); assert(size(u8) == 1 && align(u8) == 1 && size(u8) % align(u8) == 0); assert(size(u16) == 2 && align(u16) == 2 && size(u16) % align(u16) == 0); assert(size(u32) == 4 && align(u32) == 4 && size(u32) % align(u32) == 0); assert(size(u64) == 8 && align(u64) == 8 && size(u64) % align(u64) == 0); // Implementation-defined (test meets spec limits) assert(size(int) >= 4 && align(int) >= 4 && size(int) % align(int) == 0); assert(size(uint) >= 4 && align(uint) >= 4 && size(uint) % align(uint) == 0); assert(size(size) % align(size) == 0); assert(size(uintptr) % align(uintptr) == 0); // etc assert(size(rune) == 4 && align(rune) == 4 && size(rune) % align(rune) == 0); }; export fn main() void = { dimensions(); }; harec-0.24.2/tests/03-pointers.ha000066400000000000000000000113101464473277600164400ustar00rootroot00000000000000use rt::{compile, status}; type inta = int; type intp = *int; type u32a = u32; type func = fn() void; fn basics() void = { let x = 42; let y: intp = &x; assert(*y == 42); let y: *int = y; assert(*y == 42); *y = 1337; assert(x == 1337); let z: *inta = &42; assert(*z == 42); let w: *const u32a = &42u32; assert(*w == 42); let a: *func = &basics; let b: *opaque = &42; assert(size(*int) == size(uintptr)); assert(align(*int) == align(uintptr)); }; fn _nullable() void = { let x: nullable *int = null; assert(x == null); let y = 42; x = &y; assert(*(x: *int) == 42); compile(status::CHECK, "fn test() void = { let x: nullable *int = null; let z = *x; };")!; }; fn casts() void = { let a: *uint = &4u; let b = a: *opaque; let c = b: *uint; assert(a == c && *c == 4); let a: nullable *uint = &7u; let b = a: *uint; assert(b == a && *b == 7); let a: nullable *uint = &10u; let b = a as *uint; assert(b == a && *b == 10); let a: nullable *int = &4; assert(a is *int); let a: nullable *int = null; assert(a is null); assert((a as null): nullable *opaque == null); let a: nullable *int = &4; assert(a is *int); let a = &42; let b = a: intp; assert(b == a && *b == 42); }; fn reject() void = { compile(status::PARSE, " type s = null; fn test() void = { void; }; ")!; compile(status::PARSE, " type s = *null; fn test() void = { void; }; ")!; compile(status::CHECK, " fn test() void = { let a = &null; }; ")!; compile(status::PARSE, " fn test() void = { let a = &3: null; }; ")!; compile(status::PARSE, " fn test() void = { let a: nullable *int = &3: null; }; ")!; compile(status::CHECK, " fn test() void = { let b: nullable *int = null; let a = b as null; }; ")!; compile(status::CHECK, " fn test() void = { let a = (null, 3); }; ")!; compile(status::PARSE, " fn test() void = { let a: []null = [null]; }; ")!; compile(status::CHECK, " fn test() void = { let a = [null]; }; ")!; compile(status::PARSE, " fn test() void = { let a: [_]null = [null]; }; ")!; compile(status::CHECK, " fn test() void = { let a = null; }; ")!; compile(status::CHECK, " fn test() void = { let a: nullable *int = &4; a as int; }; ")!; compile(status::CHECK, " fn test() void = { let a: nullable *int = &4; a as *str; }; ")!; // type assertions on non-nullable pointers are prohibited compile(status::CHECK, " fn test() void = { let a: *int = &4; assert(a as *int); }; ")!; // dereference expression not in translation-compatible subset compile(status::CHECK, " let a: int = 0; let b: *int = &a; let c: int = *b; ")!; // can't cast to alias of opaque compile(status::CHECK, " type t = opaque; fn test() void = { let x: *t = &0; }; ")!; // can't have pointer to void compile(status::CHECK, " fn test() void = { let a: *void = &12; }; ")!; compile(status::CHECK, " fn test() void = { let a = &void; }; ")!; compile(status::CHECK, " fn test() void = { &12: *void; }; ")!; compile(status::CHECK, " fn f() void = { let a: nullable *void = null; }; ")!; // arithmetic on pointer types is not allowed compile(status::CHECK, ` fn f() void = { let a = &12i + &8i; }; `)!; compile(status::CHECK, ` fn f() void = { let a = &12i + 8i: uintptr; }; `)!; compile(status::CHECK, ` fn f() void = { let a = &12i + 8i; }; `)!; compile(status::CHECK, ` fn f() void = { let a = &12i; a += &8i; }; `)!; compile(status::CHECK, ` fn f() void = { let a = &12i; a += 8i: uintptr; }; `)!; compile(status::CHECK, ` fn f() void = { let a = &12i; a += 8i; }; `)!; // type promotion compile(status::CHECK, ` fn test() void = { let a: *str = &0; }; `)!; compile(status::CHECK, ` fn test() void = { let a: *str = alloc(0); }; `)!; // pointer to never compile(status::CHECK, ` fn test() void = { &abort(); }; `)!; compile(status::CHECK, ` fn test() nullable *never = null; `)!; compile(status::CHECK, ` fn test() void = { *(&0: *never); }; `)!; // pointer to zero-size type compile(status::CHECK, ` fn test() void = { let a = &void; }; `)!; compile(status::CHECK, ` fn test() void = { let a = &(void, void); }; `)!; compile(status::PARSE, ` fn test() void = { let a = &(int, void).1; }; `)!; compile(status::CHECK, ` fn test() void = { let a = &([]: [0]int); }; `)!; compile(status::CHECK, ` fn test() void = { let a = &struct { v: void = void }; }; `)!; compile(status::CHECK, ` fn test() void = { let a = &struct { i: int = 0, v: void = void }.v; }; `)!; }; export fn main() void = { basics(); _nullable(); casts(); reject(); }; harec-0.24.2/tests/04-strings.ha000066400000000000000000000051611464473277600162760ustar00rootroot00000000000000use rt::{compile, status, toutf8}; fn measurements() void = { const x = "Hello!"; assert(len(x) == 6); assert(len("Hello!") == 6); assert(len("Hello!\0") == 7); assert(len("He\0llo!") == 7); assert(size(str) == size(*u8) + size(size) * 2); const alignment: size = if (size(*u8) > size(size)) size(*u8) else size(size); assert(align(str) % alignment == 0); static assert(len("Hello!") == 6); }; fn storage() void = { const string = "こんにちは"; const ptr = &string: *struct { data: *[*]u8, length: size, capacity: size, }; assert(ptr.length == 15 && ptr.capacity == 15); // UTF-8 encoded const expected: [_]u8 = [ 0xE3, 0x81, 0x93, 0xE3, 0x82, 0x93, 0xE3, 0x81, 0xAB, 0xE3, 0x81, 0xA1, 0xE3, 0x81, 0xAF, ]; for (let i = 0z; i < len(expected); i += 1) { assert(ptr.data[i] == expected[i]); }; const empty = ""; const ptr2 = &empty: *struct { data: nullable *[*]u8, length: size, capacity: size, }; assert(ptr2.data == null); }; fn concat() void = { const s = "Hell" "o, " "wor" "ld!"; const t = *(&s: **[*]u8); const expected = [ 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', ]; for (let i = 0z; i < len(expected); i += 1) { assert(t[i] == expected[i]: u8); }; }; fn equality() void = { assert("foo" != "bar"); assert("foo" != "foobar"); assert("foobar" == "foobar"); assert("foo\0bar" != "foo\0foo"); static assert("foo" != "bar"); static assert("foo" != "foobar"); static assert("foobar" == "foobar"); static assert("foo\0bar" != "foo\0foo"); }; fn escapes() void = { const s = "à"; assert(s == "\xc3\xa0"); assert(s == "\xc3" "" "\xa0"); assert(s == "\u00e0"); assert(s == "\U000000e0"); const s = toutf8(s); assert(len(s) == 2 && s[0] == 0xc3 && s[1] == 0xa0); assert("\x345" == "45"); assert("\033" == "\x0033"); }; fn raw() void = { assert(`hello \" world` == "hello \\\" world"); }; fn reject() void = { compile(status::CHECK, ` fn f() void = { let x = "asdf" + "fdsa"; }; `)!; compile(status::CHECK, ` fn f() void = { let x = "asdf"; x += "fdsa"; }; `)!; compile(status::PARSE, `let s = "\xc3";`)!; compile(status::PARSE, `let s = "\xc3\x00";`)!; compile(status::PARSE, `let s = "\xc30";`)!; compile(status::PARSE, `let s = "\xc3" "\xc3";`)!; compile(status::LEX, `let s = "\xa";`)!; compile(status::LEX, `let s = "\u69";`)!; compile(status::LEX, `let s = "\U1234567";`)!; compile(status::LEX, `let s = "\xah";`)!; compile(status::LEX, `let s = "\uahij";`)!; compile(status::LEX, `let s = "\Uahijklmn";`)!; }; export fn main() void = { measurements(); storage(); concat(); equality(); escapes(); raw(); reject(); }; harec-0.24.2/tests/05-implicit-casts.ha000066400000000000000000000066241464473277600175400ustar00rootroot00000000000000use rt::{compile, status}; type subtype = struct { foo: int, }; type super1 = struct { foo: subtype, bar: int, }; type super2 = struct { subtype, bar: int, }; type super3 = struct { struct { foo: int }, bar: int, }; type func = fn() void; fn rules() void = { // Fixed precision ints let _i64: i64 = 0i64; _i64 = 42i8; _i64 = 42i16; _i64 = 42i32; _i64 = 42i; let _i32: i32 = 0i32; _i32 = 42i8; _i32 = 42i16; let _i16: i16 = 0i16; _i16 = 42i8; let _u64: u64 = 0u64; _u64 = 42u8; _u64 = 42u16; _u64 = 42u32; _u64 = 42u; let _u32: u32 = 0u32; _u32 = 42u8; _u32 = 42u16; let _u16: u16 = 0u16; _u16 = 42u8; // Implementation-defined precision if (size(int) == 8) { compile(status::SUCCESS, "fn test() void = { let i: int = 42i64; };")!; }; let i: int = 42i; i = 42i32; i = 42i16; i = 42i8; if (size(uint) == 8) { compile(status::SUCCESS, "fn test() void = { let u: uint = 42u64; };")!; }; let u: uint = 42u; u = 42u32; u = 42u16; u = 42u8; // Precision loss (should fail) compile(status::CHECK, "fn test() void = { let _i8: i8 = 42i16; };")!; compile(status::CHECK, "fn test() void = { let _i8: i8 = 42i32; };")!; compile(status::CHECK, "fn test() void = { let _i8: i8 = 42i64; };")!; compile(status::CHECK, "fn test() void = { let _i8: i8 = 42i; };")!; compile(status::CHECK, "fn test() void = { let _i16: i16 = 42i32; };")!; compile(status::CHECK, "fn test() void = { let _i16: i16 = 42i64; };")!; compile(status::CHECK, "fn test() void = { let _i32: i32 = 42i64; };")!; compile(status::CHECK, "fn test() void = { let _u8: u8 = 42u16; };")!; compile(status::CHECK, "fn test() void = { let _u8: u8 = 42u32; };")!; compile(status::CHECK, "fn test() void = { let _u8: u8 = 42u64; };")!; compile(status::CHECK, "fn test() void = { let _u8: u8 = 42u; };")!; compile(status::CHECK, "fn test() void = { let _u16: u16 = 42u32; };")!; compile(status::CHECK, "fn test() void = { let _u16: u16 = 42u64; };")!; compile(status::CHECK, "fn test() void = { let _u32: u32 = 42u64; };")!; compile(status::CHECK, "fn test() void = { let _f32: f32 = 42f64; };")!; // Pointer conversions let nptr: nullable *int = null; nptr = &i; let vptr: nullable *opaque = nptr; // Slice conversions let s: []int = [1, 2, 3]; let s: []opaque = s; // Struct subtyping let sptr: *subtype = &super1 { ... }; let sptr: *subtype = &super2 { ... }; let sptr: *struct { foo: int } = &super3 { ... }; // Invalid pointer conversions compile(status::CHECK, "fn test() void = { let x: nullable *int = null; let y: *int = x; };" )!; compile(status::CHECK, "fn test() void = { let x: int = 10; let y: *int = &x; let y: *uint = x; };" )!; compile(status::CHECK, "type i = int;" "fn test() void = { let x = &42; let y: *i = x; };" )!; compile(status::CHECK, "type v = void;" "fn test() void = { let x = &0; let x: *v = x; };" )!; // Invalid slice conversions compile(status::CHECK, "type i = int;" "fn test() void = { let x: []int = [1, 2, 3]; let y: []i = x; };" )!; compile(status::CHECK, "type v = void;" "fn test() void = { let x: []int = [0]; let x: []v = x; };" )!; // Non-const from const (copy) const j = 10; let k = j; }; fn rvalue() i64 = { return 1234; }; fn callme(in: i64) void = { assert(in == 1234i64); }; fn calls() void = { callme(1234); }; export fn main() void = { rules(); assert(rvalue() == 1234i64); calls(); // TODO: Expand this: // - Floats // - Arrays <-> slices }; harec-0.24.2/tests/06-structs.ha000066400000000000000000000260321464473277600163160ustar00rootroot00000000000000use rt::{compile, status}; fn padding() void = { assert(size(struct { x: i32, y: i32 }) == 8); assert(size(struct { x: i32, y: i64 }) == 16); assert(size(union { x: i8, y: i16, z: i32 }) == 4); assert(align(struct { x: i32, y: i32 }) == 4); assert(align(struct { x: i32, y: i64 }) == 8); assert(align(union { x: i8, y: i16, z: i32 }) == 4); const s = struct { x: i8 = 10, y: i16 = 20, z: i32 = 30, q: i64 = 40, }; assert(&s.x: uintptr: size % 1 == 0); assert(&s.y: uintptr: size % 2 == 0); assert(&s.z: uintptr: size % 4 == 0); assert(&s.q: uintptr: size % 8 == 0); }; type v = struct { v: void }; type vv = struct { v: void, w: void }; let a = v { ... }; let b = vv { ... }; def A = v { ... }; def B = vv { ... }; fn storage() void = { let coords = struct { x: i32 = 10, y: i32 = 20 }; let ptr = &coords: *[*]i32; assert(ptr[0] == 10 && ptr[1] == 20); }; fn assignment() void = { let coords = struct { x: int = 20, y: int = 30 }; coords.x = 40; coords.y = 50; assert(coords.x == 40 && coords.y == 50); coords = struct { x: int = 60, y: int = 70 }; assert(coords.x == 60 && coords.y == 70); let a = v { ... }; a.v = a.v; let b = vv { ... }; b.v = b.w; b.w = void; }; fn deref() void = { let coords = struct { x: int = 20, y: int = 30 }; let a = &coords; assert(a.x == 20 && a.y == 30); let b = &a; assert(b.x == 20 && b.y == 30); let c = &b; assert(c.x == 20 && c.y == 30); c.x = 42; c.y = 96; assert(coords.x == 42 && coords.y == 96); }; type embedded = struct { foo: u8, }; type embed1 = struct { embedded, struct { bar: u8, baz: u64, }, }; type embed2 = struct { struct { bar: u8, baz: u64, }, embedded, }; type embed3 = struct { embedded, embedded: int, }; type embed4 = struct { embedded: int, embedded, }; fn nested() void = { let s = embed1 { foo = 42, bar = 69, baz = 1337, }; assert(offset(s.foo) == 0 && offset(s.bar) == 8 && offset(s.baz) == 16); assert(s.foo == 42 && s.bar == 69 && s.baz == 1337); let s = embed2 { foo = 42, bar = 69, baz = 1337, }; assert(offset(s.bar) == 0 && offset(s.baz) == 8 && offset(s.foo) == 16); assert(s.foo == 42 && s.bar == 69 && s.baz == 1337); let s = embed3 { foo = 42, embedded = 69, }; let s = embed4 { foo = 42, embedded = 69, }; let s = struct { x: int = 10, y: struct { z: int, q: int, a: struct { b: int, c: int }, } = struct { z: int = 20, q: int = 30, a: struct { b: int, c: int } = struct { b: int = 42, c: int = 24, }, }, }; assert(s.x == 10); assert(s.y.z == 20); assert(s.y.q == 30); assert(s.y.a.b == 42); assert(s.y.a.c == 24); assert(&s.y: uintptr == &s.y.z: uintptr); s.x = 1337; assert(s.x == 1337); s.y.a = struct { b: int = 1337, c: int = 7331 }; assert(s.y.a.b == 1337); assert(s.y.a.c == 7331); }; type coords = struct { @offset(0) x: int, @offset(4) y: int }; type coords3 = struct { coords, z: int }; type embed = struct { a: uint, b: u8 }; type _enum = enum { A = -1, B, C }; // complex embedded hierarchy type me = struct { embed, coords3, @offset(24) v: void, f: int, g: (int, str), h: _enum, p: nullable *int }; let g1: me = me { b = 4, x = -1, y = -2, z = -3, f = 20, ... }; let g2: me = me { x = -1, y = -2, z = -3, f = 20, ... }; let g3: me = me { y = -2, z = -3, f = 20, ... }; let g4: me = me { z = -3, f = 20, ... }; let g5: me = me { f = 20, ... }; let g6: me = me { ... }; fn named() void = { let x = coords { y = 10, x = 20 }; assert(x.x == 20 && x.y == 10); }; type offset_test = struct { a: int, b: void, c: size, d: void, e: size, f: void, g: [0]int, }; fn _offset() void = { let x = me { ... }; assert(offset(x.a) == 0); assert(offset(x.b) == 4); assert(offset(x.x) == 8); assert(offset(x.y) == 12); assert(offset(x.z) == 16); assert(offset(x.v) == 24); assert(offset(x.f) == 24); assert(offset(x.g) == 32); assert(offset(x.h) == 64); assert(offset(x.p) == 72); assert(size(me) == 80); let x = offset_test { ... }; assert(offset(x.a) == 0); assert(offset(x.b) == 4); assert(offset(x.c) == 8); assert(offset(x.d) == 16); assert(offset(x.e) == 16); assert(offset(x.f) == 24); assert(offset(x.g) == 24); assert(size(offset_test) == 24); }; fn autofill() void = { let x = coords { x = 10, ... }; assert(x.x == 10 && x.y == 0); assert(g1.a == 0 && g1.b == 4 && g1.x == -1 && g1.y == -2 && g1.z == -3 && g1.f == 20 && g1.g.0 == 0 && g1.g.1 == "" && g1.h == _enum::B && g1.p == null); assert(g2.a == 0 && g2.b == 0 && g2.x == -1 && g2.y == -2 && g2.z == -3 && g2.f == 20 && g2.g.0 == 0 && g2.g.1 == "" && g2.h == _enum::B && g2.p == null); assert(g3.a == 0 && g3.b == 0 && g3.x == 0 && g3.y == -2 && g3.z == -3 && g3.f == 20 && g3.g.0 == 0 && g3.g.1 == "" && g3.h == _enum::B && g3.p == null); assert(g4.a == 0 && g4.b == 0 && g4.x == 0 && g4.y == 0 && g4.z == -3 && g4.f == 20 && g4.g.0 == 0 && g4.g.1 == "" && g4.h == _enum::B && g4.p == null); assert(g5.a == 0 && g5.b == 0 && g5.x == 0 && g5.y == 0 && g5.z == 0 && g5.f == 20 && g5.g.0 == 0 && g5.g.1 == "" && g5.h == _enum::B && g5.p == null); assert(g6.a == 0 && g6.b == 0 && g6.x == 0 && g6.y == 0 && g6.z == 0 && g6.f == 0 && g6.g.0 == 0 && g6.g.1 == "" && g6.h == _enum::B && g6.p == null); let l1: me = me { b = 4, x = -1, y = -2, z = -3, f = 20, ... }; let l2: me = me { x = -1, y = -2, z = -3, f = 20, ... }; let l3: me = me { y = -2, z = -3, f = 20, ... }; let l4: me = me { z = -3, f = 20, ... }; let l5: me = me { f = 20, ... }; let l6: me = me { ... }; assert(l1.a == 0 && l1.b == 4 && l1.x == -1 && l1.y == -2 && l1.z == -3 && l1.f == 20 && l1.g.0 == 0 && l1.g.1 == "" && l1.h == _enum::B && l1.p == null); assert(l2.a == 0 && l2.b == 0 && l2.x == -1 && l2.y == -2 && l2.z == -3 && l2.f == 20 && l2.g.0 == 0 && l2.g.1 == "" && l2.h == _enum::B && l2.p == null); assert(l3.a == 0 && l3.b == 0 && l3.x == 0 && l3.y == -2 && l3.z == -3 && l3.f == 20 && l3.g.0 == 0 && l3.g.1 == "" && l3.h == _enum::B && l3.p == null); assert(l4.a == 0 && l4.b == 0 && l4.x == 0 && l4.y == 0 && l4.z == -3 && l4.f == 20 && l4.g.0 == 0 && l4.g.1 == "" && l4.h == _enum::B && l4.p == null); assert(l5.a == 0 && l5.b == 0 && l5.x == 0 && l5.y == 0 && l5.z == 0 && l5.f == 20 && l5.g.0 == 0 && l5.g.1 == "" && l5.h == _enum::B && l5.p == null); assert(l6.a == 0 && l6.b == 0 && l6.x == 0 && l6.y == 0 && l6.z == 0 && l6.f == 0 && l6.g.0 == 0 && l6.g.1 == "" && l6.h == _enum::B && l6.p == null); }; fn invariants() void = { // embedding a non-alias type compile(status::PARSE, "type t = struct { u8, x: int };")!; compile(status::PARSE, "type t = struct { x: int, u8};")!; const failures = [ // Assign field from non-assignable type: "fn test() void = { let x: struct { y: int } = struct { y: int = 10u }; };", " type coords = struct { x: int, y: int }; fn test() void = { let x = coords { x: int = 10u, y: int = 20u }; }; ", // multiple initializations for single field "type s = struct { x: int }; fn test() s = s { x = 5, x = 7 };", "type e = struct { x: int }, s = struct { y: int, e };" "fn test() s = s { x = 5, x = 7 };", "type e = struct { x: int }, s = struct { e, y: int};" "fn test() s = s { x = 5, x = 7 };", // embedding a nonexistent identifier "type t = struct { a, x: int };" "type t = struct { x: int, a };" // embedding a non-struct alias "type a = str; type t = struct { a, x: int };" "type a = str; type t = struct { x: int, a };" // embedding a non-type object "let a: int = 6; type t = struct { a, x: int };" "let a: int = 6; type t = struct { x: int, a };" // Duplicate members "type s = struct { a: int, a: int };" "type s = struct { struct { a: int }, a: int };", "type s = struct { a: int, struct { a: int } };", "type embed = struct { a: int }; type s = struct { embed, a: int };", "type embed = struct { a: int }; type s = struct { a: int, embed };", "type embed = struct { a: int }; type s = struct { embed, embed };", // Dereference non-nullable pointer: "fn test() void = { let x: nullable *struct { y: int } = null; x.y; };", // Select field from non-struct object: "fn test() void = { let x = 10; x.y; };", // Unknown field: "fn test() void = { let x: struct { y: int } = struct { y: int = 10 }; x.z; };", // Untyped field for unnamed struct: "fn test() void = { let x = struct { x = 10 }; };", // Members of undefined size before last "type s = struct { x: [*]int, a: str };", // size() of struct/union of undefined size "type s = struct { x: str, a: [*]u8 }; let a = size(s);", "type s = union { x: str, a: [*]u8 }; let a = size(s);", // No arithmetic on structs "type s = struct { a: int };" "fn f() void = { let x = s { a = 3 } + s { a = 5 }; };", "type s = struct { a: int };" "fn f() void = { let x = s { a = 3 }; x += s { a = 5 }; };", // Try to autofill field without default value: "type foo = struct { x: (void | int), y: (void | int) };" "fn test() void = { let x = foo { x = 0, ... }; };", "type foo = struct { x: [1]*int };" "fn test() void = { let x = foo { ... }; };", "type foo = struct { x: int, y: [*]int };" "fn test() void = { let x = foo { x = 0, ... }; };", "type foo = struct { x: int, y: struct { z: int, a: (void | int) } };" "fn test() void = { let x = foo { x = 0, ... }; };", "type foo = struct { x: *int, y: int };" "fn test() void = { let x = foo { ... }; };", "type a = *int; type foo = struct { x: a };" "fn test() void = { let x = foo { ... }; };", "type foo = struct { x: int, y: bar };" "type bar = enum { A = 1, B = 2 };" "fn test() void = { let x = foo { x = 0, ... }; };", "type foo = struct { x: int, y: bar };" "type bar = enum { A = 1, B, C = -1 };" "fn test() void = { let x = foo { x = 0, ... }; };", // Invalid @offsets "type foo = struct { @offset(4) x: int, @offset(0) y: int };", "type foo = struct { x: u64, @offset(4) y: u32 };", "type foo = union { x: u32, @offset(4) y: u32 };", // @packed union "type foo = union @packed { x: u32, y: str };", ]; for (let i = 0z; i < len(failures); i += 1) { compile(status::CHECK, failures[i])!; }; }; fn fields() void = { g1.v; let n: u32 = 0; let up = &n: *union { struct { a: u8, b: u8, }, c: u32, }; assert(&up.a: uintptr == &n: uintptr); assert(&up.b: uintptr == &n: uintptr + 1); assert(&up.c: uintptr == &n: uintptr); let sp = &n: *struct { a: u8, struct { b: u8, struct { c: u8, }, }, }; assert(&sp.a: uintptr == &n: uintptr); assert(&sp.b: uintptr == &n: uintptr + 1); assert(&sp.c: uintptr == &n: uintptr + 2); }; def S = struct { a: u8 = 69, b: u32 = 1337, }; fn eval() void = { static let s = struct { a: u8 = 69, b: u32 = 1337, }; static assert(S.a == 69); static assert(struct { a: u8 = 69, b: u32 = 1337, }.b == 1337); }; export fn main() void = { padding(); storage(); assignment(); deref(); nested(); named(); _offset(); autofill(); invariants(); fields(); eval(); // TODO: more union tests }; harec-0.24.2/tests/07-aliases.ha000066400000000000000000000070251464473277600162320ustar00rootroot00000000000000type my_int = int; fn alias_builtin() void = { let i: my_int = 1234; assert(i == 1234, "built-in alias"); }; fn unwrap() void = { let i: ...my_int = 1234; assert(i == 1234); }; type my_array = [3]int; type my_array_ptr = *my_array; type my_array_ptr_ptr = *my_array_ptr; type my_slice = []int; fn alias_array() void = { let a: my_array = [1, 2, 3]; let i: my_int = 0; let b: my_array_ptr = &a; let c: my_array_ptr_ptr = &b; let d: my_slice = c[..]; assert(a[i] == 1, "array alias"); assert(a[1] == 2, "array alias"); assert(a[2] == 3, "array alias"); assert(b[i] == 1, "array ptr alias"); assert(b[1] == 2, "array ptr alias"); assert(b[2] == 3, "array ptr alias"); assert(c[i] == 1, "array ptr ptr alias"); assert(c[1] == 2, "array ptr ptr alias"); assert(c[2] == 3, "array ptr ptr alias"); assert(d[i] == 1, "array ptr ptr slice alias"); assert(d[1] == 2, "array ptr ptr slice alias"); assert(d[2] == 3, "array ptr ptr slice alias"); }; type my_fn = *const fn(int) int; type my_fn_ptr = *my_fn; type my_fn_ptr_ptr = *my_fn_ptr; type my_fn_my_int = *const fn(my_int) int; fn foo(n: int) int = (n + 1) * 2; fn alias_fn() void = { let f: my_fn = &foo; let g: my_fn_ptr = &f; let h: my_fn_ptr_ptr = &g; assert(f(0) == foo(0), "fn alias"); assert(g(0) == foo(0), "fn ptr alias"); assert(h(0) == foo(0), "fn ptr ptr alias"); }; type my_struct = struct { x: int, y: int }; type my_struct_ptr = *my_struct; type my_struct_ptr_ptr = *my_struct_ptr; type my_other_struct = struct { x: my_struct_ptr_ptr }; fn alias_struct() void = { let s: my_struct = struct { x: int = 42, y: int = 69, }; let t: my_struct_ptr = &s; let u: my_struct_ptr_ptr = &t; let v: my_other_struct = struct { x: my_struct_ptr_ptr = u }; assert(s.x == 42, "struct alias"); assert(s.y == 69, "struct alias"); assert(t.x == 42, "struct alias ptr"); assert(t.y == 69, "struct alias ptr"); assert(u.x == 42, "struct alias ptr ptr"); assert(u.y == 69, "struct alias ptr ptr"); assert(v.x.x == 42, "struct alias ptr ptr alias"); assert(v.x.y == 69, "struct alias ptr ptr alias"); }; type my_tagged = (int | void); fn alias_tagged() void = { let a: my_tagged = 42; assert(a is int, "tag"); assert(a as int == 42, "value"); }; type my_my_array = my_array; type my_my_int = my_int; fn alias_alias() void = { let a: my_my_array = [1, 2, 3]; let i: my_my_int = 0; assert(a[i] == 1, "alias alias"); assert(a[1] == 2, "alias alias"); assert(a[2] == 3, "alias alias"); }; type recur = struct { self: *recur, }; fn recursive() void = { let x: recur = struct { self: *recur = null: *recur, }; x.self = &x; assert(x.self == x.self.self); }; fn measurement() void = { assert(size(my_int) == size(int) && size(my_my_int) == size(int)); assert(size(my_array) == size([3]int)); assert(size(my_array_ptr) == size(*[3]int)); assert(size(my_slice) == size([]int)); assert(size(my_struct) == size(struct { x: int, y: int })); assert(size(my_struct_ptr) == size(*struct { x: int, y: int })); assert(size(my_tagged) == size((int | void))); assert(align(my_int) == align(int) && align(my_my_int) == align(int)); assert(align(my_array) == align([3]int)); assert(align(my_array_ptr) == align(*[3]int)); assert(align(my_slice) == align([]int)); assert(align(my_struct) == align(struct { x: int, y: int })); assert(align(my_struct_ptr) == align(*struct { x: int, y: int })); assert(align(my_tagged) == align((int | void))); }; export fn main() void = { alias_builtin(); alias_array(); alias_fn(); alias_struct(); alias_tagged(); alias_alias(); recursive(); measurement(); }; harec-0.24.2/tests/08-slices.ha000066400000000000000000000206471464473277600161010ustar00rootroot00000000000000use rt::{compile, status}; type slice = struct { data: nullable *opaque, length: size, capacity: size, }; fn from_array() void = { let src = [1, 2, 3]; let x: []int = src; let xptr = &x: *slice; assert(xptr.data == &src); let y: []int = []; let yptr = &y: *slice; assert(yptr.data == null); }; fn storage() void = { let x: []int = [1, 2, 3, 4, 5]; const expected = [1, 2, 3, 4, 5]; let ptr = &x: *slice; assert(len(x) == 5); assert(ptr.length == 5 && ptr.capacity == 5); for (let i = 0z; i < len(expected); i += 1) { assert(x[i] == expected[i]); }; let x: *[1]u8 = alloc([0...]); free(x); }; fn casting() void = { let x: []int = [1, 2, 3, 4, 5]; let y = x: *[5]int; for (let i = 0z; i < len(x); i += 1) { assert(x[i] == y[i]); }; []: []int: []opaque; compile(status::CHECK, "fn test() void = { []: []opaque; };" )!; compile(status::CHECK, "fn test() void = { [1]: []opaque; };" )!; }; fn measurements() void = { let x: []int = [1, 2, 3, 4, 5]; assert(size([]int) == size(*[*]int) + size(size) * 2); assert(align([]int) == (if (align(*[*]int) > align(size)) align(*[*]int) else align(size))); assert(len(x) == 5); assert(&x[0]: uintptr: size % size(int) == 0); static assert(len([1, 2, 3, 4, 5]: []int) == 5); compile(status::CHECK, "fn test() void = { let x = []; static assert(len(x) == 0); };" )!; }; fn indexing() void = { let x = [1, 3, 3, 7]; assert(x[0] == 1 && x[1] == 3 && x[2] == 3 && x[3] == 7); compile(status::CHECK, "fn test() void = { let x: []int = [1, 2, 3]; x[\"hello\"]; };" )!; compile(status::CHECK, "fn test() void = { let x = 10; x[10]; };" )!; compile(status::CHECK, "fn test() void = { let s: []u8 = []; let ss = s: []opaque; ss[0] = ss[1]; };" )!; }; fn zero3(s: []int) void = { s[..] = [0, 0, 0]; }; type sl_alias = []int; fn assignment() void = { let source = [1, 2, 3]; let x: []int = source; x[0] = 4; x[1] = 5; x[2] = 6; assert(x[0] == 4 && x[1] == 5 && x[2] == 6); assert(source[0] == 4 && source[1] == 5 && source[2] == 6); let y: []int = [4, 5, 6]; x = y; x[0] = 7; x[1] = 8; x[2] = 9; assert(x[0] == 7 && x[1] == 8 && x[2] == 9); assert(source[0] == 4 && source[1] == 5 && source[2] == 6); zero3(y); assert(y[0] == 0 && y[1] == 0 && y[2] == 0); let z: []int = [1, 2, 3, 4, 5]; z[1..4] = [42, 69, 1337]; assert(z[0] == 1 && z[1] == 42 && z[2] == 69 && z[3] == 1337 && z[4] == 5); z[2..5] = y; assert(z[0] == 1 && z[1] == 42 && z[2] == 0 && z[3] == 0 && z[4] == 0); let z: sl_alias = z; z[2..5] = y; let z: *sl_alias = &z; z[2..5] = y; let x: []int = []; let opaqueslice: []opaque = x; let opaqueslice: []opaque = []: []int; compile(status::CHECK, "export fn main() void = { let a: []int = [1]; a[..] += a; };" )!; compile(status::CHECK, "fn f() void = { let a: []int = [1] + [2]; };" )!; compile(status::CHECK, "type t = opaque; fn f() void = { let x: []int = []; let x: []t = x; };" )!; }; fn assert_slice_eq(actual: []int, expected: []int) void = { assert(len(expected) == len(actual)); for (let i = 0z; i < len(expected); i += 1) { assert(expected[i] == actual[i]); }; }; fn cap(s: []int) size = (&s: *slice).capacity; fn slicing() void = { let a: [_]int = [1, 2, 3, 4, 5]; assert_slice_eq(a[..], [1, 2, 3, 4, 5]); assert_slice_eq(a[..3], [1, 2, 3]); assert_slice_eq(a[1..3], [2, 3]); assert_slice_eq(a[1..], [2, 3, 4, 5]); assert_slice_eq(a[5..], []); assert(cap(a[..0]) == len(a)); assert(cap(a[..2]) == len(a)); assert(cap(a[..]) == len(a)); assert(cap(a[2..]) == len(a) - 2); assert(cap(a[5..]) == 0); let b = a[..3]; assert(cap(b[..0]) == len(a)); assert(cap(b[..2]) == len(a)); assert(cap(b[..]) == len(a)); assert(cap(b[2..]) == len(a) - 2); assert(cap(b[3..]) == 2); let b: []int = [1, 2, 3, 4, 5]; assert_slice_eq(b[..], [1, 2, 3, 4, 5]); assert_slice_eq(b[..3], [1, 2, 3]); assert_slice_eq(b[1..3], [2, 3]); assert_slice_eq(b[1..], [2, 3, 4, 5]); assert_slice_eq(b[5..], []); let p = &a; assert_slice_eq(p[..], [1, 2, 3, 4, 5]); assert_slice_eq(p[..3], [1, 2, 3]); assert_slice_eq(p[1..3], [2, 3]); assert_slice_eq(p[1..], [2, 3, 4, 5]); assert_slice_eq(p[5..], []); compile(status::CHECK, "fn test() void = { let x = \"test\"; x[1..3]; };" )!; compile(status::CHECK, "fn test() void = { let x = [1, 2, 3]; x[\"hi\"..]; };" )!; compile(status::CHECK, "fn test() void = { let x = [1, 2, 3]; x[2..1]; };" )!; compile(status::CHECK, "fn test() void = { let x = [1, 2, 3]; x[..4]; };" )!; }; type tree = struct { value: u64, children: []tree, }; fn sum_tree(t: tree) u64 = { let sum = t.value; for (let i = 0z; i < len(t.children); i += 1) { sum += sum_tree(t.children[i]); }; return sum; }; fn recursive_structure() void = { const t = tree { value = 15, children = [tree { value = 23, children = [tree { value = 62, children = [], }, tree { value = 34, children = [], }], }], }; assert(sum_tree(t) == 134, "recursive structure using slices"); }; fn expandable() void = { let s: [6]u64 = [0...]; s[1..3] = [1...]; assert(s[0] == 0); assert(s[1] == 1); assert(s[2] == 1); assert(s[3] == 0); s[2..] = [123...]; assert(s[1] == 1); assert(s[2] == 123); assert(s[3] == 123); assert(s[4] == 123); assert(s[5] == 123); }; fn misc_reject() void = { // can't have slice of void compile(status::CHECK, "fn test() void = { let x: []void = [12]; };" )!; compile(status::CHECK, "fn test() void = { let x = [void]: []opaque; };" )!; compile(status::CHECK, "fn test() void = { []: []void; };" )!; compile(status::CHECK, "fn test() void = { let x: ([]void | void) = void; };" )!; compile(status::CHECK, "fn test() void = { []: []never; };" )!; }; fn lencap(s: []int) (size, size) = { let ptr = &s: *slice; return (ptr.length, ptr.capacity); }; fn cap_borrowed() void = { // size defined, cap = size - L let b: [42]int = [0...]; let (length, capacity) = lencap(b[20..25]); assert(length == 5); assert(capacity == 22); // no size defined, cap = H - L let b2: *[*]int = &b; let (length, capacity) = lencap(b2[20..25]); assert(length == 5); assert(capacity == 5); }; type s = struct { i: int, data: [4]int, tuple: ([2]int, int), nested: [4][3]int, }; let global = s { ... }; let backing = [1, 2, 3, 4]; let ref = backing[1..3]; let ref_chained = backing[1..][..2]; let literal = [1, 2, 3][..2]; let chained = [1, 2, 3, 4][1..][..2]; let long_chain = [1, 2, 3, 4][1..][1..][1..]; fn eval() void = { static assert(len([1, 2, 3][1..2]) == 1); static assert(len([1, 2, 3][1..][..1]) == 1); static assert(len(backing[1..2]) == 1); static assert(len(backing[1..2][..]) == 1); let (length, capacity) = lencap(literal); assert(length == 2); assert(capacity == 3); assert(literal[0] == 1 && literal[1] == 2); let (length, capacity) = lencap(chained); assert(length == 2); assert(capacity == 3); assert(chained[0] == 2 && chained[1] == 3); let (length, capacity) = lencap(ref); assert(length == 2); assert(capacity == 3); assert(ref[0] == 2 && ref[1] == 3); let (length, capacity) = lencap(ref_chained); assert(length == 2); assert(capacity == 3); assert(ref[0] == 2 && ref[1] == 3); let (length, capacity) = lencap(global.data[..0]); assert(length == 0); assert(capacity == 4); let (length, capacity) = lencap(global.tuple.0[1..]); assert(length == 1); assert(capacity == 1); let (length, capacity) = lencap(global.tuple.0[..][1..]); assert(length == 1); assert(capacity == 1); let (length, capacity) = lencap(global.nested[2][1..]); assert(length == 2); assert(capacity == 2); let (length, capacity) = lencap(global.nested[2][1..][..1]); assert(length == 1); assert(capacity == 2); compile(status::CHECK, "let x = [1, 2, 3][2..1];")!; compile(status::CHECK, "let x = [1, 2, 3][..10];")!; compile(status::CHECK, "let x = [1, 2, 3][4..];")!; compile(status::CHECK, "let x = [1, 2, 3][..][2..1];")!; compile(status::CHECK, "let x = [1, 2, 3][..][..10];")!; compile(status::CHECK, "let x = [1, 2, 3][..][4..];")!; compile(status::CHECK, "let x = [1, 2, 3]; let y = x[2..1];")!; compile(status::CHECK, "let x = [1, 2, 3]; let y = x[..10];")!; compile(status::CHECK, "let x = [1, 2, 3]; let y = x[4..];")!; compile(status::CHECK, "let x = [1, 2, 3]; let y = x[..]; let a = y[..];")!; compile(status::CHECK, "let x = [1, 2, 3]; let y = x[..]; let a = y[1];")!; }; export fn main() void = { from_array(); storage(); measurements(); indexing(); assignment(); slicing(); recursive_structure(); expandable(); misc_reject(); cap_borrowed(); eval(); }; harec-0.24.2/tests/09-funcs.ha000066400000000000000000000141151464473277600157270ustar00rootroot00000000000000use rt; use rt::{compile, exit, status}; fn simple() int = return 69; fn addone(x: *int) void = { *x += 1; }; fn pointers() void = { let x = 0; addone(&x); assert(x == 1); let y = &addone; y(&x); assert(x == 2); let z = &y; z(&x); assert(x == 3); }; fn vafn(expected: []int, values: int...) void = { assert(len(expected) == len(values)); for (let i = 0z; i < len(values); i += 1) { assert(expected[i] == values[i]); }; }; fn vaargs() void = { vafn([1, 2, 3], 1, 2, 3); let data = [1, 2, 3]; vafn(data, data...); vafn([]); }; let x: int = 42; @init fn init() void = { x = 1337; rt::exit_status = 42; }; @init fn init() void = { void; // Should be allowed to have the same name }; @fini fn fini() void = { rt::exit_status = 0; }; @fini fn fini() void = { void; // Should be allowed to have the same name }; fn cvafn(n: int, ...) void = { let ap = vastart(); defer vaend(ap); let ap2 = ap; defer vaend(ap2); for (let i = 0; i < n; i += 1) { let arg: int = vaarg(ap); assert(arg == i + 1); }; for (let i = 0; i < n; i += 1) { let arg: int = vaarg(ap2); assert(arg == i + 1); }; }; fn cvafn2(...) void = { let ap = vastart(); defer vaend(ap); assert(vaarg(ap): int == 1337); assert(vaarg(ap): int == 42); }; fn cvaargs() void = { cvafn(3, 1, 2, 3); cvafn2(1337, 42); cvafn(3, void, 1, 2, void, 3); cvafn2(void, 1337, void, void, 42); }; fn reject() void = { compile(status::PARSE, "fn f(x: int..., ...) void = void;")!; let failures: [_]str = [ // parameter of undefined size "fn f(x: [*]int) void = void;", "fn f(x: [*]int...) void = void;", // return value of undefined size "fn f(x: int) [*]int = void;", "let x: size = size(fn(x: int) int);", "let x: size = align(fn(x: int) int);", // @test functions are always typechecked "@test fn test() void = 42 + \"foo\";" "let x: int = 4;", // ensure there is at least one declaration "fn f(x: int) void = void;" "fn g(x: int) void = void;" "fn test() void = { f + g; };", "fn test() void = { test = test; };", "fn f() void = { size(*never); };", // non-prototype declarations with unnamed parameters "fn f(int) void = void;", "fn f(*int) void = void;", "type t = void; fn f(t) void = void;", // variadic call to a function that does not support that "fn a(x: int) void = void; fn b() void = { a(5...); };", "fn a(x: int, ...) void = void; fn b() void = { a(5...); };", "fn a(x: []int) void = void; fn b() void = { a([5]...); };", "fn a(x: []int, ...) void = void; fn b() void = { a([5]...); };", // unresolved parameter type "fn f(arg: T) void; fn g() void = f(1);", "fn f(arg: T...) void; fn g() void = f(1);", "fn f(arg: T, ...) void; fn g() void = f(1);", // required params may not follow optional "fn f(a: int = 5, b: int) void = void;", // default value must be assignable to type "fn f(a: str = 5) void = void;", // too many arguments "fn f() void = f(0);", "fn f(x: int) void = f(0, 1);", // not enough arguments "fn f(x: int) void = f();", "fn f(x: int, y: int...) void = f();", "fn f(x: int, y: int) void = f(0);", "fn f(x: int, y: int, z: int...) void = f(0);", // argument type mismatch "fn f(x: str) void = f(0);", "fn f(x: str = \"asdf\") void = f(0);", "fn f(x: str...) void = f(0);", "fn f(x: str...) void = f(\"asdf\", 0);", ]; for (let i = 0z; i < len(failures); i += 1) { compile(status::CHECK, failures[i])!; }; }; type zerostruct = struct { v: void }; type zerounion = union { v: void }; fn zeroparamfunc( a: int, b: void, c: str, d: [0]int, e: f64, f: zerostruct, g: zerounion, h: *int, i: (void, void), j: int, ) void = { assert(a == 1); b; assert(c == "2"); d; assert(len(d) == 0); assert(e == 3.0); f; f.v; g; g.v; assert(*h == 4); i; i.0; i.1; assert(j == 5); }; fn zerosizeparams() void = { zeroparamfunc(1, void, "2", [], 3.0, zerostruct { ... }, zerounion { ... }, &4, (void, void), 5); }; fn neverfunc(x: int) never = if (x == 1) exit(0) else abort(); fn _never() void = { if (false) neverfunc(0); if (true) neverfunc(neverfunc(1)); abort(); }; type int_alias = int; fn optional_simple(a: int, b: int = 3) int = b; fn optional_str(x: str = "hi") str = x; fn optional_add(a: int = 3, b: int = 2) int = a + b; fn optional_tagged(x: (int | void) = void) (int | void) = x; fn optional_tuple(x: (int, str) = (42, "hi")) str = x.1; fn optional_alias(x: int_alias = 3) int_alias = x; fn optional_alias2(x: int = 3: int_alias) int = x; fn optional_slc(x: []int = []) size = len(x); fn optional_slc2(x: []int = [1, 2]) int = { // Call repeatedly in a loop to check that the slice is fresh each time. assert(x[1] == 2); x[1] = 3; return x[0]; }; fn optional_ptr(x: nullable *int = null) bool = x is null; fn optional_variadic(a: int = 3, b: int...) bool = a == 3 || len(b) == 2; fn optional_variadic_c(n: int = 0, ...) void = { let ap = vastart(); defer vaend(ap); for (let i = 0; i < n; i += 1) { assert(vaarg(ap): int == i); }; }; fn optional_params() void = { assert(optional_simple(5, 1) == 1); assert(optional_simple(5) == 3); assert(optional_str() == "hi"); assert(optional_str("hello") == "hello"); assert(optional_add() == 5); assert(optional_add(1) == 3); assert(optional_add(1, 1) == 2); assert(optional_tagged() is void); assert(optional_tagged(5) as int == 5); assert(optional_tuple() == "hi"); assert(optional_tuple((12, "wow")) == "wow"); assert(optional_alias() == 3); assert(optional_alias(1: int_alias) == 1); assert(optional_alias2() == 3); assert(optional_alias2(1: int_alias) == 1); assert(optional_slc() == 0); assert(optional_slc([1, 2, 3]) == 3); for (let i = 0; i < 3; i += 1) { assert(optional_slc2() == 1); assert(optional_slc2([2, 2]) == 2); }; assert(optional_ptr()); let i = 0; assert(!optional_ptr(&i)); assert(optional_variadic()); assert(optional_variadic(3)); assert(optional_variadic(0, 1, 2)); optional_variadic_c(); optional_variadic_c(0); optional_variadic_c(1, 0); optional_variadic_c(3, 0, 1, 2); }; export fn main() void = { assert(simple() == 69); pointers(); vaargs(); cvaargs(); zerosizeparams(); assert(x == 1337); reject(); optional_params(); _never(); }; harec-0.24.2/tests/10-binarithms.ha000066400000000000000000000135361464473277600167470ustar00rootroot00000000000000use rt::{compile, status}; use testmod; fn error() bool = { abort(); }; fn set(x: *int) bool = { *x = 42; return true; }; fn andorxor() void = { assert((false || false) == false); assert((false || true) == true); assert((true || false) == true); assert((true || true) == true); assert((true || error()) == true); let x = 0; assert((false || set(&x)) == true); assert(x == 42); static assert((false || false) == false); static assert((false || true) == true); static assert((true || false) == true); static assert((true || true) == true); let x = 0; let f = false; f ||= false; assert(!f); f ||= set(&x); assert(x == 42); assert(f); f || error(); assert(f); f ||= false; assert(f); assert((false && false) == false); assert((false && true) == false); assert((true && false) == false); assert((true && true) == true); assert((false && error()) == false); x = 0; assert((true && set(&x)) == true); assert(x == 42); static assert((false && false) == false); static assert((false && true) == false); static assert((true && false) == false); static assert((true && true) == true); let x = 0; let f = true; f &&= true; f &&= set(&x); assert(x == 42); assert(f); f &&= false; assert(!f); f &&= error(); f &&= true; assert(!f); assert((false ^^ false) == false); assert((false ^^ true) == true); assert((true ^^ false) == true); assert((true ^^ true) == false); static assert((false ^^ false) == false); static assert((false ^^ true) == true); static assert((true ^^ false) == true); static assert((true ^^ true) == false); let f = true; f ^^= true; assert(!f); f ^^= false; assert(!f); f ^^= true; assert(f); f ^^= false; assert(f); }; fn sar_shr() void = { assert(-12697259629065987i64 >> 26 == -189203913); let x = 1i64; x <<= 63; assert(x == -9223372036854775808i64); x >>= 63; assert(x == -1); let y = 1u64; y <<= 63; assert(y == 9223372036854775808); y >>= 63; assert(y == 1); assert(-4i32 >> 1 == -2); let h0 = -12697259629065987i64; let h1 = (h0 + (1i64 << 25)) >> 26; assert(h1 == -189203912); }; fn arithmetic() void = { assert(1337 + 1234 == 2571); assert(1337 - 1234 == 103); assert(1234 - 1337 == -103); assert(1337 * 1234 == 1649858); assert(1337 / 1234 == 1); assert(1234 / 1337 == 0); assert(625 / 5 == 125); assert(1337 % 1234 == 103); assert(1234 % 1337 == 1234); assert(625 % 5 == 0); assert(2147483647i32 + 1 == -2147483648i32); assert(-2147483648i32 - 1 == 2147483647i32); assert(4294967295u32 + 1 == 0u32); assert(0u32 - 1 == 4294967295u32); assert(-1337 * 1234 == -1649858); assert(1337 * -1234 == -1649858); assert(-1337 * -1234 == 1649858); assert(-1337 / 1234 == -1); assert(1337 / -1234 == -1); assert(-1337 / -1234 == 1); assert(-1234 / 1337 == 0); assert(1234 / -1337 == 0); assert(-1234 / -1337 == 0); assert(-625 / 5 == -125); assert(625 / -5 == -125); assert(-625 / -5 == 125); assert(-1337 % 1234 == -103); assert(1337 % -1234 == 103); assert(-1337 % -1234 == -103); assert(-1234 % 1337 == -1234); assert(1234 % -1337 == 1234); assert(-1234 % -1337 == -1234); assert(-625 % 5 == 0); assert(625 % -5 == 0); assert(-625 % -5 == 0); static assert(1337 + 1234 == 2571); static assert(1337 - 1234 == 103); static assert(1234 - 1337 == -103); static assert(1337 * 1234 == 1649858); static assert(1337 / 1234 == 1); static assert(1234 / 1337 == 0); static assert(625 / 5 == 125); static assert(1337 % 1234 == 103); static assert(1234 % 1337 == 1234); static assert(625 % 5 == 0); static assert(2147483647i32 + 1 == -2147483648i32); static assert(-2147483648i32 - 1 == 2147483647i32); static assert(4294967295u32 + 1 == 0u32); static assert(0u32 - 1 == 4294967295u32); static assert(-1337 * 1234 == -1649858); static assert(1337 * -1234 == -1649858); static assert(-1337 * -1234 == 1649858); static assert(-1337 / 1234 == -1); static assert(1337 / -1234 == -1); static assert(-1337 / -1234 == 1); static assert(-1234 / 1337 == 0); static assert(1234 / -1337 == 0); static assert(-1234 / -1337 == 0); static assert(-625 / 5 == -125); static assert(625 / -5 == -125); static assert(-625 / -5 == 125); static assert(-1337 % 1234 == -103); static assert(1337 % -1234 == 103); static assert(-1337 % -1234 == -103); static assert(-1234 % 1337 == -1234); static assert(1234 % -1337 == 1234); static assert(-1234 % -1337 == -1234); static assert(-625 % 5 == 0); static assert(625 % -5 == 0); static assert(-625 % -5 == 0); }; fn comparison() void = { assert(3 > 2); assert(2 < 3); assert(-3 < -2); assert(-2 > -3); assert(3 >= 3); assert(3 <= 3); assert(0 > -1); assert(0 < -1u); static assert(3 > 2); static assert(2 < 3); static assert(-3 < -2); static assert(-2 > -3); static assert(3 >= 3); static assert(3 <= 3); static assert(0 > -1); static assert(0 < -1u); }; def FLOAT: f64 = 6.0 * 7.0; def I8: i8 = 127 * 2; def U8: u8 = 128 * 2; def ALIAS: testmod::enum_alias = 1: testmod::_enum: testmod::enum_alias + 1: testmod::enum_alias; let a: i8 = 3i8 - (-128i8); let b: i8 = 3i8 + (-128i8); def A: i8 = 3i8 - (-128i8); def B: i8 = 3i8 + (-128i8); def I32: i32 = 3 - (-2147483648i32); fn eval() void = { assert(FLOAT == 42.0); assert(I8 == -2i8); assert(U8 == 0); assert(a == -125i8); assert(b == -125i8); assert(A == -125i8); assert(B == -125i8); assert(I32 == -2147483645i32); assert(ALIAS == 2); static assert(FLOAT == 42.0); static assert(I8 == -2i8); static assert(U8 == 0); static assert(A == -125i8); static assert(B == -125i8); static assert(I32 == -2147483645i32); static assert(ALIAS == 2); }; fn reject() void = { compile(status::CHECK, "let x = 1 / 0;")!; compile(status::CHECK, "let x = -2147483648i32 / -1;")!; compile(status::CHECK, "let x = 1 % 0;")!; compile(status::CHECK, "let x = -2147483648i32 % -1;")!; }; export fn main() void = { // TODO: other binarithms andorxor(); sar_shr(); arithmetic(); comparison(); eval(); reject(); }; harec-0.24.2/tests/11-globals.ha000066400000000000000000000141651464473277600162320ustar00rootroot00000000000000use rt::{compile, status}; // globals without type hint def NOHINT = 1234z; let nohint = 12z; const nohintconst = 123z; // with type defined afterwards const nhval = nh { field = 1, }; type nh = struct { field: int, }; let x: int = 42, y: int = 69; let v0: void = void; let v1 = void; let v2: [0]int = []; let v3 = (void, void); let v4 = struct { v: void = void }; def V0: void = void; def V1 = void; def V2: [0]int = []; def V3 = (void, void); def V4 = struct { v: void = void }; def u32_tag = 1906196061; def u64_tag = 1268499444; fn write() void = { assert(x == 42 && y == 69); x = 1337; assert(x == 1337); assert(nohint == 12); assert(nohintconst == 123); assert(NOHINT == 1234); assert(nhval.field == 1); }; let ar: [3]int = [1, 2, 3]; let sl: []int = [1, 2, 3]; let st: str = "Hello!"; type coords = struct { x: int, y: int }; let su: coords = coords { y = 10, x = 20}; let su_autofill: coords = coords { y = 10, x = 20, ... }; let au: coords = coords { ... }; type coords3 = struct { coords: coords, z: int }; let a3: coords3 = coords3 { ... }; type embedded = struct { a: uint, b: u8 }; type with_embedded = struct { embedded, c: int }; let em: with_embedded = with_embedded { a = 3, b = 4, c = 18 }; let em_autofill: with_embedded = with_embedded { ... }; type with_embedded2 = struct { c: int, embedded }; let em2: with_embedded2 = with_embedded2 { a = 3, b = 4, c = 18 }; let em2_autofill: with_embedded2 = with_embedded2 { ... }; type aenum = enum u64 { BIG_VALUE = 0x1234567887654321, }; const big_value: aenum = aenum::BIG_VALUE; type renum = enum rune { R1 = 'a', R2 = 'g', }; let renum_val = renum::R1; const float: f32 = 1234.5678; const double: f64 = 1234.5678; def A: [_]int = [1, 2]; let a: [_]int = [1, 2]; fn storage() void = { assert(len(ar) == 3); assert(ar[0] == 1 && ar[1] == 2 && ar[2] == 3); assert(len(sl) == 3); assert(sl[0] == 1 && sl[1] == 2 && sl[2] == 3); assert(len(st) == 6); assert(su.x == 20 && su.y == 10); assert(su_autofill.x == 20 && su_autofill.y == 10); assert(au.x == 0 && au.y == 0); assert(a3.coords.x == 0 && a3.coords.y == 0 && a3.z == 0); assert(em.a == 3 && em.b == 4 && em.c == 18); assert(em_autofill.a == 0 && em_autofill.b == 0 && em_autofill.c == 0); assert(em2.a == 3 && em2.b == 4 && em2.c == 18); assert(em2_autofill.a == 0 && em2_autofill.b == 0 && em2_autofill.c == 0); assert(big_value == 0x1234567887654321: aenum); assert(float == 1234.5678); assert(double == 1234.5678); }; fn invalid() void = { compile(status::CHECK, "fn test() int; let x: int = test();")!; compile(status::CHECK, "const a: u8 = 2; const b: u8 = a + 5;")!; compile(status::PARSE, "def a;")!; compile(status::PARSE, "def a: int;")!; compile(status::PARSE, "let a;")!; compile(status::CHECK, "def a: [_]str = 0;")!; compile(status::CHECK, "def a: int = \"string\";")!; compile(status::CHECK, "def a: [_]int = [\"string\"];")!; compile(status::CHECK, "let a: [_]str = 0;")!; compile(status::CHECK, "let a: int = \"string\";")!; compile(status::CHECK, "let a: [_]int = [\"string\"];")!; compile(status::CHECK, "let a = [];")!; compile(status::CHECK, "def a = [];")!; }; fn counter() int = { static let x = 0; x += 1; return x; }; fn static_binding() void = { assert(counter() == 1); assert(counter() == 2); assert(counter() == 3); }; const val: u32 = 42; const arr: [3]int = [1, 2, 3]; const _struct = struct { x: u32 = 1, y: u64 = 2 }; const tup: (u8, str) = (2, "asdf"); const ptr: *u32 = &val; const ptr_arr: *int = &arr[1]; const ptr_struct = &_struct.y; const ptr_tup: *str = &tup.1; fn pointers() void = { assert(ptr == &val && *ptr == val); assert(ptr_arr == &arr[1] && *ptr_arr == 2); assert(ptr_struct == &_struct.y && *ptr_struct == 2); assert(ptr_tup == &tup.1 && *ptr_tup == "asdf"); compile(status::CHECK, "let a = [1, 2, 3]; let b = &a[3];")!; compile(status::CHECK, "let a = (1, 2, 3); let b = &a.3;")!; // Disallow auto-dereference compile(status::CHECK, "let a = [1, 2, 3]; let b = &a; let c = &b[1];")!; compile(status::CHECK, "let a = (1, 2, 3); let b = &a; let c = &b.1;")!; compile(status::CHECK, "let a = struct { x: int = 2 }; let b = &a; let c = &b.x;")!; }; type foo = (int | uint); let subtype: foo = 10u; let arr_of_tagged_of_tuple: [3]((int, int)|void) = [(1, 2), (3, 4), (5, 6)]; type align4 = (i32 | u32); type align8 = (i32 | u32 | u64); let tagged4: align4 = 10u32; let tagged8: align8 = 10u32; let tagged8_u64: align8 = 10u64; fn tagged() void = { assert((arr_of_tagged_of_tuple[0] as (int, int)).0 == 1); assert((arr_of_tagged_of_tuple[0] as (int, int)).1 == 2); assert((arr_of_tagged_of_tuple[1] as (int, int)).0 == 3); assert((arr_of_tagged_of_tuple[1] as (int, int)).1 == 4); assert((arr_of_tagged_of_tuple[2] as (int, int)).0 == 5); assert((arr_of_tagged_of_tuple[2] as (int, int)).1 == 6); assert(subtype is uint); // TODO: subset-compat let t4 = &tagged4: *struct { tag: u32, @offset(4) union { _i32: i32, _u32: u32, }, }; assert(t4.tag == u32_tag); // u32 type ID assert(t4._u32 == 10); let t8 = &tagged8: *struct { tag: u32, @offset(4) union { _i32: i32, _u32: u32, }, @offset(8) _u64: u64, }; assert(t8.tag == u32_tag); // u32 type ID assert(t8._u32 == 10); let t8 = &tagged8_u64: *struct { tag: u32, @offset(4) union { _i32: i32, _u32: u32, }, @offset(8) _u64: u64, }; assert(t8.tag == u64_tag); // u64 type ID assert(t8._u64 == 10); }; // Real-world sample type basic = enum { FN, FOR, IF, IN, NOT, SWITCH, WHILE, }; const keywords: [_](str, basic) = [ ("fn", basic::FN), ("for", basic::FOR), ("if", basic::IF), ("in", basic::IN), ("not", basic::NOT), ("switch", basic::SWITCH), ("while", basic::WHILE), ]; fn tuplearray() void = { assert(keywords[0].0 == "fn"); assert(keywords[1].0 == "for"); assert(keywords[2].0 == "if"); assert(keywords[3].0 == "in"); assert(keywords[4].0 == "not"); assert(keywords[5].0 == "switch"); assert(keywords[6].0 == "while"); }; export fn main() void = { // TODO: Expand this test: // - Declare & validate globals of more types // - Globals which are pointers to other globals write(); storage(); invalid(); static_binding(); pointers(); tagged(); tuplearray(); }; harec-0.24.2/tests/12-loops.ha000066400000000000000000000204321464473277600157360ustar00rootroot00000000000000use rt::{compile, status}; type int_alias = int; type tagged_alias = (int | str); type slice_alias = []int_alias; type array_alias = [4]int_alias; fn scope() void = { let x = 0; for (let i = 1; i == 1; i += 1) { for (true) { assert(x == 0); assert(i == 1); break; }; }; compile(status::CHECK, "fn test() void = { for (true) { let x = 10; break; }; x; };")!; // To make sure that the afterthought is part of the loop's scope for (let i = 0; true; (if (true) { break; })) true; }; fn conditional() void = { let i = 1; for (i < 10) { i *= 2; }; assert(i == 16); }; fn afterthought() void = { let i = 1; for (i < 5; i += 1) { i *= 2; }; assert(i == 7); }; fn binding() void = { let x = 0; for (let i = 0; i < 10; i += 1) { i *= 2; x += 1; }; assert(x == 4); }; fn _break() void = { let x = 0; for (let i = 0; i < 1; i += 1) { let j = 0; for (j < 10) { j += 1; if (j == 5) { break; }; }; assert(j == 5); x += 1; }; assert(x == 1); }; fn _continue() void = { let finished = false; let x = 0; for (!finished) { for (let i = 0; i < 10; i += 1) { if (i == 5) { continue; }; assert(i != 5); }; finished = true; x += 1; }; assert(x == 1); }; fn label() void = { let i = 0; for :outer (i < 10) { for :inner (let j = 0; j < 7; j += 1) { i += 1; if (j == 6) { for (let k = 0; k < 5; k += 1) { if (k == 2) { continue :inner; }; assert(k < 2); }; }; assert(j != 6); if (i > 7) { break :outer; }; }; }; assert(i == 8); compile(status::CHECK, "fn test() void = { for :foo (true) { break :bar; }; };")!; compile(status::CHECK, "fn test() void = { for (true) { break :bar; }; };")!; compile(status::CHECK, "fn test() void = { break :bar; };")!; compile(status::CHECK, "fn test() void = :foo { break :foo; };")!; compile(status::CHECK, "fn test() void = { for :foo (true) { yield :foo; }; };")!; compile(status::CHECK, "fn test() void = :foo { for :foo (true) { yield :foo; }; };")!; compile(status::CHECK, "fn test() void = for :foo (true) :foo { break :foo; };")!; }; type abool = bool; fn alias() void = { for (true: abool) { return; }; }; fn result() void = { for (true) break; for :loop (true) { for (true) break :loop; }; let integer = switch (0) { case 0 => yield 0; case => for (true) void; }; assert(integer == 0); }; fn foreach_next() (int | done) = { static let counter = 0; if (counter < 4) { counter += 1; return counter; }; return done; }; fn foreach_next_alias() (int_alias | done) = { static let counter = 0; if (counter < 4) { counter += 1; return counter; }; return done; }; fn foreach_next_void() (void | done) = { static let counter = 0; if (counter < 4) { counter += 1; return void; }; return done; }; fn foreach_tuple() ((str, int) | done) = { let pairs = [ ("Hello", 1), ("World", 2), ("!", 3), ]; static let counter = 0; if (counter < 3) { counter += 1; return pairs[counter - 1]; }; return done; }; fn foreach_large() ([1024]int | done) = { static let counter = 0; if (counter < 4) { counter += 1; return [12...]; }; return done; }; fn foreach_tagged() (int | str | done) = { static let counter = 0; if (counter < 4) { counter += 1; return if (counter % 2 == 0) counter else ""; }; return done; }; fn foreach_tagged_alias() (tagged_alias | done) = { static let counter = 0; if (counter < 4) { counter += 1; return if (counter % 2 == 0) counter else ""; }; return done; }; fn for_each() void = { let array = [1, 2, 3]; let slice: []int = []; defer free(slice); append(slice, 1); append(slice, 2); append(slice, 3); let pairs = [ ("Hello", 1), ("World", 2), ("!", 3), ]; let counter = 1; for (let x .. []: []int) { counter += 1; }; assert(counter == 1); let counter = 1; for (let x &.. []: []int) { counter += 1; }; assert(counter == 1); let counter = 1; for (let x .. array) { assert(x == counter); counter += 1; }; assert(counter == 4); let counter = 1; for (let x .. slice) { assert(x == counter); counter += 1; }; assert(counter == 4); let counter = 1; for (let x .. &slice) { assert(x == counter); counter += 1; }; assert(counter == 4); let counter = 1u32; for (let x: u32 .. [1, 2, 3]) { assert(x == counter); counter += 1; }; assert(counter == 4); let counter = 0; for (let (string, count) .. pairs) { assert(string == pairs[counter].0); assert(count == pairs[counter].1); counter += 1; }; assert(counter == 3); let counter = 1u32; for (let x: *u32 &.. [1, 2, 3]) { assert(*x == counter); counter += 1; }; assert(counter == 4); let counter = 1; for (let x &.. array) { assert(*x == counter); counter += 1; }; assert(counter == 4); let counter = 1; for (let x &.. slice) { assert(*x == counter); counter += 1; }; assert(counter == 4); let counter = 1; for (let x: *int &.. array) { assert(*x == counter); counter += 1; }; assert(counter == 4); let counter = 1; for (let x: *int &.. slice) { assert(*x == counter); counter += 1; }; assert(counter == 4); let counter = 1; for (let x => foreach_next()) { assert(x == counter); counter += 1; }; assert(counter == 5); let counter = 1; for (let x => foreach_next_alias()) { assert(x == counter); counter += 1; }; assert(counter == 5); let counter = 0; for (let (string, count) => foreach_tuple()) { assert(string == pairs[counter].0); assert(count == pairs[counter].1); counter += 1; }; assert(counter == 3); let counter = 1; for (let x => foreach_tagged()) { if (counter % 2 == 0) { assert(x as int == counter); } else { assert(x as str == ""); }; counter += 1; }; assert(counter == 5); let counter = 1; for (let x => foreach_tagged_alias()) { if (counter % 2 == 0) { assert(x as int == counter); } else { assert(x as str == ""); }; counter += 1; }; assert(counter == 5); let counter = 1; for (let (x, y) .. [(void, 1), (void, 2), (void, 3)]) { assert(counter == y); counter += 1; }; assert(counter == 4); for (let x => foreach_next_void()) { x; }; let counter = 1; for (let x => foreach_large()) { for (let y .. x) { assert(y == 12); }; counter += 1; }; assert(counter == 5); let test_slice: slice_alias = [1, 1, 1]; let counter = 1; for (let x .. test_slice) { assert(x == 1); counter += 1; }; assert(counter == 4); let counter = 1; for (let x &.. test_slice) { assert(*x == 1); counter += 1; }; assert(counter == 4); let test_array: array_alias = [2, 2, 2, 2]; let counter = 1; for (let x .. test_array) { assert(x == 2); counter += 1; }; assert(counter == 5); let counter = 1; for (let x &.. test_array) { assert(*x == 2); counter += 1; }; assert(counter == 5); // pointer to alias of array type: let test_array_ref = &test_array; let counter = 1; for (let x &.. test_array_ref) { assert(*x == 2); counter += 1; }; assert(counter == 5); let counter = 1; for (let x &.. &test_array) { assert(*x == 2); counter += 1; }; assert(counter == 5); let counter = 1; for (let x &.. &(&(&test_array))) { assert(*x == 2); counter += 1; }; assert(counter == 5); let counter = 1; for (let x => done: (int | done)) { counter += 1; }; assert(counter == 1); // No unpacking by reference compile(status::CHECK, "fn test() void = { let pairs = [(1, 2), (3, 4), (5, 6)]; for (let (x, y) &.. pairs) { x; }; };")!; compile(status::PARSE, "fn test() void = { for (static let i = 0; i < 4; i += 1) { i; } };")!; compile(status::CHECK, "type done_alias = done; fn next() (int | done_alias | str | done) = done; fn test() void = { for (let x => next()) { x; }; };")!; compile(status::CHECK, "fn test() void = { let slice: []int = []; for (let x .. slice) { delete(slice[0]); };};")!; compile(status::CHECK, "fn test() void = { let slice: []int = []; for (let x .. slice) { append(slice, 1); };};")!; compile(status::CHECK, "fn test() void = { let slice: []int = []; for (let x .. slice) { insert(slice[0], 1); };};")!; compile(status::CHECK, "fn test() void = { let slice: []int = []; for (let x .. slice) { free(slice); };};")!; compile(status::CHECK, "type d = !done;")!; }; fn next() ((int, int) | done) = (4, 2); export fn main() void = { scope(); conditional(); afterthought(); binding(); _break(); _continue(); label(); alias(); result(); for_each(); }; harec-0.24.2/tests/13-tagged.ha000066400000000000000000000162311464473277600160400ustar00rootroot00000000000000use rt::{compile, status}; def size_tag = 2206074632; def f32_tag = 1568378015; def int_tag = 1099590421; def void_tag = 3012680272; def u8_tag = 3181589295; def u16_tag = 3481467866; def u32_tag = 1906196061; def u64_tag = 1268499444; fn measurements() void = { const x: (u8 | u16 | u32 | u64) = 1337u16; // With padding const alignment: size = if (size(u64) < size(uint)) size(uint) else size(u64); assert(align((u8 | u16 | u32 | u64)) == alignment); assert(size((u8 | u16 | u32 | u64)) == alignment * 2); assert(&x: uintptr: size % size(uint) == 0); assert(&x: uintptr: size % size(u64) == 0); const y: (u8 | u16) = 1337u16; // No padding assert(align((u8 | u16)) == align(uint)); assert(align(((u8 | u16) | (i8 | i16))) == align(uint)); }; fn storage() void = { let x: (u8 | u16 | u32 | u64) = 42u8; const y = &x: *struct { tag: u32, @offset(4) union { _u8: u8, _u16: u16, _u32: u32, }, @offset(8) _u64: u64, }; assert(offset(y._u8) == 4); assert(offset(y._u16) == 4); assert(offset(y._u32) == 4); assert(offset(y._u64) == 8); assert(y.tag == u8_tag); assert(y._u8 == 42); x = 1337u16; assert(y.tag == u16_tag); assert(y._u16 == 1337); x = 0xCAFEBABEu32; assert(y.tag == u32_tag); assert(y._u32 == 0xCAFEBABE); x = 0xCAFEBABEDEADBEEFu64; assert(y.tag == u64_tag); assert(y._u64 == 0xCAFEBABEDEADBEEF); }; fn operators() void = { let x: (u8 | u16 | u32 | u64) = 42u8; assert(x is u8); x = 1337u16; assert(x is u16); x = 0xCAFEBABEu32; assert(x is u32); x = 0xCAFEBABEDEADBEEFu64; assert(x is u64); }; type signed = (i8 | i16 | i32 | i64 | int); type unsigned = (u8 | u16 | u32 | u64 | uint); type integer = (...signed | ...unsigned); fn reduction() void = { const a: (i8 | i16) = 42i8; const b: (i16 | i8) = a; const c: (i8 | i16 | i32) = a; const d: (i8 | i16 | i8 | i16) = a; compile(status::CHECK, // Cannot reduce to a single member "fn test() void = { let a: (u8 | u8) = 42u8; };" )!; compile(status::CHECK, // Cannot assign from more general type "fn test() void = { let a: (i8 | i16 | i32) = 42i8; let b: (i8 | i16) = a; };" )!; assert(a is i8 && b is i8 && c is i8 && d is i8); assert(size((i8 | i16 | i32)) == size((i8 | (i16 | i32)))); assert(size(integer) == size(signed)); assert(size(integer) != size((signed | unsigned))); const i: integer = 10i; assert(i is int); }; fn casts() void = { let a: (u8 | u16) = 42u16; assert(a as u16 == 42); let x = a: u8; assert(x == 42); const val = 0xBEEFu16; const is_little = (&val: *[2]u8)[0] == 0xEF; a = 0xCAFEu16; x = a: u8; assert(x == (if (is_little) 0xFEu8 else 0xCAu8)); // compile time static assert(4: (size | void) is size); static assert(4: (size | void) as size == 4z); static assert(4: (size | void): size == 4z); }; fn membercast() void = { // Simple case let x: (int | void) = void; let p = &x: *struct { id: uint, data: int, }; assert(p.id == void_tag); x = 1337; assert(p.id == int_tag); assert(p.data == 1337); // Align of 4 let x: (int | f32 | void) = 1337; let p = &x: *struct { id: uint, data: union { idata: int, fdata: f32, }, }; assert(p.id == int_tag); assert(p.data.idata == 1337); x = 13.37f32; assert(p.id == f32_tag); assert(p.data.fdata == 13.37f32); // Align of 8 let x: (size | void) = 1337z; let p = &x: *struct { id: uint, data: size, }; assert(p.id == size_tag); assert(p.data == 1337z); }; fn subsetcast() void = { // Equal alignment // subset -> superset let x: (size | void) = 1337z; let y: (size | int | void) = x; let p = &y: *struct { tag: u32, @offset(4) i: int, @offset(8) z: size, }; assert(p.tag == size_tag); assert(p.z == 1337z); // superset -> subset let x: (size | void | int) = 2z; assert(x: (size | void) as size == 2); assert(x as (size | void) as size == 2); assert(x is (size | void) && (x is size) && !(x is void)); // Disjoint alignment // subset -> superset let x: (int | void) = 1337; let y: (size | int | void) = x; let p = &y: *struct { tag: u32, @offset(4) i: int, @offset(8) z: size, }; assert(p.tag == int_tag); assert(p.i == 1337); // superset -> subset let x: (size | int | void) = 2i; assert(x: (int | void) as int == 2); assert(x as (int | void) as int == 2); assert(x is (int | void) && (x is int) && !(x is void)); }; type foo = (int | void); type bar = (size | foo); type t1 = t2; type t2 = int; type t3 = (t2 | void); type t4 = t2; type t5 = (t4 | void); fn castout() void = { let x: (int | void) = 1337; assert(x: int == 1337); assert(x as int == 1337); assert(x is int); // XXX: We can probably expand this let a: bar = 42i; assert(a as int == 42); assert(a: int == 42); assert(a is int); const a: t1 = 42; const x = a: t3; assert(x as t2 == 42); const x = a: t5; assert(x as t4 == 42); }; fn assertions() void = { let a: (u8 | u16) = 42u16; assert(a is u16); assert(a as u16 == 42u16); }; fn reject() void = { // cannot type assert into a disjoint tagged type compile(status::CHECK, "fn test() void = { let a: (u8 | u16) = 42u8; let b = a as (str | void); };" )!; // cannot type assert into non-member type compile(status::CHECK, "fn test() void = { let a: (u8 | u16) = 42u8; let b = a as *str; };" )!; // cannot type assert into superset compile(status::CHECK, "fn test() void = { let a: (u8 | u16) = 42u8; let b = a as (u8 | u16 | void); };" )!; // cannot type assert into the same type compile(status::CHECK, "fn test() void = { let a: (u8 | u16) = 42u8; let b = a as (u8 | u16); };" )!; // cannot have members of undefined size compile(status::CHECK, "fn test() (void | [*]int) = { void; };" )!; // cannot have <2 members compile(status::CHECK, "fn test() (void | void) = { void; };" )!; compile(status::CHECK, "fn test() ((void | void) | void) = { void; };" )!; compile(status::CHECK, "fn test() void = { let x = 3: (int | void) + 5: (int | void); };" )!; compile(status::CHECK, "fn test() void = { let x = 3: (int | void); x += 5: (int | void); };" )!; }; def val1: integer = 8u8; def val1val: u8 = val1 as u8; def val1type: bool = val1 is u8; def val2: integer = val1; def val2val: u8 = val2 as u8; def val2type: bool = val2 is u8; def val3: integer = 8u8: u16; def val3val: u16 = val3 as u16; def val3type: bool = val3 is u16; fn translation() void = { assert(val1 as u8 == 8u8); assert(val1val == 8u8); assert(val1type == true); assert(val2 as u8 == 8u8); assert(val2val == 8u8); assert(val2type == true); assert(val3 as u16 == 8u16); assert(val3val == 8u16); assert(val3type == true); }; export type align4 = (i32 | u32); export type align8 = (i32 | u32 | u64); export fn abi4(t4: align4) align4 = { assert(t4 as i32 == 1337); return t4; }; fn abi8_v4(t8: align8) align8 = { assert(t8 as i32 == 1337); return t8; }; fn abi8_v8(t8: align8) align8 = { assert(t8 as u64 == 1337); return t8; }; fn abi() void = { assert(abi4(1337i32) as i32 == 1337); assert(abi8_v4(1337i32) as i32 == 1337); assert(abi8_v8(1337u64) as u64 == 1337); }; export fn main() void = { measurements(); storage(); operators(); reduction(); casts(); membercast(); subsetcast(); castout(); assertions(); reject(); translation(); abi(); }; harec-0.24.2/tests/14-switch.ha000066400000000000000000000105541464473277600161110ustar00rootroot00000000000000use rt::{compile, status, toutf8}; type t = int; type e = !int; let x = 1337; fn basics() void = { let cases = [[0, 1], [1, 3], [10, 20], [11, 21], [12, 22], [13, 13]]; for (let i = 0z; i < len(cases); i += 1) { let x = cases[i][0]; let y: int = switch (x) { case 0 => yield x + 1; case 1 => yield x + 2; case => yield x; case 10, 11, 12 => yield x + 10; }; assert(y == cases[i][1]); }; let result = switch ("hare") { case "uhhh java" => abort(); case "hare" => yield true; case => abort(); }; assert(result); let y = &x; switch (y) { case &x => void; case => abort(); }; switch (y: *uint) { case &x: *uint => void; case => abort(); }; // assignability switch (0) { case 0i8 => void; case 1i16 => abort(); case 2: !int => abort(); case 3: t => abort(); case 4: !t => abort(); case => abort(); }; // regression test const x: e = 0; switch (x) { case 0 => void; case 1: e => abort(); case => abort(); }; compile(status::CHECK, " fn test() void = switch (0.0) { case 0.0 => void; case 1.0 => void; case => void; }; ")!; compile(status::CHECK, " fn test(x: int) void = switch (0) { case x => void; case => void; }; ")!; compile(status::CHECK, " fn test() void = switch (&0) { case &0 => void; case => void; }; ")!; compile(status::CHECK, " fn test() void = switch (0i32) { case 0i64 => void; case => void; }; ")!; }; fn tagged_result() void = { let x = 42; let y: (int | uint) = switch (x) { case 42 => yield 1337i; case => yield 1337u; }; assert(y is int); x = 24; y = switch (x) { case 42 => yield 1337i; case => yield 1337u; }; assert(y is uint); }; fn binding() void = { switch (1) { case => let x = 42; }; }; type a = enum { A, B, C }; type b = enum { A, B, C = B }; fn exhaustivity() void = { switch (true) { case true => void; case false => abort(); }; switch (a::A) { case a::A => void; case a::B, a::C => abort(); }; switch (a::A) { case a::B, a::C => abort(); case => void; }; switch (b::B) { case b::A => abort(); case b::C => void; }; switch (b::C) { case b::A => abort(); case b::B => void; }; let buf: [4096]u8 = [0...]; let buf = buf[..0]; static append(buf, toutf8("fn test() void = switch (0u8) {")...); for :outer (let i: u8 = '0'; i <= '2'; i += 1) { for (let j: u8 = '0'; j <= '9'; j += 1) { for (let k: u8 = '0'; k <= '9'; k += 1) { if (i == '2' && j == '5' && k == '6') { break :outer; }; static append(buf, toutf8("case ")...); if (i != '0') { static append(buf, i); }; if (i != '0' || j != '0') { static append(buf, j); }; static append(buf, k); static append(buf, toutf8("=> void;")...); }; }; }; static append(buf, toutf8("};")...); compile(status::SUCCESS, *(&buf: *str))!; compile(status::CHECK, " fn test() void = switch (0) { case 0 => void; }; ")!; compile(status::CHECK, " type x = enum { A, B, C }; fn test() void = switch (x::A) { case x::A => void; case x::B => void; }; ")!; }; fn duplicates() void = { compile(status::CHECK, " fn test() void = switch (0) { case 0 => void; case 1 => void; case 2, 0, 3 => void; case => void; }; ")!; compile(status::CHECK, " fn test() void = switch (0) { case 0 => void; case => void; case => void; }; ")!; compile(status::CHECK, " let x = 0; fn test() void = switch (&x) { case &x => void; case &x => void; case => void; }; ")!; compile(status::CHECK, " let x = ([1, 2, 3, 4], 5); fn test() void = switch (&0) { case &(x.0: [*]int)[4] => void; case &x.1 => void; case => void; }; ")!; // TODO: uncomment this once translation-time unions actually work //compile(status::CHECK, " // type t = union { x: [2]int, y: (int, int) }; // let x = t { ... }; // fn test() void = switch (&0) { // case &x.x[1] => void; // case &x.y.1 => void; // case => void; // }; //")!; compile(status::CHECK, " type x = enum { A, B, C = B }; fn test() void = switch (x::A) { case x::A => void; case x::B => void; case x::C => void; }; ")!; }; fn label() void = { switch :foo (0) { case 0 => if (true) { yield :foo; }; abort(); case => if (true) abort(); // unreachable // but still test that this branch inherits the label yield :foo; }; }; export fn main() void = { basics(); tagged_result(); binding(); exhaustivity(); duplicates(); label(); }; harec-0.24.2/tests/15-enums.ha000066400000000000000000000105151464473277600157350ustar00rootroot00000000000000use rt::{compile, status}; use testmod; type implicit_values = enum { ZERO, ONE, TWO, }; fn implicit() void = { assert(implicit_values::ZERO == 0); assert(implicit_values::ONE == 1); assert(implicit_values::TWO == 2); }; type explicit_values = enum { NEGONE = -1, SIXTEEN = 16, SEVENTEEN, EIGHTEEN, FIFTY = 50, }; fn explicit() void = { assert(explicit_values::NEGONE == -1); assert(explicit_values::SIXTEEN == 16); assert(explicit_values::SEVENTEEN == 17); assert(explicit_values::EIGHTEEN == 18); assert(explicit_values::FIFTY == 50); }; type with_storage = enum u16 { CAFE = 0xCAFE, BABE = 0xBABE, DEAD = 0xDEAD, BEEF = 0xBEEF, }; type rune_storage = enum rune { FOO = '0', BAR = '1', }; type uintptr_storage = enum uintptr { FOO = 0, BAR = 1, }; let global_uintptr: uintptr_storage = uintptr_storage::FOO; fn storage() void = { assert(size(explicit_values) == size(int)); assert(size(with_storage) == size(u16)); assert(size(uintptr_storage) == size(uintptr)); assert(align(explicit_values) == align(int)); assert(align(with_storage) == align(u16)); assert(align(uintptr_storage) == align(uintptr)); const val = 0xBEEFu16; const is_little = (&val: *[2]u8)[0] == 0xEF; assert(with_storage::CAFE: u8 == (if (is_little) 0xFEu8 else 0xCAu8)); assert(with_storage::BABE: u8 == (if (is_little) 0xBEu8 else 0xBAu8)); assert(with_storage::DEAD: u8 == (if (is_little) 0xADu8 else 0xDEu8)); assert(with_storage::BEEF: u8 == (if (is_little) 0xEFu8 else 0xBEu8)); assert(rune_storage::FOO == '0' && rune_storage::BAR == '1'); }; fn reject() void = { // enum type definition used outside type declaration compile(status::PARSE, "export let a: enum { A, B } = 0;")!; compile(status::PARSE, "export let a: int = 0: enum{A, B}: int;")!; // enum circular dependencies compile(status::CHECK, "type a = enum { A = B, B = A };")!; compile(status::CHECK, "type a = enum { A = b::B }, b = enum { B = a::A };")!; compile(status::CHECK, " def a: int = e::VAL1; type e = enum { VAL1 = a }; ")!; compile(status::CHECK, " def a: int = e::VAL1; type e = enum { VAL1 = VAL2, VAL2 = a }; ")!; // invalid storage compile(status::PARSE, "type a = enum f64 { A = 0.0 };")!; // invalid value compile(status::CHECK, "type a = enum { A = void };")!; }; type interdependent1 = enum { A = 0, B = interdependent2::A + 1, C = interdependent2::B + 1, }; type interdependent2 = enum { A = interdependent1::A + 1, B = interdependent1::B + 1, C = interdependent1::C + 1, }; fn interdependent() void = { assert(interdependent1::A == 0); assert(interdependent2::A == 1); assert(interdependent1::B == 2); assert(interdependent2::B == 3); assert(interdependent1::C == 4); assert(interdependent2::C == 5); }; type e1 = enum { a, b, c, d }, a1 = e1; type a2 = e2, e2 = enum u8 { a, b, c, d }; // reverse type imported_alias = testmod::_enum; type imported_double_alias = testmod::enum_alias; fn aliases() void = { assert(size(a1) == size(e1)); assert(a1::a == e1::a); assert(a1::b == e1::b); assert(a1::c == e1::c); assert(a1::d == e1::d); assert(size(a2) == size(e2)); assert(a2::a == e2::a); assert(a2::b == e2::b); assert(a2::c == e2::c); assert(a2::d == e2::d); // test with alias of imported enum assert(imported_alias::ONE == testmod::_enum::ONE); assert(imported_alias::TWO == testmod::_enum::TWO); assert(imported_alias::THREE == testmod::_enum::THREE); assert(imported_double_alias::ONE == testmod::_enum::ONE); assert(imported_double_alias::TWO == testmod::_enum::TWO); assert(imported_double_alias::THREE == testmod::_enum::THREE); // regression test: imported alias of enum where imported module's // namespace has multiple components assert(testmod::namespaced_alias::ONE == testmod::_enum::ONE); assert(testmod::namespaced_alias::TWO == testmod::_enum::TWO); assert(testmod::namespaced_alias::THREE == testmod::_enum::THREE); }; // Force T2 to be resolved before t2::T3 and t2::T3 before t2::T1 type t1 = enum { I1 = t2::T1, I2 = t2::T3, I3 = T2: int, }; def T2: uint = 0; type t2 = enum { T1, T2 = 1, T3 = T2 + 1, }; fn resolution_order() void = { assert(t2::T1 == 0); assert(t2::T2 == 1); assert(t2::T3 == 2); assert(t1::I1 == 0); assert(t1::I2 == 2); assert(t1::I3 == 0); assert(T2 == 0); }; export fn main() void = { implicit(); explicit(); storage(); reject(); interdependent(); aliases(); resolution_order(); }; harec-0.24.2/tests/16-defer.ha000066400000000000000000000071031464473277600156730ustar00rootroot00000000000000use rt; use rt::{compile, status}; let x: int = 10; fn basics() void = { assert(x == 10); defer x = 20; assert(x == 10); if (true) { return; }; defer x = 30; }; fn scope() void = { let x = 10; { defer x = 20; assert(x == 10); }; assert(x == 20); }; fn loops() void = { let x = 0; for (let i = 0; i < 5; i += 1) { defer x += 1; assert(x == i); }; assert(x == 5); }; fn control() void = { let x = 0; for (let i = 0; i < 5; i += 1) { if (true) { continue; }; defer x += 1; }; assert(x == 0); for (let i = 0; i < 5; i += 1) { defer x += 1; if (true) { break; }; abort(); }; assert(x == 1); defer { yield; }; defer { for (true) break; for (false) continue; }; }; fn reject() void = { let parse = [ "export fn main() void = defer 0;", "export fn main() void = { if (true) defer 0; };", "export fn main() void = { for (defer 0; true; true) void; };", "export fn main() void = { defer defer 0; };", ]; let check = [ "export fn main() void = { defer yield; };", "export fn main() void = :outer { defer { yield :outer; }; };", "export fn main() void = for (true) { defer break; };", "export fn main() void = for (true) { defer continue; };", "export fn main() void = { defer return; };", "export fn main() void = for :outer (true) { defer { break :outer; }; };", ]; for (let i = 0z; i < len(parse); i += 1) { compile(status::PARSE, parse[i])!; }; for (let i = 0z; i < len(check); i += 1) { compile(status::CHECK, check[i])!; }; }; fn _never() void = { { let x = 0; defer x = 1; abort(if (x == 0) yield else "defer ran too early"); }; defer { defer abort(); defer { let x = 0; defer x = 1; exit(x); }; }; defer x += 1; { x = 0; defer x += 1; // above defers are run before this executes, so this is never // actually reached abort(); }; }; fn exit(x: int) never = { assert(x == 0); defer rt::exit(x); (void: !void: (void | !void))!; // void x = 1; abort(); }; @fini fn fini() void = assert(x == 2); fn nested() void = { let sl: []size = []; defer free(sl); defer { assert(len(sl) == 7); for (let i = 0z; i < len(sl); i += 1) { assert(sl[i] == i); }; }; defer { defer append(sl, 6); defer if (true) { append(sl, 3); defer append(sl, 5); append(sl, 4); }; append(sl, 2); }; defer append(sl, 1); append(sl, 0); }; fn spam() void = { // regression test: ensure harec doesn't generate exponential IR here defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; defer spamfunc()!; }; fn spamfunc() (void | !void) = void; export fn main() void = { basics(); assert(x == 20); scope(); loops(); control(); reject(); nested(); spam(); _never(); }; harec-0.24.2/tests/17-alloc.ha000066400000000000000000000060021464473277600156760ustar00rootroot00000000000000type my_struct = struct { x: int, y: int, }; type my_struct_ptr = *my_struct; type my_int = int; type my_u32 = u32; fn allocation() void = { let x = alloc(1234); assert(*x == 1234); free(x); let y: nullable *int = alloc(1234); if (y != null) { assert(*(y: *int) == 1234); }; free(y); let z: my_struct_ptr = alloc(my_struct { x = 42, y = 69, }); assert(z.x == 42 && z.y == 69); free(z); let w: *my_int = alloc(1234); assert(*w == 1234); free(w); let a: *const my_u32 = alloc(1234u32); assert(*a == 1234); free(a); }; fn assignment() void = { let x = alloc(1234); *x = 4321; assert(*x == 4321); free(x); }; fn double_pointer() void = { let x = alloc(1234); let y = alloc(x); *x = 4321; assert(**y == 4321); **y = 1337; assert(*x == 1337); free(y); free(x); }; fn double_alloc() void = { let x = alloc(1234); let y = alloc(4321); assert(x != y && *x != *y); free(x); free(y); }; type aslice = []int; type aarray = [3]int; fn array() void = { let aa: *aarray = alloc([0...]); free(aa); let aa: nullable *aarray = alloc([0...]); free(aa); let aa: *aarray = alloc([0...]: aarray); free(aa); let aa: nullable *aarray = alloc([0...]: aarray); free(aa); let x: *[24]int = alloc([1, 2...]); assert(len(x) == 24); assert(x[0] == 1); for (let i = 1z; i < len(x); i += 1) { assert(x[i] == 2); }; free(x); }; fn slice() void = { let x: aslice = alloc([1, 2, 3]: aarray, 10); assert(len(x) == 3); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == (i + 1): int); }; free(x); let y: []int = alloc([1, 2, 3]); assert(len(x) == 3); for (let i = 0z; i < len(y); i += 1) { assert(y[i] == (i + 1): int); }; free(y); let z: []int = []; let p = &z: *struct { data: nullable *[*]int, length: size, capacity: size, }; assert(p.data == null && p.length == 0 && p.capacity == 0); let x: []int = alloc([1, 2, 3...], 42); defer free(x); assert(x[0] == 1); assert(x[1] == 2); assert(x[2] == 3); for (let i = 2z; i < len(x); i += 1) { assert(x[i] == 3); }; // ensure capacity is cast to size correctly let a: u32 = 4; let y: []u64 = alloc([1...], a); defer free(y); assert(len(y) == 4 && y[0] == 1 && y[3] == 1); }; fn slice_copy() void = { let x: []int = [1, 2, 3]; let p: []int = alloc(x...); defer free(p); assert(p: *[*]int != x: *[*]int); assert(len(p) == len(x)); for (let i = 0z; i < len(p); i += 1) { assert(p[i] == x[i]); }; let q: *[]int = alloc(x); defer free(q); assert((*q): *[*]int == x: *[*]int); let r: []int = alloc([1, 2, 3]...); defer free(r); assert(len(x) == len(r)); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == r[i]); }; }; fn string() void = { let x = struct { data: *[3]int = alloc([1, 2, 3]), length: size = 3, capacity: size = 3, }; let y = *(&x: *str); assert(len(y) == 3); free(y); }; fn _null() void = { let x: nullable *int = null; free(x); free(null); }; export fn main() void = { assignment(); allocation(); double_pointer(); double_alloc(); array(); slice(); slice_copy(); string(); _null(); }; harec-0.24.2/tests/18-match.ha000066400000000000000000000103431464473277600157040ustar00rootroot00000000000000type foo = void; type bar = void; type foobar = (foo | bar); type baz = int; type foobarbaz = (foobar | baz); type signed = (i8 | i16 | i32 | i64 | int); type unsigned = (u8 | u16 | u32 | u64 | uint | size); type integer = (...signed | ...unsigned); type align_4 = (void | int); type align_8 = (void | int | i64); type aint = int; type bint = aint; fn tagged() void = { let cases: [3](int | uint | str) = [10i, 10u, "hello"]; let expected: [_]size = [1, 2, 5]; for (let i = 0z; i < len(cases); i += 1) { let y: size = match (cases[i]) { case int => yield 1; case uint => yield 2; case let s: str => yield len(s); }; assert(y == expected[i]); }; }; fn _never() void = { let x: (int | uint | str) = 1337i; for (true) { let y: int = match (x) { case int => yield 42; case uint => abort(); case str => break; }; assert(y == 42); x = "hi"; }; }; fn _default() void = { let x: (int | uint | str) = 1337u; let y: int = match (x) { case int => yield 42; case => yield 24; }; assert(y == 24); }; fn pointer() void = { let x = 42; let y: nullable *int = &x; let z: int = match (y) { case let y: *int => yield *y; case null => abort(); }; assert(z == 42); y = null; z = match (y) { case *int => abort(); case null => yield 1337; }; assert(z == 1337); }; fn alias() void = { let cases: []foobar = [foo, bar]; let expected = [42, 24]; for (let i = 0z; i < len(cases); i += 1) { let y: int = match (cases[i]) { case foo => yield 42; case bar => yield 24; }; assert(y == expected[i]); }; }; fn tagged_result() void = { let x: (int | void) = 42i; let y: (int | void) = match (x) { case let x: int => yield x; case let x: void => yield x; }; assert(y is int); x = void; y = match (x) { case let x: int => yield x; case let x: void => yield x; }; assert(y is void); // XXX: i'm like 90% sure that while implementing // https://todo.sr.ht/~sircmpwn/hare/871 this will cause a null pointer // dereference match (x) { case int => abort(); case => void; }; }; fn implicit_cast() void = { let x: foobar = foo; let y: nullable *int = null; let a: (int | foobar) = match (y) { case null => yield foo; case let z: *int => yield *z; }; assert(a is foobar); }; fn transitivity() void = { let x: (foobar | int) = 10; match (x) { case let i: int => assert(i == 10); case foo => abort(); case bar => abort(); }; x = foo; let visit = false; match (x) { case int => abort(); case foo => visit = true; case bar => abort(); }; assert(visit); x = bar; visit = false; match (x) { case int => abort(); case foo => abort(); case foobar => visit = true; }; assert(visit); visit = false; match (x) { case let z: (foo | bar) => visit = true; assert(z is bar); case int => abort(); }; assert(visit); let y: foobarbaz = 10; visit = false; match (y) { case baz => visit = true; case foo => abort(); case bar => abort(); }; assert(visit); y = foo; visit = false; match (y) { case baz => abort(); case foo => visit = true; case bar => abort(); }; assert(visit); let z: (bint | void) = 10; match (z) { case aint => void; case void => abort(); }; }; fn numeric() void = { // Real-world test let visit = true; let x: integer = 1337i; match (x) { case let s: signed => match (s) { case let i: int => visit = true; assert(i == 1337); case => abort(); }; case let u: unsigned => abort(); }; assert(visit); }; fn alignment_conversion() void = { let x: align_8 = 1234i; match (x) { case let y: align_4 => assert(y as int == 1234); case => abort(); }; let y: align_4 = 4321i; x = y: align_8; assert(x as int == 4321); }; fn binding() void = { let x: (int | void) = void; match (x) { case => let x = 42; }; }; fn label() void = { match :foo (0: (int | void)) { case int => if (true) { yield :foo; }; abort(); case => if (true) abort(); // unreachable // but still test that this branch inherits the label yield :foo; }; }; export fn main() void = { tagged(); _never(); _default(); pointer(); alias(); tagged_result(); implicit_cast(); transitivity(); numeric(); alignment_conversion(); binding(); label(); // TODO: Test exhaustiveness and dupe detection }; harec-0.24.2/tests/19-append.ha000066400000000000000000000060041464473277600160570ustar00rootroot00000000000000use rt::{compile, status}; fn basics() void = { let x: []int = []; append(x, 1); append(x, 2); append(x, 3); assert(len(x) == 3); assert(x[0] == 1 && x[1] == 2 && x[2] == 3); free(x); }; fn multi() void = { let x: []int = []; append(x, [1, 2, 3]...); assert(len(x) == 3); let y: []int = [4, 5, 6]; append(x, y...); assert(len(x) == 6); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == i: int + 1); }; free(x); let x: []int = alloc([], 3); append(x, &[1, 2, 3]...); assert(len(x) == 3); free(x); }; fn _static() void = { let buf: [32]int = [0...]; let x = buf[..0]; static append(x, 1); static append(x, 2); static append(x, 3); assert(len(x) == 3); static append(x, [4, 5, 6]...); assert(len(x) == 6); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == i: int + 1); assert(buf[i] == i: int + 1); }; let x = [1, 2, 3][..0]; static append(x, &[1, 2, 3]...); assert(len(x) == 3); }; fn withlength() void = { let x: []int = []; append(x, [42...], 10); assert(len(x) == 10); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == 42); }; free(x); }; fn typehints() void = { let x: []u8 = []; append(x, 42); append(x, [42]...); append(x, [42...], 3); assert(len(x) == 5); for (let i = 0z; i < 5; i += 1) { assert(x[i] == 42u8); }; free(x); }; fn reject() void = { compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; let y: int = 42; append(x, y); }; ")!; // object member type != value type compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; let y = 42u8; append(x, y...); }; ")!; // value is not an array or a slice compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; let y: []int = [42]; append(x, y...); }; ")!; // object member type != value member type compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; append(x, [42i...], 5); }; ")!; // same as above, but for an expression with length compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; append(x, [0u8...], 2i); }; ")!; // length expression is not assignable to size compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; append(x, [42], 3); }; ")!; // must be an expandable array compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; let x: nullable *[]u8 = &x; append(x, 42); }; ")!; // object member type is nullable pointer compile(status::PARSE, " fn test() void = { let x: []u8 = []; append(x, [0...]..., 3); }; ")!; // ellipsis with length compile(status::CHECK, " fn test() void = { let x: []u8 = []; append(x, [1]: [*]u8...); }; ")!; // unbounded array value compile(status::CHECK, " let y: opaque; fn test() void = { let x = []: []u8: []opaque; append(x, y); }; ")!; // value has undefined size }; fn _never() void = { let x: []int = []; { append(x, yield); }; { append(x, [0...], yield); }; }; export fn main() void = { basics(); multi(); _static(); withlength(); typehints(); reject(); _never(); }; harec-0.24.2/tests/20-if.ha000066400000000000000000000025361464473277600152040ustar00rootroot00000000000000use rt; fn equality() void = { assert((if (2 == 2) true else false)); }; fn inequality() void = { assert((if (2 == 1) false else true)); }; fn else_() void = { assert((if (2 == 1) false else true)); }; fn elseif() void = { assert((if (2 == 1) false else if (2 == 2) true else false)); }; fn justif() void = { let x: int = 1; if (true) { // asserts that the side-effects work x = 0; }; assert(x == 0); }; fn gt() void = { assert((if (1 > 2) false else true)); }; fn gte() void = { assert((if (2 >= 2) true else false)); }; fn lt() void = { assert((if (1 < 2) true else false)); }; fn lte() void = { assert((if (2 <= 2) true else false)); }; fn and2(left: bool) bool = { return if (left && 1 / 0 == 0) false else true; }; fn and() void = { assert((if (true && and2(1 == 1 && false)) true else false)); }; fn or() void = { assert((if (true || 1 / 0 == 0) true else false)); }; fn tagged() void = { assert((if (true) 1u8 else 0i8) as u8 == 1); assert((if (false) 1u8 else 0i8) as i8 == 0); }; type abool = bool; fn alias() void = { if (true: abool) { return; }; abort("unreachable"); }; fn _never() never = { if (true) { rt::exit(0); } else { abort(); }; }; export fn main() void = { equality(); inequality(); else_(); elseif(); justif(); gt(); gte(); lt(); lte(); and(); or(); tagged(); alias(); _never(); }; harec-0.24.2/tests/21-tuples.ha000066400000000000000000000142621464473277600161220ustar00rootroot00000000000000use rt::{compile, status}; def CONST1: (int, str) = (15, "foo"); def CONST2: [_](int, str) = [(15, "foo"), (30, "bar")]; fn storage() void = { let x: (int, size) = (42, 1337); assert(size((int, size)) == size(size) * 2); assert(size((int, (u8, size))) == size(size) * 3); assert(align((int, size)) == align(size)); assert(align((int, (u8, size))) == align(size)); let ptr = &x: *struct { i: int, z: size }; assert(ptr.i == 42 && ptr.z == 1337); assert(size((i8, int, size)) == 2 * size(size)); assert(size((int, i8, size)) == 2 * size(size)); assert(size((size, int, i8)) == 2 * size(size)); assert(size((size, i8, int)) == 2 * size(size)); assert(size((i8, size, int)) == 3 * size(size)); assert(size((int, size, i8)) == 3 * size(size)); let x: (int, void, size) = (42, void, 1337); assert(size((int, void, size)) == size((int, size))); assert(size((int, (void, size))) == size((int, size))); assert(align((int, void, size)) == align(size)); assert(align((int, (void, size))) == align(size)); let ptr = &x: *struct { i: int, z: size }; assert(ptr.i == 42 && ptr.z == 1337); let x: (void, void) = (void, void); assert(size((void, void)) == 0); }; fn indexing() void = { let x: ((int, uint), size) = ((42, 69), 1337); assert(x.0.0 == 42); assert(x.0.1 == 69); assert(x.1 == 1337); assert(x.1z == 1337); assert(x.0x1 == 1337); assert(x.1e+0 == 1337); }; fn assignment() void = { let x = (42, 1337z); assert(x.0 == 42 && x.1 == 1337); x.0 = 1337; x.1 = 42; assert(x.0 == 1337 && x.1 == 42); let x = (void, void); x.0 = x.1; x.1 = void; }; fn func(in: (int, size)) (int, size) = (in.0 + 1, in.1 + 1); fn eval_expr_access() void = { static assert((42, 0).0 == 42 && (42, 0).1 == 0); static assert(CONST1.0 == 15 && CONST1.1 == "foo"); static assert(CONST2[0].0 == 15 && CONST2[0].1 == "foo"); static assert(CONST2[1].0 == 30 && CONST2[1].1 == "bar"); }; fn eval_expr_tuple() void = { static let t = (42, 8); }; fn funcs() void = { let x = func((41, 1336)); assert(x.0 == 42 && x.1 == 1337); }; fn unpacking_static() int = { static let (a, b) = (0, 0); a += 1; b += 1; return a; }; fn unpacking_demo() (int, int) = { return (10, 20); }; fn unpacking_eval() (int, int) = { static let i = 0; const res = (10 + i, 20 + i); i += 1; return res; }; let unpacking_global: int = 0i; fn unpacking_addone() int = { unpacking_global += 1; return unpacking_global; }; type tuple_alias = (int, int); fn unpacking() void = { const (a, b, c) = (42, 8, 12); assert(a == 42); assert(b == 8); assert(c == 12); const (a, b): (i64, u64) = (2i, 4z); assert(a == 2i64); assert(b == 4u64); const (a, b, c): (i64, str, f64) = (2i, "hello", 1.0); assert(a == 2i64); assert(b == "hello"); assert(c == 1.0f64); let (a, b): (i64, u64) = (1i, 3z); a += 1; b += 1; assert(a == 2i64); assert(b == 4u64); const (_, b, c) = (1, 2, 3); assert(b == 2); assert(c == 3); const (a, _, c) = (1, 2, 3); assert(a == 1); assert(c == 3); const (a, b, _) = (1, 2, 3); assert(a == 1); assert(b == 2); const t: tuple_alias = (1, 2); const (a, b) = t; assert(a == 1); assert(b == 2); unpacking_static(); unpacking_static(); const a = unpacking_static(); assert(a == 3); const (a, b) = unpacking_demo(); assert(a == 10); assert(b == 20); const (a, b) = unpacking_eval(); assert(a == 10); assert(b == 20); let (a, b, _, d) = (unpacking_addone(), unpacking_addone(), unpacking_addone(), unpacking_addone()); assert(a == 1 && b == 2 && d == 4); }; // Regression tests for miscellaneous compiler bugs fn regression() void = { let a: (((int | void), int) | void) = (void, 0); let x = (1, 0); x = (2, x.0); assert(x.0 == 2 && x.1 == 1); }; fn reject() void = { let parse = [ // unpack with def "fn t() void = { def (x, y) = (1, 2); };", // empty tuple "fn t() void = { let a: (() | void) = void; };", "fn t() void = { let a = (); };", "fn t() void = { let () = (); };", // one member "fn t() void = { let a: ((int) | void) = void; };", // null type "fn t() void = { let a: ((null, int) | void) = void; };", // invalid field access "fn t() void = { let a = (0, 1, 2); a.-1; };", "fn t() void = { let a = (0, 1, 2).-1; };", ]; let check = [ // no name in unpack "fn t() void = { let (_, _) = (1, 2); };", // unpack of non-tuple type "fn t() void = { let (x, y) = 5; };", "fn t() void = { let (a, b): int = (2, 3); };", // static unpack "fn getval() int = 5; fn t() void = { static let (a, b) = (2, getval()); };", "fn getval() int = 5; fn t() void = { static let (a, _) = (2, getval()); };", // member count mismatch "fn t() void = { let a: (u8, u8, u8) = (2, 3); };", "fn t() void = { let a: (u8, u8) = (2, 3); let b: (u8, u8, u8) = a; };", "fn t() void = { let a: (u8, u8) = (2, 3, 4); };", "fn t() void = { let a: (u8, u8, u8) = (2, 3, 4); let b: (u8, u8) = a; };", "fn t() void = { let (x, y) = (1, 2, 3); };", "fn t() void = { let (x, y, z) = (1, 2); };", "fn t() void = { let (x, y, _) = (1, 2); };", "fn t() void = { let (x, _, _) = (1, 2); };", // one member "fn t() void = { let a = (4u); a.0; };", "fn t() void = { let (a) = (4u); a.0; };", "fn t() void = { let (a) = 4u; a.0; };", // member of undefined size "fn t() void = { let a: (([*]int, int) | void) = void; };", "fn t() void = { let a = (t, 2); };", "fn t() void = { let (a, b) = (t, 2); };", "fn t() void = { let (_, b) = (t, 2); };", // null type member "fn t() void = { let a = (null, 2); };", "fn t() void = { let (a, b) = (null, 2); };", "fn t() void = { let (_, b) = (null, 2); };", // invalid field access "fn t() void = { let a = (0, 1, 2); a.3; };", "fn t() void = { let a = (0, 1, 2).3; };", // arithmetic on tuples "fn t() void = { let a = (0, 1) + (2, 3); };", "fn t() void = { let a = (0, 1); a += (2, 3); };", ]; for (let i = 0z; i < len(parse); i += 1) { compile(status::PARSE, parse[i])!; }; for (let i = 0z; i < len(check); i += 1) { compile(status::CHECK, check[i])!; }; }; fn _never() void = { { let x: (int, int) = (yield, 1); }; { let (y, z): (int, int) = (yield, 1); }; }; export fn main() void = { storage(); indexing(); assignment(); funcs(); eval_expr_tuple(); eval_expr_access(); unpacking(); regression(); reject(); _never(); }; harec-0.24.2/tests/22-delete.ha000066400000000000000000000014121464473277600160420ustar00rootroot00000000000000use rt; type my_slice = []int; fn index() void = { let x: []int = alloc([1, 2, 3, 4, 5]); let y = &x: *my_slice; delete(x[1]); assert(len(x) == 4); assert(x[0] == 1 && x[1] == 3 && x[2] == 4 && x[3] == 5); static delete(y[3]); assert(len(x) == 3); delete(y[0]); assert(len(x) == 2); assert(x[0] == 3 && x[1] == 4); free(x); }; fn slice() void = { let x: my_slice = alloc([1, 2, 3, 4, 5]); let y = &x; const s = y: *rt::slice; delete(x[..3]); assert(len(x) == 2); assert(x[0] == 4 && x[1] == 5); delete(x[len(x)..]); static delete(y[..]); assert(len(x) == 0); assert(s.capacity < 5); append(x, [6, 7, 8, 9]...); delete(x[1..3]); assert(len(x) == 2); assert(x[0] == 6 && x[1] == 9); free(x); }; export fn main() void = { index(); slice(); }; harec-0.24.2/tests/23-errors.ha000066400000000000000000000042421464473277600161210ustar00rootroot00000000000000use rt::{compile, status}; type err_int = !int; fn assignability() void = { // Error and non-error types are interchangable: let a: !int = 10; let b: int = a; assert(a == b); }; type error = !void; fn err_if_false(in: bool) (error | int) = { if (in) { return 1337; }; return error; }; fn indirect(in: bool) (error | int) = { let x = err_if_false(in)?; return x; }; fn propagate() void = { assert(indirect(true) as int == 1337); assert(indirect(false) is error); }; fn cannotignore() void = { compile(status::CHECK, " type error = !void; export fn main() int = { error; return 42; }; ")!; err_if_false(true)!; }; fn void_assignability() void = { compile(status::CHECK, ` type err = !void; fn reterr() (int | err) = { return err; }; fn properr() void = { reterr()?; }; export fn main() void = void; `)!; // error types cannot be assigned to void compile(status::CHECK, ` fn disallow_1() void = { return "I am illegal"; }; fn disallow_2() void = { return 12; }; export fn main() void = void; `)!; // non-void types cannot be assigned to void }; fn measurements() void = { assert(size(!int) == size(int)); assert(size(!f64) == size(f64)); assert(size(!(int | void)) == size((int | void))); assert(size(!(i8, rune)) == size((i8, rune))); assert(size(!struct { x: int, y: str }) == size(struct { x: int, y: str })); assert(size(!union { x: int, y: str }) == size(union { x: int, y: str })); assert(size(![2]int) == size([2]int)); assert(size(![]int) == size([]int)); assert(size(!*size) == size(*size)); assert(align(!int) == align(int)); assert(align(!f64) == align(f64)); assert(align(!(int | void)) == align((int | void))); assert(align(!(i8, rune)) == align((i8, rune))); assert(align(!struct { x: int, y: str }) == align(struct { x: int, y: str })); assert(align(!union { x: int, y: str }) == align(union { x: int, y: str })); assert(align(![2]int) == align([2]int)); assert(align(![*]int) == align([*]int)); assert(align(![]int) == align([]int)); assert(align(!*size) == align(*size)); }; export fn main() void = { assignability(); propagate(); cannotignore(); void_assignability(); measurements(); }; harec-0.24.2/tests/24-imports.ha000066400000000000000000000026301464473277600163020ustar00rootroot00000000000000use rt::{compile, status}; use testmod; use alias = testmod; use testmod::*; static assert(testmod::val == 42); static assert(testmod::val == alias::val); static assert(testmod::val == val); static assert(testmod::val2 == val2); static assert(testmod::other::EIGHT == 8); let x: int = testmod::val; let y: u8 = testmod::val; fn reject() void = { compile(status::USER, " use wrong; export fn main() void = { testmod::val; }; ")!; compile(status::CHECK, " use testmod::{val}; export fn main() void = static assert( testmod::val == 42 ); ")!; compile(status::CHECK, " use testmod::{val}; export fn main() void = static assert( val2 == 90 ); ")!; compile(status::PARSE, " use testmod; use test = testmod::*; export fn main() void = void; ")!; compile(status::PARSE, " use testmod*; export fn main() void = void; ")!; compile(status::PARSE, " use testmod{val}; export fn main() void = void; ")!; compile(status::PARSE, " use a::b = testmod; export fn main() void = void; ")!; compile(status::PARSE, " use test:: = testmod; export fn main() void = void; ")!; compile(status::PARSE, " use testmod::; export fn main() void = void; ")!; compile(status::PARSE, " use test = testmod::; export fn main() void = void; ")!; compile(status::CHECK, " use testmod; export fn main() void = { testmod::testmod::val3; }; ")!; }; export fn main() void = { reject(); }; harec-0.24.2/tests/25-promotion.ha000066400000000000000000000004041464473277600166310ustar00rootroot00000000000000export fn main() void = { assert(0xFFu8 << 8u16 == 0xFF00u16); assert(0xFFu8 << 8u8 == 0u8); assert(0xFFu8 << 24u32 == 0xFF000000u32); assert(0xFFu8 << 32u32 == 0xFFu32); assert(0xFFu8 << 32u64 == 0xFF00000000u64); assert(0xFFu8 << 64u64 == 0xFFu64); }; harec-0.24.2/tests/26-regression.ha000066400000000000000000000111771464473277600167750ustar00rootroot00000000000000// Miscellaneous regression tests use rt; use rt::{compile, status}; type embedded = struct { a: u64, b: u8, }; type thing = struct { offs: u64, e: embedded, }; def THING: thing = thing{ offs = 0, e = embedded { a = 1, b = 0, }, }; type mod::t = (size | const size); // needs to have a type id larger than size // order of these three matters type b = struct { c: c }; type a = struct { b }; type c = *a; // order of these two matters let packedsz = size(packed); type packed = struct @packed { a: u64, b: u8 }; type d = [3]int; type e = bool; let x = [1, 2, 3]: d: []int; static assert(true: e == true: e); static assert('a' == 'a'); static assert(~0u8 == 0xffu8); static assert(-2u8 == 0xfeu8); fn control_never() void = { let x = { yield yield 0; }; assert(x == 0); x = { return yield 1; }; assert(x == 1); x = { let x: int = yield 2; abort(); }; assert(x == 2); { for (true; yield) x += 1; }; assert(x == 3); { for (let i: int = yield; true) x += 1; }; assert(x == 3); { x = yield; x = 4; }; assert(x == 3); { x += yield; abort(); }; assert({ let x = true; x &&= yield x; abort(); }); if (false) { let x: int = abort(); x += abort(); let x = true; x &&= abort(); }; control_never1() as int; control_never2() as int; }; fn control_never1() (int | void) = { return return 0; }; fn control_never2() (int | void) = { { yield return 0; }; }; export fn main() void = { let t = thing { offs = 0, e = embedded { a = 1, b = 0, }, }; let t = t; assert(t.e.a == 1); let t2 = THING; assert(t2.e.a == 1); t2.offs = 42; assert(THING.offs == 0); let x: (void | int) = 10; match (x) { case let i: int => assert(i == 10); case void => abort(); }; let p = 0; let p = &p: uintptr: u64: (u64 | void); let p = match (p) { case void => abort(); case let p: u64 => yield p: uintptr: *int; }; assert(*p == 0); let thing: int = 0; let thing = &thing: (*int | int); let p = match (thing) { case int => abort(); case let p: *int => yield p; }; *p = 0; match (void: (void | !void)) { case void => void; case !void => abort(); }; let s: []f64 = [1.0, 2.0, 3.0]; s[..] = [0.0...]; compile(status::CHECK, " fn a() void = switch (b) { case &c => void; };" )!; compile(status::PARSE, "let a;")!; compile(status::CHECK, " type a = struct { b: int, c: int, }; def A: a = a { b = 0 };" )!; compile(status::CHECK, "let a = &0;")!; compile(status::CHECK, "def A: a = 1 % 1;")!; compile(status::CHECK, "def A: b = void;")!; static assert(true == true && true != false); compile(status::CHECK, " type a = str; type b = struct { a }; def A = b { c = 0 };" )!; compile(status::CHECK, " def A = 0; fn a() void = A = 0;" )!; compile(status::CHECK, "def A = x && true;")!; compile(status::CHECK, "type a = struct { b: fn() void };")!; compile(status::CHECK, "fn a() []int = alloc([]: [*]int, 0);")!; compile(status::CHECK, "fn a() [1]int = [1]: []int: [1]int;")!; compile(status::CHECK, "fn a() void = &*&a;")!; compile(status::CHECK, "let a = [*&0];")!; compile(status::CHECK, "fn a() *opaque = alloc(void);")!; compile(status::CHECK, "fn a() void = { static let b = x & struct { a: int = 0 }; };")!; let a: (size | mod::t) = 0; assert(0xffu8 + 1 >> 1 == 0); compile(status::CHECK, "type a = *...b; type b = *...a;")!; compile(status::CHECK, "def A = len([1]: []str);")!; control_never(); compile(status::CHECK, "fn a() void = { abort(): int; };")!; // identifier exceeds maximum length let buf: [1024]u8 = [0...]; let buf = buf[..0]; static append(buf, rt::toutf8("let a")...); // IDENT_MAX (in identifier.h) is defined as 255 for (let i = 0z; i < 255 / 2; i += 1) { static append(buf, rt::toutf8("::a")...); }; const n = len(buf); static append(buf, rt::toutf8(" = 0;")...); compile(status::SUCCESS, *(&buf: *str))!; static insert(buf[n], rt::toutf8("::a")...); compile(status::PARSE, *(&buf: *str))!; assert(size(packed) == packedsz); assert(size(packed) == 9); let r = 'a'; r: i64; :outer { let x: []int = { if (true) yield :outer; yield []; }; abort(); }; compile(status::CHECK, "fn f() void = { let x = 0; x { ... }; };")!; compile(status::CHECK, "static assert(size(b) == 0); let b = 0;")!; assert((if (false) 0) is void); let v = if (false) 0; assert(v is void); assert((if (true) 0) is int); v = if (true) 0; assert(v is int); compile(status::CHECK, "fn a() void = { offset(a.b); };")!; compile(status::CHECK, "fn f() void = { let x: []u8 = &0: *[*]u8; };")!; compile(status::CHECK, "fn f(x: t = 2u) void = void;")!; compile(status::CHECK, "fn f() void = { for (let i = return; true) void; };")!; }; harec-0.24.2/tests/27-rt.ha000066400000000000000000000031241464473277600152340ustar00rootroot00000000000000// tests for the test runtime itself use rt; fn compile() void = { rt::compile(rt::status::SUCCESS, " fn test() void = { void; };" )!; rt::compile(rt::status::CHECK, " fn test() void = { let a: int = [1, 2, 3]; };" )!; }; let global = 0i; fn foofn() str = { global += 1; return "foo"; }; def FOO: str = "foo"; type s = str; type b = bool; static assert(true); static assert(true, "msg"); fn assert_() void = { let foo = "foo"; static assert(true, "foo"); static assert(true: b, "foo"); static assert(true, "foo": s); static assert(true, FOO); static assert(true: b, FOO); static assert(true, FOO: s); assert(true, "foo"); assert(true: b, "foo"); assert(true, "foo": s); assert(true, FOO); assert(true: b, FOO); assert(true, FOO: s); assert(true, foo); assert(true: b, foo); assert(true, foo: s); assert(true, foofn()); assert(true: b, foofn()); assert(true, foofn(): s); assert(global == 0); // no side effects if assertion succeeds let failures: [_]str = [ // types "fn f() void = { assert(5); };", "fn f() void = { assert(true, 5); };", "fn f() void = { static assert(5); };", "fn f() void = { static assert(true, 5); };", // compile-time eval "fn f() void = { let a = true; static assert(a); };", "fn f() void = { let a = \"foo\"; static assert(true, a); };", // top-level static assertions "static assert(false);", ]; for (let i = 0z; i < len(failures); i += 1) { rt::compile(rt::status::CHECK, failures[i])!; }; rt::compile(rt::status::PARSE, "export static assert(true);")!; }; export fn main() void = { assert_(); compile(); }; harec-0.24.2/tests/28-insert.ha000066400000000000000000000065341464473277600161240ustar00rootroot00000000000000use rt::{compile, status}; fn basics() void = { let x: []int = alloc([1, 2, 5]); let y = &x; insert(x[2], 4); insert(x[2], 3); assert(len(x) == 5); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == i: int + 1); }; insert(y[0], 42); assert(len(x) == 6 && x[0] == 42); free(x); }; fn multi() void = { let x: []int = alloc([1, 2, 5]); insert(x[2], [3, 4]...); assert(len(x) == 5); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == i: int + 1); }; free(x); let x: []int = alloc([1, 2, 5]); let y: []int = [3, 4]; insert(x[2], y...); assert(len(x) == 5); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == i: int + 1); }; free(x); let x: []int = alloc([], 3); insert(x[0], &[1, 2, 3]...); assert(len(x) == 3); free(x); }; fn _static() void = { let buf: [32]int = [1, 2, 5, 42...]; let x = buf[..3]; static insert(x[2], 4); static insert(x[2], 3); assert(len(x) == 5); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == i: int + 1); assert(buf[i] == i: int + 1); }; let z: []int = [1, 2, 3, 4]; static insert(x[2], [1, 2, 3, 4]...); static insert(x[2], z...); for (let i = len(x); i < len(buf); i += 1) { assert(buf[i] == 42); }; let x = [1, 2, 3][..0]; static insert(x[0], &[1, 2, 3]...); assert(len(x) == 3); }; fn withlength() void = { let x: []size = alloc([0, 0, 2, 2]); insert(x[2], [1...], 2); assert(len(x) == 6); for (let i = 0z; i < len(x); i += 1) { assert(x[i] == i / 2); }; free(x); }; fn typehints() void = { let x: []u8 = []; insert(x[0], 42); insert(x[1], [42]...); assert(len(x) == 2); assert(x[0] == 42u8); assert(x[1] == 42u8); free(x); }; fn reject() void = { compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; let y: int = 42; insert(x[1], y); }; ")!; // object member type != value type compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; let y = 42u8; insert(x[1], y...); }; ")!; // value is not an array or a slice compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; let y: []int = [42]; insert(x[1], y...); }; ")!; // object member type != value member type compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; insert(x[1], [42i...], 3); }; ")!; // same as above, but for an expression with length compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; insert(x[1], [0u8...], 2i); }; ")!; // length expression is not assignable to size compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; insert(x[1], [42], 3); }; ")!; // must be an expandable array compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; let x: nullable *[]u8 = &x; insert(x[1], 42); }; ")!; // object member type is nullable pointer compile(status::PARSE, " fn test() void = { let x: []u8 = [0u8]; insert(x[1], [0...]..., 3); }; ")!; // ellipsis with length compile(status::CHECK, " fn test() void = { let x: []u8 = [0u8]; insert(x[1], [1]: [*]u8...); }; ")!; // unbounded array value compile(status::CHECK, " let y: opaque; fn test() void = { let x = []: []u8: []opaque; insert(x[0], y); }; ")!; // value has undefined size }; fn _never() void = { let x: []int = []; insert(x[0], return); abort(); }; export fn main() void = { basics(); multi(); _static(); withlength(); typehints(); reject(); _never(); }; harec-0.24.2/tests/29-unarithm.ha000066400000000000000000000022111464473277600164340ustar00rootroot00000000000000use rt::{compile, status}; type abool = bool; fn lnot() void = { assert(!(false: abool)); }; fn addr() void = { let x = 42; let xptr = &x; assert(*xptr == 42); let y = &69; assert(*y == 69); let z = &struct { a: int = 42 }; assert(z.a == 42); let q = &{ yield 42; }; assert(*q == 42); *{ yield q; } = 12; assert(*q == 12); compile(status::CHECK, " export fn main() void = { &null; }; ")!; compile(status::CHECK, " export fn main() void = { &void; }; ")!; compile(status::CHECK, " type foo = void; export fn main() void = { &foo; }; ")!; compile(status::CHECK, " fn f() void = void; export fn main() void = { &f(); }; ")!; }; fn neg() void = { let a = -2; assert(a == 0 - 2); let b = 1-1; assert(b == 0); }; fn deref() void = { assert(*{ yield &42; } == 42); assert(*switch (0) { case => yield &42; } == 42); assert(*match (0: (int | void)) { case => yield &42; } == 42); compile(status::CHECK, " export fn main() void = { *static assert(true); }; ")!; compile(status::PARSE, " export fn main() void = { *if (true) &42 else &1337; }; ")!; }; export fn main() void = { lnot(); addr(); neg(); deref(); }; harec-0.24.2/tests/30-reduction.c000066400000000000000000000113151464473277600164300ustar00rootroot00000000000000#include #include #include #include #include #include "check.h" #include "identifier.h" #include "lex.h" #include "parse.h" #include "scope.h" #include "type_store.h" #include "typedef.h" #include "types.h" #include "util.h" void test(struct context *ctx, const char *expected, const char *input) { // The target is irrelevant here, since it's only used to compute the // sizes of types, which isn't tested here or depended on. builtin_types_init("x86_64"); ctx->errors = NULL; ctx->next = &ctx->errors; sources = (const char *[2]){"", input}; const struct type *etype = NULL; if (strlen(expected) != 0) { FILE *ebuf = fmemopen((char *)expected, strlen(expected), "r"); struct lexer elex; lex_init(&elex, ebuf, 0); struct ast_type *eatype = parse_type(&elex); etype = type_store_lookup_atype(ctx, eatype); } FILE *ibuf = fmemopen((char *)input, strlen(input), "r"); struct lexer ilex; lex_init(&ilex, ibuf, 1); struct ast_expression *iaexpr = parse_expression(&ilex); struct expression iexpr = {0}; check_expression(ctx, iaexpr, &iexpr, NULL); if (etype == NULL) { assert(ctx->errors != NULL); return; } struct errors *error = ctx->errors; while (error) { fprintf(stderr, "%s:%d:%d: error: %s\n", sources[error->loc.file], error->loc.lineno, error->loc.colno, error->msg); struct errors *next = error->next; free(error); error = next; } if (ctx->errors) { exit(EXIT_FAILURE); } if (etype->id != iexpr.result->id) { fprintf(stderr, "Expected expression %s to have type ", input); emit_type(etype, stderr); fprintf(stderr, ", got "); emit_type(iexpr.result, stderr); fprintf(stderr, "\n"); exit(EXIT_FAILURE); } } int main(void) { struct context ctx = {0}; static type_store ts = {0}; struct modcache *modcache[MODCACHE_BUCKETS]; memset(modcache, 0, sizeof(modcache)); ctx.is_test = false; ctx.store = &ts; ctx.modcache = modcache; ctx.unit = scope_push(&ctx.scope, SCOPE_UNIT); test(&ctx, "(int | void)", "if (true) 0: int else void: void"); test(&ctx, "(nullable *int | void)", "if (true) null " "else if (true) null: nullable *int " "else if (true) null: *int"); test(&ctx, "(nullable *int | void)", "match (0u8: (u8 | u16 | u32 | u64)) { " "case u8 => " " yield null: *int; " "case u16 => " " yield null: nullable *int; " "case u32 => " " yield null; " "case u64 => " " yield;" "}"); test(&ctx, "(nullable *int | void)", "switch (0) { " "case 42 => " " yield null: *int;" "case 69 => " " yield null: nullable *int;" "case 1337 => " " yield null;" "case => " " yield;" "};"); // if, match, and switch all use the same code for reduction, so we // don't need to rigorously test all three test(&ctx, "nullable *int", "if (true) null: *int " "else null"); test(&ctx, "nullable *int", "if (true) null: *int " "else null: nullable *int"); test(&ctx, "(*int | const nullable *int)", "if (true) null: *int " "else null: const nullable *int"); test(&ctx, "const rune", "if (true) 'a' " "else 'a': const rune"); test(&ctx, "const rune", "if (true) 'a': const rune " "else 'a'"); test(&ctx, "(*int | const nullable *int)", "if (true) null: *int " "else if (true) null: const nullable *int " "else null: nullable *int"); test(&ctx, "(int | void)", "{ " "if (true) yield; " "yield 0; " "}"); test(&ctx, "", "if (true) null " "else if (true) null: *int " "else null: *opaque"); test(&ctx, "", "if (true) null " "else void"); test(&ctx, "", "if (true) null: *int " "else if (true) null"); // However, literals behave differently in if vs switch/match test(&ctx, "int", "if (true) 0 else if (true) 1 else 2"); test(&ctx, "(int | i64)", "if (true) 0 else 9223372036854775807"); test(&ctx, "(int | size)", "if (true) 0 else 0z"); test(&ctx, "(int | void)", "if (true) 0 else void"); test(&ctx, "int", "switch (0) { " "case 0 => " " yield 0; " "case 1 => " " yield 1; " "case => " " yield 2; " "};"); test(&ctx, "(int | i64)", "switch (0) { " "case 0 => " " yield 0; " "case => " " yield 9223372036854775807; " "};"); test(&ctx, "(int | size)", "switch (0) { " "case 0 => " " yield 0; " "case => " " yield 0z; " "};"); test(&ctx, "(int | void)", "switch (0) { " "case 0 => " " yield 0; " "case => " " yield; " "};"); test(&ctx, "(int | size | u32)", "switch (0) { " "case 0 => " " yield 0; " "case 1 => " " yield 1z; " "case => " " yield 2u32; " "};"); test(&ctx, "(int | i64)", "switch (0) { " "case 0 => " " yield 0; " "case 1 => " " yield 1i; " "case => " " yield 9223372036854775807; " "};"); test(&ctx, "(int | void)", "{ " " for (false) yield 42; " "};"); } harec-0.24.2/tests/31-postfix.ha000066400000000000000000000033531464473277600163020ustar00rootroot00000000000000type coords = struct { x: size, y: size }; type coords3 = struct { _2: coords, z: size }; fn foo() size = 2; fn equal(x: int, y: int) bool = x == y; fn aggregate(c: coords) coords = c; fn not(x: bool) bool = x == false; fn nested() void = { let c = coords3 { _2 = coords { x = 10, y = 20, }, z = 30, }; assert(c._2.x == 10); assert(c._2.y == 20); assert(c.z == 30); let x = coords { x = 10, y = 20 }; let a = [x, x, x, x]; assert(a[0].x == 10); assert(a[0].y == 20); assert(a[1].x == 10); assert(a[1].y == 20); assert(a[2].x == 10); assert(a[2].y == 20); assert(a[3].x == 10); assert(a[3].y == 20); }; fn nonaccess() void = { let c = coords { x = 10, y = 20 }; assert(aggregate(coords { x = 10, y = 20 }).x == 10); assert(aggregate(c).y == 20); assert(coords { x = 10, y = 20 }.x == 10); assert(coords { x = 10, y = 20 }.y == 20); assert([1, 2, 3, 4][2] == 3); }; fn deref() void = { let a = coords { x = 10, y = 20 }; let b = &a; let c = &b; assert(a.x == 10); assert(b.x == 10); assert(c.x == 10); let x = [1, 3, 3, 7]; let y = &x; let z = &y; assert(x[2] == 3); assert(y[2] == 3); assert(z[2] == 3); let q = coords { x = 2, y = 2 }; let o = &q; assert(x[q.x] == 3); assert(x[o.x] == 3); let f = ¬ let g = &f; assert(not(true) == false); assert(f(true) == false); assert(g(true) == false); }; fn calls() void = { // Indirect let x: size = foo(); assert(x == 2); // Direct let x = [1, 2, 3]; assert(x[foo()] == 3); // Direct & indirect params let x = 1234; assert(equal(x, 1234)); // Aggregate params and return let x = coords { x = 1234, y = 4321 }; let x = aggregate(x); assert(x.x == 1234 && x.y == 4321); }; export fn main() void = { nested(); nonaccess(); deref(); calls(); }; harec-0.24.2/tests/32-copy.ha000066400000000000000000000021601464473277600155540ustar00rootroot00000000000000type coords = struct { x: i8, y: int, z: size }; type anyint = struct { _8: i8, _16: i16, _32: i32, _64: i64 }; export fn main() void = { // Simple case let x = 10; let y = x; assert(y == 10); // With indirect target let a = [1, 2, 3, 4]; let x = 2z; assert(a[x] == 3); // Aggregate types: // arrays let x = [1, 2, 3, 4]; let y = x; assert(&x != &y); assert(x[0] == y[0]); assert(x[1] == y[1]); assert(x[2] == y[2]); assert(x[3] == y[3]); // structs let a = coords { x = 10, y = 20, z = 30 }; let b = a; assert(&a != &b); assert(a.x == b.x); assert(a.y == b.y); assert(a.z == b.z); // unions let a = anyint { _16 = 10, ... }; let b = a; assert(&a != &b); assert(a._16 == b._16); // tuples let a = (1, 2z, 3u8); let b = a; assert(a.0 == b.0); assert(a.1 == b.1); assert(a.2 == b.2); let x = "hello world"; let y = x; let px = &x: *struct { data: *[*]u8, length: size, capacity: size, }; let py = &y: *struct { data: *[*]u8, length: size, capacity: size, }; assert(px.length == py.length); assert(px.capacity == py.capacity); assert(px.data == py.data); // TODO: Slices }; harec-0.24.2/tests/33-yield.ha000066400000000000000000000014521464473277600157140ustar00rootroot00000000000000use rt::{compile, status}; fn basics() void = { let x = { yield 10; }; let y = :outer { if (true) { yield :outer, 20; }; abort(); }; assert(x == 10); assert(y == 20); }; fn _never() void = { :outer { let x: int = if (true) { yield :outer; } else 1; abort(); }; compile(status::CHECK, "fn test() void = { let x: int = if (true) { yield; } else 1; };" )!; }; fn cast_lowering() void = { let x: (int | void) = { yield 10; }; assert(x as int == 10); let x = { for (false) yield 10; }; assert(x is void); }; fn shadowing() void = { let i = 0; :foo { :foo { for :foo (true) { break :foo; }; i += 1; yield :foo; }; i += 1; yield :foo; }; assert(i == 2); }; export fn main() void = { basics(); _never(); cast_lowering(); shadowing(); }; harec-0.24.2/tests/34-declarations.ha000066400000000000000000000413231464473277600172600ustar00rootroot00000000000000use rt::{compile, status, toutf8}; use testmod; def ARR: [3]u8 = [1, 2, 3]; // interdependent constants def A1: int = -1; def A2: int = A1 + 2; def A3: [A2 + 2]int = [A1, A2, 0]; // reverse order def B3: [B2 + 2]int = [B1, B2, 0]; def B2: int = B1 + 2; def B1: int = -1; // flexible types def I = 12; def F = 12.0; def R = 'x'; // zero-size constants def V = void; def ZA: [0]int = []; def ZS = struct { v: void = void }; def ZT = (void, void); fn constants() void = { assert(ARR[0] == 1 && ARR[1] == 2 && ARR[2] == 3); assert(A1 == -1 && B1 == -1); assert(A2 == 1 && B2 == 1); assert(len(B3) == 3); assert(A3[0] == -1 && B3[0] == -1); assert(A3[1] == 1 && B3[1] == 1); assert(A3[2] == 0 && B3[2] == 0); let x = I; assert(x == 12); let x: *int = &x; let x: u8 = I; assert(x == 12); let x: i64 = I; assert(x == 12); let x = F; assert(x == 12.0); let x: *f64 = &x; let x: f32 = F; assert(x == 12.0); let x = R; assert(x == 'x'); let x: *rune = &x; let x: u32 = R; assert(x == 'x'); V; ZS; ZS.v; ZT; ZT.0; ZT.1; ZA; assert(len(ZA) == 0); }; def C = 42; fn local_constants() void = { def main = 42; // ensure hosted main check isn't used on locals static assert(main == 42); static assert(C == 42); def C = 0, C = 1337; static assert(C == 1337); { def C = 69; static assert(C == 69); }; static assert(C == 1337); static let x = 0; def C = &x; assert(C == &x && *C == 0); let x = { def LEN = 4; yield [0...]: [LEN]int; }; def LEN = 8; assert(len(x) == 4); let x: [LEN]int = { def LEN = 4; yield [0...]; }; assert(len(x) == LEN && LEN == 8); compile(status::CHECK, `fn test() void = { { def X = 0; }; def C = X; };`)!; compile(status::CHECK, `fn test() void = { { def X = 0; }; X; };`)!; compile(status::CHECK, `fn test() void = { let x = 0; def X = x; };`)!; compile(status::CHECK, `fn test() void = { let x = 0; def X = &x; };`)!; compile(status::CHECK, `fn test() void = { static let x = 0; def X = x; };`)!; compile(status::SUCCESS, ` def X = 42; fn test() void = { static assert(X == 1337); def X = 42; static assert(X == 42); }; `, "-DX=1337")!; compile(status::PARSE, `fn test() void = { static def X = 0; };`)!; }; // elementary self-referential types type self_slice = []self_slice; type self_ptr = *self_ptr; type self_slice_ptr = *[]self_slice_ptr; type self_ptr_slice = []*self_ptr_slice; // type referencing a constant type arr1 = [sizearr1]str; def sizearr1: size = 5z; // reverse order def sizearr2: size = 5z; type arr2 = [sizearr2]str; // self-referential struct type struct1 = struct { a: []struct1 }; // self-referential struct with multiple indirections type struct2 = struct { a: []**[]**nullable *struct2 }; // self-referential struct with self-reference having a nonzero offset type struct3 = struct { a: int, data: *struct3 }; // struct with multiple self-refences type struct4 = struct { ptr: *struct4, slice: []struct4, ptr2: *[]struct4 }; // self-referential indirect struct type pstruct1 = []struct { a: pstruct1 }; // self-referential indirect struct with multiple indirections type pstruct2 = []***[]struct { a: pstruct2 }; // self-referential indirect struct with self-reference having a nonzero offset type pstruct3 = *struct { a: int, data: pstruct3 }; // indirect struct with multiple self-refences type pstruct4 = *struct { a: pstruct4, b: [5]pstruct4, c: pstruct4 }; // self-referential tagged union type tagged1 = (*tagged1 | void); // self-referential tagged union with multiple indirections type tagged2 = (***[][]nullable *tagged2 | int); // tagged union with multiple self-references type tagged3 = (*tagged3 | **tagged3 | []tagged3); // tagged union with duplicate self-referential members type tagged4 = (void | *tagged4 | int | *tagged4 | *tagged4 | str); // self-referential indirect tagged union type ptagged1 = *(ptagged1 | void); // self-referential indirect tagged union with multiple indirections type ptagged2 = []*nullable *[]*(ptagged2 | int); // indirect tagged union with multiple self-references type ptagged3 = *([2]ptagged3 | ptagged3 | (ptagged3, ptagged3)); // indirect tagged union with duplicate self-referential members type ptagged4 = [](void | ptagged4 | int | ptagged4 | ptagged4 | str); // self-referential tuple type tuple1 = (*tuple1, u16); // self-referential tuple with multiple indirections type tuple2 = (***[][]nullable *tuple2, str); // tuple with multiple self-references type tuple3 = (*tuple3, *tuple3, []tuple3); // self-referential indirect tuple type ptuple1 = *(ptuple1, u16); // self-referential indirect tuple with multiple indirections type ptuple2 = ***[][]nullable *(ptuple2, str); // tuple with multiple self-references type ptuple3 = [](ptuple3, ptuple3, [3]ptuple3); // elementary mutually recursive types type mut_A1 = *mut_A2, mut_A2 = *mut_A1; type mut_A3 = []mut_A4, mut_A4 = []mut_A3; type mut_A5 = *mut_A6, mut_A6 = []mut_A5; type mut_A7 = []mut_A8, mut_A8 = *mut_A7; type mut_A9 = mut_A10, mut_A10 = *mut_A9; type mut_B10 = *mut_B9, mut_B9 = mut_B10; // reverse type mut_A11 = mut_A12, mut_A12 = []mut_A11; type mut_B12 = []mut_B11, mut_B11 = mut_B12; // reverse // mutually recursive structs type mut_struct_A1 = struct { data: *mut_struct_A2 }, mut_struct_A2 = struct { data: mut_struct_A1 }; type mut_struct_B2 = struct { data: mut_struct_B1 }, // reverse mut_struct_B1 = struct { data: *mut_struct_B2 }; // mutually recursive structs with padding type mut_struct_A3 = struct { padding: u16, data: *mut_struct_A4 }, mut_struct_A4 = struct { padding: u16, data: mut_struct_A3 }; type mut_struct_B4 = struct { padding: u16, data: mut_struct_B3 }, // reverse mut_struct_B3 = struct { padding: u16, data: *mut_struct_B4 }; // mutually recursive indirect structs type mut_pstruct_A1 = *struct { data: mut_pstruct_A2 }, mut_pstruct_A2 = struct { data: mut_pstruct_A1 }; type mut_pstruct_B2 = struct { data: mut_pstruct_B1 }, // reverse mut_pstruct_B1 = *struct { data: mut_pstruct_B2 }; // mutually recursive tagged unions type mut_tagged_A1 = (*mut_tagged_A2 | u8), mut_tagged_A2 = (mut_tagged_A1 | u8); type mut_tagged_B2 = (mut_tagged_B1 | u8), mut_tagged_B1 = (*mut_tagged_B2 | u8); // reverse // mutually recursive tagged unions with repeated members type mut_tagged_A3 = (*mut_tagged_A4 | u8 | *mut_tagged_A4), mut_tagged_A4 = (mut_tagged_A3 | u8 | mut_tagged_A3 | mut_tagged_A3); type mut_tagged_B4 = (mut_tagged_B3 | u8 | mut_tagged_B3 | mut_tagged_B3), // reverse mut_tagged_B3 = (*mut_tagged_B4 | u8 | *mut_tagged_A4); // mutually recursive indirect tagged unions type mut_ptagged_A1 = *(mut_ptagged_A2 | u8), mut_ptagged_A2 = (mut_ptagged_A1 | u8); type mut_ptagged_B2 = (mut_ptagged_B1 | u8), mut_ptagged_B1 = *(mut_ptagged_B2 | u8); // reverse // mutually recursive tuples type mut_tuple_A1 = (*mut_tuple_A2, u8), mut_tuple_A2 = (mut_tuple_A1, u8); type mut_tuple_B2 = (mut_tuple_B1, u8), mut_tuple_B1 = (*mut_tuple_B2, u8); // reverse // mutually recursive indirect tuples type mut_ptuple_A1 = *(mut_ptuple_A2, u8), mut_ptuple_A2 = (mut_ptuple_A1, u8); type mut_ptuple_B2 = (mut_ptuple_B1, u8), mut_ptuple_B1 = *(mut_ptuple_B2, u8); // reverse // type with a type dimension dependency type arri8_A = [size(arrintptr_A)]u8, arrintptr_A = [8]*int; type arrintptr_B = [8]*int, arri8_B = [size(arrintptr_B)]u8; // reverse type arri8_al_A = [align(arrintptr_al_A)]u8, arrintptr_al_A = [8]*int; type arrintptr_al_B = [8]*int, arri8_al_B = [align(arrintptr_al_B)]u8; // reverse type arr_circ = [size(*arr_circ)]int; // mutually recursive types with a dimension dependency type arru8_A = [size(arru8ptr_A)]u8, arru8ptr_A = [8]*arru8_A; type arru8ptr_B = [8]*arru8_B, arru8_B = [size(arru8ptr_B)]u8; // reverse type arru8_al_A = [align(arru8ptr_al_A)]u8, arru8ptr_al_A = [8]*arru8_al_A; type arru8ptr_al_B = [8]*arru8_al_B, arru8_al_B = [align(arru8ptr_al_B)]u8; // reverse // zero-size let v = void; let za: [0]int = []; let zs = struct { v: void = void }; let zt = (void, void); // unwrapped aliases to tagged unions type unwrap_A1 = ([32]u8 | void | str), unwrap_A2 = (i64 | ...unwrap_A1), unwrap_alias_A1 = unwrap_A2, unwrap_alias_A2 = unwrap_alias_A1, unwrap_alias_A3 = ...unwrap_alias_A2; type unwrap_alias_B3 = ...unwrap_alias_B2, // reverse unwrap_alias_B2 = unwrap_alias_B1, unwrap_alias_B1 = unwrap_B2, unwrap_B2 = (i64 | ...unwrap_B1), unwrap_B1 = ([32]u8 | void | str); fn sz() void = { // size static assert(size(mut_struct_A3) == 2 * size(*opaque)); static assert(size(mut_struct_B3) == 2 * size(*opaque)); static assert(size(mut_tagged_A1) == 2 * size(*opaque)); static assert(size(mut_tagged_B1) == 2 * size(*opaque)); static assert(size(mut_tagged_A3) == 2 * size(*opaque)); static assert(size(mut_tagged_B3) == 2 * size(*opaque)); static assert(size(mut_tagged_A4) == 3 * size(*opaque)); static assert(size(mut_tagged_B4) == 3 * size(*opaque)); static assert(size(mut_tuple_A1) == 2 * size(*opaque)); static assert(size(mut_tuple_B1) == 2 * size(*opaque)); static assert(size(arru8_A) == 8 * size(*opaque)); static assert(size(arru8_B) == 8 * size(*opaque)); static assert(size(arru8ptr_A) == 8 * size(*opaque)); static assert(size(arru8ptr_B) == 8 * size(*opaque)); static assert(size(unwrap_A1) == size(unwrap_alias_A3)); static assert(size(unwrap_B1) == size(unwrap_alias_B3)); //align static assert(align(mut_struct_A3) == align(*opaque)); static assert(align(mut_struct_B3) == align(*opaque)); static assert(align(mut_tagged_A1) == align(*opaque)); static assert(align(mut_tagged_B1) == align(*opaque)); static assert(align(mut_tagged_A3) == align(*opaque)); static assert(align(mut_tagged_B3) == align(*opaque)); static assert(align(mut_tagged_A4) == align(*opaque)); static assert(align(mut_tagged_B4) == align(*opaque)); static assert(align(mut_tuple_A1) == align(*opaque)); static assert(align(mut_tuple_B1) == align(*opaque)); static assert(align(arru8_A) == align(u8)); static assert(align(arru8_B) == align(u8)); static assert(align(arru8ptr_A) == align(*opaque)); static assert(align(arru8ptr_B) == align(*opaque)); static assert(align(unwrap_A1) == align(unwrap_alias_A3)); static assert(align(unwrap_B1) == align(unwrap_alias_B3)); }; fn hosted_main() void = { let pass = [ "export fn main() void;", "export fn main() void = void;", `export @symbol("main") fn notmain() void;`, "fn main() void;", `export @symbol("notmain") fn main() int;`, `export let @symbol("notmain") notmain: int;`, "export fn not::main() int;", ]; for (let i = 0z; i < len(pass); i += 1) { compile(status::SUCCESS, pass[i])!; }; let failures = [ "fn main() void = void;", "export fn main(x: int) void;", "export fn main() int;", "export type main = int;", "export def main = 0;", `export @symbol("main") fn f() int;`, "export let main: int;", `export let @symbol("main") notmain: int;`, "export type v = void; export fn main() v;", ]; for (let i = 0z; i < len(failures); i += 1) { compile(status::CHECK, failures[i])!; compile(status::SUCCESS, failures[i], "-m\0", "\0")!; }; compile(status::CHECK, `export @symbol(".main") fn main() int;`, "-m\0", ".main\0")!; compile(status::SUCCESS, `export @symbol("main") fn main() int;`, "-m\0", ".main\0")!; compile(status::CHECK, `export fn main() int;`, "-m\0", ".main\0")!; }; fn reject() void = { let failures = [ "type a = b; type b = a;", "type a = [20]a;", "type a = unknown;", "def x: int = 6; type a = x;", "type a = int; type a = str;", "def a: int = b; def b: int = a;", "def x: size = size(t); type t = [x]int;", "def a: int = 12; type t = (int |(...a | a));", "type a = (...unknown | int);", "def x = size(*foo);", "def x = size(*never);", "def x = size(*[*][*]int);", // usage of non-type aliases "let a: int = 4; type x = a;", "let a: int = 4; type x = *a;", "let a: int = 4; type x = []a;", "let a: int = 4; type x = [3]a;", "let a: int = 4; type x = (str, a);", "let a: int = 4; type x = (str | a);", "let a: int = 4; type x = struct { y: str, z: a};", "let a: int = 4; type x = union { y: str, z: a};", "let a: int = 4; fn x(y: str, z: a) void = { void; };", // attributes on prototypes "@init fn f() void;", "@fini fn f() void;", "@test fn f() void;", // @symbol alongside other attributes `@symbol("foo") @init fn foo() void = void;`, `@symbol("foo") @fini fn foo() void = void;`, `@symbol("foo") @test fn foo() void = void;`, // initializing object with undefined size "let a: [*]int = [1, 2, 3];", // type alias of never "export type t = never;", ]; for (let i = 0z; i < len(failures); i += 1) { compile(status::CHECK, failures[i])!; }; let failures = [ // binding not directly in compound expression "export fn main() void = let a = 4;", "export fn main() void = { if (true) let a = 4; };", // invalid symbol `@symbol("") fn f() void;`, `@symbol("#!@%") fn f() void;`, `@symbol("") let x: int;`, `@symbol("#!@%") let x: int;`, ]; for (let i = 0z; i < len(failures); i += 1) { compile(status::PARSE, failures[i])!; }; // usage of unexported type in exported declaration let unexported_type = [ ("a", "a { ... }"), ("[4]a", "[a { ... }, a { ... }, a { ... }, a { ... }]"), ("[]a", "[a { ... }]: []a"), ("nullable *fn(x: a) void", "null: nullable *fn(x: a) void"), ("nullable *fn() a", "null: nullable *fn() a"), ("nullable *a", "null: nullable *a"), ("struct { x: a }", "struct { x: a = a { ... } }"), ("nullable *struct { a }", "null: nullable *struct { a }"), ("nullable *union { x: a }", "null: nullable *union { x: a }"), ("(int | a)", "a { ... }"), ("(int, a)", "(0, a { ... })"), ]; let exported_decl = [ ("export type b = ", ";", ""), ("export fn b(x: ", ") void = void;", ""), ("export fn b() ", " = ", ";"), ("export let b: ", ";", ""), ("export let b = ", "", ";"), ("export def b: ", " = ", ";"), ("export def b = ", "", ";"), ]; let buf: [256]u8 = [0...]; for (let i = 0z; i < len(unexported_type); i += 1) { for (let j = 0z; j < len(exported_decl); j += 1) { const t = unexported_type[i]; const d = exported_decl[j]; let buf = buf[..0]; static append(buf, toutf8("type a = struct { x: int };\n")...); static append(buf, toutf8(d.0)...); if (d.1 != "") { static append(buf, toutf8(t.0)...); static append(buf, toutf8(d.1)...); }; if (d.2 != "") { static append(buf, toutf8(t.1)...); static append(buf, toutf8(d.2)...); }; compile(status::CHECK, *(&buf: *str))!; static insert(buf[0], toutf8("export ")...); compile(status::SUCCESS, *(&buf: *str))!; }; }; compile(status::SUCCESS, "export type e = enum {FOO}; export fn f(x: e) e = x;")!; compile(status::CHECK, "type e = enum {FOO}; export fn f(x: e) e = x;")!; }; // Types t_0 to t_9 form a complete directed graph on 10 vertices. // The edge from t_$i to t_$j is indirect if $i > $j, otherwise it is direct. // This ensures the generated graph is the maximum possible valid dependency // graph of 10 hare type aliases. type t_0 = ( t_1, t_2, t_3, t_4, t_5, t_6, t_7, t_8, t_9, ); type t_1 = ( *t_0, t_2, t_3, t_4, t_5, t_6, t_7, t_8, t_9, ); type t_2 = ( *t_0, *t_1, t_3, t_4, t_5, t_6, t_7, t_8, t_9, ); type t_3 = ( *t_0, *t_1, *t_2, t_4, t_5, t_6, t_7, t_8, t_9, ); type t_4 = ( *t_0, *t_1, *t_2, *t_3, t_5, t_6, t_7, t_8, t_9, ); type t_5 = ( *t_0, *t_1, *t_2, *t_3, *t_4, t_6, t_7, t_8, t_9, ); type t_6 = ( *t_0, *t_1, *t_2, *t_3, *t_4, *t_5, t_7, t_8, t_9, ); type t_7 = ( *t_0, *t_1, *t_2, *t_3, *t_4, *t_5, *t_6, t_8, t_9, ); type t_8 = ( *t_0, *t_1, *t_2, *t_3, *t_4, *t_5, *t_6, *t_7, t_9, ); type t_9 = ( *t_0, *t_1, *t_2, *t_3, *t_4, *t_5, *t_6, *t_7, *t_8, ); fn complete_graph() void = { static assert(size(t_9) == 9 * size(*opaque)); static assert(size(t_8) == 17 * size(*opaque)); static assert(size(t_7) == 33 * size(*opaque)); static assert(size(t_6) == 65 * size(*opaque)); static assert(size(t_5) == 129 * size(*opaque)); static assert(size(t_4) == 257 * size(*opaque)); static assert(size(t_3) == 513 * size(*opaque)); static assert(size(t_2) == 1025 * size(*opaque)); static assert(size(t_1) == 2049 * size(*opaque)); static assert(size(t_0) == 4097 * size(*opaque)); }; export let @symbol("s_x") s_d: int = 0; export let @symbol("s_y") s_c: int; fn imported() void = { // Decl. with symbol accessible by identifier testmod::s_a; testmod::s_b; // Decl. with symbol not accessible by symbol compile(status::CHECK, ` use testmod; fn test() void = { testmod::s_x; };`)!; compile(status::CHECK, ` use testmod; fn test() void = { s_x; };`)!; // Decl. with same symbol are linked together assert(testmod::s_b == 1 && s_c == 1); s_c = 2; assert(testmod::s_b == 2 && s_c == 2); }; export fn main() void = { constants(); local_constants(); sz(); reject(); imported(); hosted_main(); complete_graph(); }; harec-0.24.2/tests/35-floats.ha000066400000000000000000000040741464473277600161030ustar00rootroot00000000000000fn measurements() void = { assert(size(f32) == align(f32)); assert(size(f32) == align(f32)); }; fn signed_casts() void = { let a = 20f64; let f = a: f32; assert(f == 20f32); let i = a: i8; assert(i == 20); let i = a: i16; assert(i == 20); let i = a: i16; assert(i == 20); let i = a: i32; assert(i == 20); let i = a: i64; assert(i == 20); let a = 20f32; let f = a: f64; assert(f == 20f64); let i = a: i8; assert(i == 20); let i = a: i16; assert(i == 20); let i = a: i16; assert(i == 20); let i = a: i32; assert(i == 20); let i = a: i64; assert(i == 20); let a = 20i8; let f = a: f32; assert(f == 20f32); let f = a: f64; assert(f == 20f64); let a = 20i16; let f = a: f32; assert(f == 20f32); let f = a: f64; assert(f == 20f64); let a = 20i32; let f = a: f32; assert(f == 20f32); let f = a: f64; assert(f == 20f64); let a = 20i64; let f = a: f32; assert(f == 20f32); let f = a: f64; assert(f == 20f64); let f = 20: f32; assert(f == 20f32); let f = 20: f64; assert(f == 20f64); let i = 13.37: int; assert(i == 13); }; fn unsigned_casts() void = { let a = 20f64; let f = a: f32; assert(f == 20f32); let i = a: u8; assert(i == 20); let i = a: u16; assert(i == 20); let i = a: u16; assert(i == 20); let i = a: u32; assert(i == 20); let i = a: u64; assert(i == 20); let a = 20f32; let f = a: f64; assert(f == 20f64); let i = a: u8; assert(i == 20); let i = a: u16; assert(i == 20); let i = a: u16; assert(i == 20); let i = a: u32; assert(i == 20); let i = a: u64; assert(i == 20); let a = 20u8; let f = a: f32; assert(f == 20f32); let f = a: f64; assert(f == 20f64); let a = 20u16; let f = a: f32; assert(f == 20f32); let f = a: f64; assert(f == 20f64); let a = 20u32; let f = a: f32; assert(f == 20f32); let f = a: f64; assert(f == 20f64); let a = 20u64; let f = a: f32; assert(f == 20f32); let f = a: f64; assert(f == 20f64); let i = 13.37: uint; assert(i == 13); }; export fn main() void = { // TODO: test parsing, compile-time evaluation measurements(); signed_casts(); unsigned_casts(); }; harec-0.24.2/tests/36-defines.ha000066400000000000000000000041701464473277600162260ustar00rootroot00000000000000use rt::{compile, status}; fn import() void = { compile(status::CHECK, " use a = rt; def b = a::TESTDEFINE; ", "-DTESTDEFINE=1")!; }; fn mandatory() void = { compile(status::SUCCESS, " def TESTDEFINE = TESTDEFINE; ", "-DTESTDEFINE=1")!; compile(status::CHECK, " def TESTDEFINE = TESTDEFINE; ")!; }; fn optional() void = { compile(status::SUCCESS, " def TESTDEFINE = 1; static assert(TESTDEFINE == 2); ", "-DTESTDEFINE=2")!; compile(status::SUCCESS, " def TESTDEFINE = 1; static assert(TESTDEFINE == 1); ")!; }; fn compatibility() void = { compile(status::SUCCESS, " def TESTDEFINE: int = 0; ", "-DTESTDEFINE:int=1")!; compile(status::CHECK, " def TESTDEFINE: int = 0; ", "-DTESTDEFINE:uint=1")!; compile(status::CHECK, " const TEST = 1; ", "-DTEST=0")!; compile(status::SUCCESS, " def TESTDEFINE = 0; static assert(TESTDEFINE == 1); let x = TESTDEFINE; let y: *u8 = &x; ", "-DTESTDEFINE:u8=1")!; compile(status::SUCCESS, " def TESTDEFINE: u8 = 0; static assert(TESTDEFINE == 1); let x = TESTDEFINE; let y: *u8 = &x; ", "-DTESTDEFINE=1")!; compile(status::SUCCESS, " def TESTDEFINE = 0u32; static assert(TESTDEFINE == 'a'); let x = TESTDEFINE; let y: *u32 = &x; ", "-DTESTDEFINE='a'")!; compile(status::SUCCESS, " def TESTDEFINE = 'a'; static assert(TESTDEFINE == 0); let x = TESTDEFINE; let y: *u32 = &x; ", "-DTESTDEFINE=0u32")!; compile(status::CHECK, " def TESTDEFINE = 0; ", "-DTESTDEFINE='a'")!; compile(status::CHECK, " def TESTDEFINE = 0.0; ", "-DTESTDEFINE=0")!; compile(status::SUCCESS, " def TESTDEFINE = 0.0; static assert(TESTDEFINE == 1.0); let x = TESTDEFINE; let y: *f32 = &x; ", "-DTESTDEFINE=1f32")!; compile(status::SUCCESS, " def TESTDEFINE = 0f32; static assert(TESTDEFINE == 1.0); let x = TESTDEFINE; let y: *f32 = &x; ", "-DTESTDEFINE=1.0")!; compile(status::CHECK, " def TESTDEFINE = 255; let x: u8 = TESTDEFINE; ", "-DTESTDEFINE=256")!; compile(status::CHECK, " def TESTDEFINE = 256; let x: u8 = TESTDEFINE; ", "-DTESTDEFINE=255")!; }; export fn main() void = { import(); mandatory(); optional(); compatibility(); }; harec-0.24.2/tests/run000077500000000000000000000010351464473277600146000ustar00rootroot00000000000000#!/bin/sh printf 'Running harec test suite at %s\n\n' "$(date)" start=$(date +"%s") ntests=0 npass=0 nfail=0 for f in ./tests/* do if [ -x "$f" ] && [ "$f" != "./tests/run" ] then ntests=$((ntests+1)) name="$(basename "$f")" printf '%-20s ...' "$name" if $f then npass=$((npass+1)) printf 'PASS\n' else nfail=$((nfail+1)) printf 'FAIL\n' fi fi done finish=$(date +"%s") printf '\n%d tests:\t%d passed\t%d failed\tin %d seconds\n' \ $ntests $npass $nfail $((finish-start)) if [ $nfail -ne 0 ] then exit 1 fi