File harec-0.25.2+git.1750492315.966012b.obscpio of Package harec
07070100014E71000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002C00000000harec-0.25.2+git.1750492315.966012b/.builds07070100014E75000081A40000000000000000000000016856649B000002AC000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/.builds/alpine.ymlimage: 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
07070100014E74000081A40000000000000000000000016856649B00000137000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/.builds/freebsd.ymlimage: 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
07070100014E73000081A40000000000000000000000016856649B0000012C000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/.builds/netbsd.ymlimage: 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
07070100014E72000081A40000000000000000000000016856649B00000131000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/.builds/openbsd.ymlimage: 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
07070100014E70000081A40000000000000000000000016856649B0000004A000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/.gitignore.bin/
.cache/
src/*.o
tests/*
!tests/*.ha
!tests/*.c
!tests/run
config.mk
07070100014E6F000081A40000000000000000000000016856649B0000001D000000000000002F00000000000000000000002D00000000harec-0.25.2+git.1750492315.966012b/.mailmapEmber Sawady <ecs@d2evs.net>
07070100014E6E000081A40000000000000000000000016856649B0000894D000000000000002F00000000000000000000002C00000000harec-0.25.2+git.1750492315.966012b/COPYING GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 <https://www.gnu.org/licenses/>.
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:
<program> Copyright (C) <year> <name of author>
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
<https://www.gnu.org/licenses/>.
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
<https://www.gnu.org/licenses/why-not-lgpl.html>.
07070100014E6D000081A40000000000000000000000016856649B00000809000000000000002F00000000000000000000003000000000harec-0.25.2+git.1750492315.966012b/MAINTAINERSGuidelines 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 <address@domain>
R: Designated *Reviewer*: FullName <address@domain>
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.
THE REST
M: Drew DeVault <sir@cmpwn.com>
M: Ember Sawady <ecs@d2evs.net>
M: Sebastian <sebastian@sebsite.pw>
M: Bor Grošelj Simić <bgs@turminal.net>
L: ~sircmpwn/hare-dev@lists.sr.ht
T: git https://git.sr.ht/~sircmpwn/harec
C: irc://irc.libera.chat/#hare-dev
F: *
F: */
07070100014E6C000081A40000000000000000000000016856649B00000824000000000000002F00000000000000000000002D00000000harec-0.25.2+git.1750492315.966012b/Makefile.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: .ssa .td .c .o .s
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 $@.tmp $<
@mv $@.tmp $@
.ssa.td:
@cmp -s $@ $@.tmp 2>/dev/null || cp $@.tmp $@
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
07070100014E6B000081A40000000000000000000000016856649B000004D1000000000000002F00000000000000000000002E00000000harec-0.25.2+git.1750492315.966012b/README.md# harec
This is a [Hare](https://harelang.org) compiler written in C11 for
POSIX-compatible systems.
## Build status
<dl>
<dt>Linux (x86_64)</dt><dd><a href="https://builds.sr.ht/~sircmpwn/harec/commits/master/alpine.yml"><img src="https://builds.sr.ht/~sircmpwn/harec/commits/master/alpine.yml.svg" alt="Build status for Linux" /></a></dd>
<dt>FreeBSD (x86_64)</dt><dd><a href="https://builds.sr.ht/~sircmpwn/harec/commits/master/freebsd.yml"><img src="https://builds.sr.ht/~sircmpwn/harec/commits/master/freebsd.yml.svg" alt="Build status for FreeBSD" /></a></dd>
<dt>NetBSD (x86_64)</dt><dd><a href="https://builds.sr.ht/~sircmpwn/harec/commits/master/netbsd.yml"><img src="https://builds.sr.ht/~sircmpwn/harec/commits/master/netbsd.yml.svg" alt="Build status for NetBSD" /></a></dd>
</dl>
## 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).
07070100014E66000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002C00000000harec-0.25.2+git.1750492315.966012b/configs07070100014E6A000081A40000000000000000000000016856649B00000246000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/configs/freebsd.mk# 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)
07070100014E69000081A40000000000000000000000016856649B00000244000000000000002F00000000000000000000003500000000harec-0.25.2+git.1750492315.966012b/configs/linux.mk# 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)
07070100014E68000081A40000000000000000000000016856649B00000245000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/configs/netbsd.mk# 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)
07070100014E67000081A40000000000000000000000016856649B00000293000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/configs/openbsd.mk# 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)
07070100014E61000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002900000000harec-0.25.2+git.1750492315.966012b/docs07070100014E65000081A40000000000000000000000016856649B0000129D000000000000002F00000000000000000000004000000000harec-0.25.2+git.1750492315.966012b/docs/declaration_solver.txtFirst 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 its 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.
07070100014E64000081A40000000000000000000000016856649B0000036D000000000000002F00000000000000000000003100000000harec-0.25.2+git.1750492315.966012b/docs/env.txtharec 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.
07070100014E63000081A40000000000000000000000016856649B00000144000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/docs/exit_codes.txtWhen 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)
07070100014E62000081A40000000000000000000000016856649B00000BF2000000000000002F00000000000000000000003500000000harec-0.25.2+git.1750492315.966012b/docs/runtime.txtharec 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: Unreachable code
3: Slice allocation capacity smaller than initializer
4: Generic assertion failure without a message
5: 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 and return true. in the case that the re-allocation was not
successful, false should be returned and the slice should not be modified.
"unensure" is called when the space is no longer needed, and should shrink the
allocation as appropriate, if possible.
type slice = struct {
data: nullable *opaque,
length: size,
capacity: size,
};
fn ensure(s: *slice, membsz: size) bool;
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.
07070100014E4F000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002C00000000harec-0.25.2+git.1750492315.966012b/include07070100014E52000081A40000000000000000000000016856649B000022FE000000000000002F00000000000000000000003200000000harec-0.25.2+git.1750492315.966012b/include/ast.h#ifndef HARE_AST_H
#define HARE_AST_H
#include <stdbool.h>
#include <stdint.h>
#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;
struct ident *name;
struct ast_import_members *next;
};
struct ast_imports {
enum ast_import_mode mode;
struct ident *ident;
union {
const 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;
struct ident *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;
struct ident *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;
bool nullable;
};
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;
const 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 ident *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 ident *ident;
struct {
struct ast_expression *array;
struct ast_expression *index;
};
struct {
struct ast_expression *_struct;
const 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 {
struct ident *name;
struct ast_binding_unpack *next;
};
struct ast_expression_binding {
struct ident *name;
struct ast_binding_unpack *unpack;
struct ast_type *type;
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 {
const 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;
const 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 {
const char *label;
struct location label_loc;
struct ast_expression_list list;
};
struct ast_match_case {
struct ident *name; // May be null
struct ast_type *type;
struct ast_expression_list exprs;
struct ast_match_case *next;
};
struct ast_expression_match {
const 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 {
const char *label;
struct ast_expression *value;
struct ast_switch_case *cases;
};
struct ast_field_value {
const char *name;
struct ast_type *type;
struct ast_expression *initializer;
struct ast_field_value *next;
};
struct ast_expression_struct {
bool autofill;
struct ident *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_type *type;
};
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 {
const char *symbol;
bool threadlocal;
struct ident *ident;
struct ast_type *type;
struct ast_expression *init;
struct ast_global_decl *next;
};
struct ast_type_decl {
struct ident *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 {
const char *symbol;
struct ident *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 ident *ns;
struct ast_subunit subunits;
};
#endif
07070100014E60000081A40000000000000000000000016856649B00000EBB000000000000002F00000000000000000000003400000000harec-0.25.2+git.1750492315.966012b/include/check.h#ifndef HARE_CHECK_H
#define HARE_CHECK_H
#include <stdbool.h>
#include <stdnoreturn.h>
#include "ast.h"
#include "identifier.h"
#include "scope.h"
#include "types.h"
#include "type_store.h"
#include "util.h"
struct expression;
#define MODCACHE_BUCKETS 256
struct modcache {
struct ident *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 ident *ns;
struct scope *unit;
struct scope *scope;
struct scope *defines;
const char *mainsym;
struct ident *mainident;
bool is_test;
int id;
struct errors *errors;
struct errors **next;
struct declarations *decls;
struct ast_types *unresolved;
struct intern_table *itbl;
};
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 ident *ident;
const 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 ident *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_decl {
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;
};
};
struct ident *mkident(struct context *ctx, struct ident *ident,
const char *symbol);
void append_decl(struct context *ctx, struct declaration *decl);
void mkstrliteral(struct expression *expr, const char *fmt, ...) FORMAT(2, 3);
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 scope_object *obj);
void resolve_dimensions(struct context *ctx, struct scope_object *obj);
void resolve_type(struct context *ctx, struct scope_object *obj);
void wrap_resolver(struct context *ctx,
struct scope_object *obj, resolvefn resolver);
struct scope *check(type_store *ts,
bool is_test,
const char *mainsym,
struct ident *mainident,
const struct ast_global_decl *defines,
const struct ast_unit *aunit,
struct unit *unit,
struct intern_table *itbl);
struct scope *check_internal(type_store *ts,
struct modcache **cache,
bool is_test,
const char *mainsym,
struct ident *mainident,
const struct ast_global_decl *defines,
const struct ast_unit *aunit,
struct unit *unit,
struct intern_table *itbl,
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, ...) FORMAT(4, 5);
#endif
07070100014E5F000081A40000000000000000000000016856649B00000093000000000000002F00000000000000000000003300000000harec-0.25.2+git.1750492315.966012b/include/emit.h#ifndef HAREC_EMIT_H
#define HAREC_EMIT_H
#include <stdio.h>
struct qbe_program;
void emit(const struct qbe_program *program, FILE *out);
#endif
07070100014E5E000081A40000000000000000000000016856649B00000105000000000000002F00000000000000000000003300000000harec-0.25.2+git.1750492315.966012b/include/eval.h#ifndef HAREC_EVAL_H
#define HAREC_EVAL_H
#include <stdbool.h>
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
07070100014E5D000081A40000000000000000000000016856649B00001D28000000000000002F00000000000000000000003300000000harec-0.25.2+git.1750492315.966012b/include/expr.h#ifndef HAREC_EXPR_H
#define HAREC_EXPR_H
#include <stdint.h>
#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;
const struct type *allocation_result;
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_UNREACHABLE = 2,
ABORT_CAP_TOO_SMALL = 3,
ABORT_ANON_ASSERTION_FAILED = 4,
ABORT_PROPAGATE_ERROR_OCCURRED = 5,
};
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 {
const 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 {
const 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;
const 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
07070100014E5C000081A40000000000000000000000016856649B00000A38000000000000002F00000000000000000000003200000000harec-0.25.2+git.1750492315.966012b/include/gen.h#ifndef HAREC_GEN_H
#define HAREC_GEN_H
#include <stddef.h>
#include "identifier.h"
#include "qbe.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 {
const 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;
const struct ident *ns;
struct rt rt;
struct gen_value *sources;
int id;
struct qbe_func *current;
struct gen_binding *bindings;
struct gen_scope *scope;
struct intern_table *itbl;
};
struct unit;
void gen(const struct unit *unit, struct qbe_program *out,
struct intern_table *itbl);
// 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);
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
07070100014E5B000081A40000000000000000000000016856649B00000582000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/include/identifier.h#ifndef HARE_IDENTIFIER_H
#define HARE_IDENTIFIER_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// 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 ident {
const char *name;
struct ident *ns;
};
struct identifiers {
struct ident *ident;
struct identifiers *next;
};
struct bucket {
void **ids;
size_t sz;
size_t cap;
};
struct intern_table {
struct bucket *sbuckets;
struct bucket *ibuckets;
};
uint32_t ident_hash(uint32_t init, const struct ident *ident);
char *ident_unparse(const struct ident *ident);
int ident_unparse_static(const struct ident *ident, char *buf);
const char *ident_to_sym(struct intern_table *itbl, const struct ident *ident);
void intern_init(struct intern_table *itbl);
void intern_init(struct intern_table *itbl);
const char *intern_copy(struct intern_table *itbl, const char *s);
const char *intern_owned(struct intern_table *itbl, char *s);
struct ident *intern_ident(struct intern_table *itbl,
const char *name, struct ident *ns);
struct ident *intern_name(struct intern_table *itbl, const char *name);
#endif
07070100014E51000081A40000000000000000000000016856649B000009A9000000000000002F00000000000000000000003200000000harec-0.25.2+git.1750492315.966012b/include/lex.h#ifndef HAREC_LEX_H
#define HAREC_LEX_H
#include <stdint.h>
#include <stdio.h>
#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_NOMEM,
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_LITERAL,
// 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 {
const char *name;
uint32_t rune;
int64_t ival;
uint64_t uval;
double fval;
struct {
size_t len;
const 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;
bool in_annotation;
struct intern_table *itbl;
};
void lex_init(struct lexer *lexer, FILE *f, int fileid, struct intern_table *itbl);
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);
const char *token_str(const struct token *tok);
const char *lexical_token_str(enum lexical_token tok);
#endif
07070100014E5A000081A40000000000000000000000016856649B000000D6000000000000002F00000000000000000000003200000000harec-0.25.2+git.1750492315.966012b/include/mod.h#ifndef HARE_MOD_H
#define HARE_MOD_H
struct ast_global_decl;
struct context;
struct ident;
struct scope *module_resolve(struct context *ctx,
const struct ast_global_decl *defines, struct ident *ident);
#endif
07070100014E59000081A40000000000000000000000016856649B000001B0000000000000002F00000000000000000000003400000000harec-0.25.2+git.1750492315.966012b/include/parse.h#ifndef HAREC_PARSE_H
#define HAREC_PARSE_H
#include <stdbool.h>
#include "identifier.h"
struct ast_expression;
struct ast_subunit;
struct ast_type;
struct lexer;
void parse(struct lexer *lexer, struct ast_subunit *unit);
struct ident *parse_identifier(struct lexer *lexer,
const char *ns, bool *trailing);
struct ast_type *parse_type(struct lexer *lexer);
struct ast_expression *parse_expression(struct lexer *lexer);
#endif
07070100014E58000081A40000000000000000000000016856649B00000F7F000000000000002F00000000000000000000003200000000harec-0.25.2+git.1750492315.966012b/include/qbe.h#ifndef HAREC_QBE_H
#define HAREC_QBE_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "util.h"
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;
bool sgn; // for sub-word types
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_sbyte,
qbe_ubyte,
qbe_shalf,
qbe_uhalf,
qbe_word,
qbe_long,
qbe_single,
qbe_double,
qbe_void;
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 {
const 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 {
const 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 {
const 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, ...) FORMAT(2, 3);
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
07070100014E57000081A40000000000000000000000016856649B000008BD000000000000002F00000000000000000000003400000000harec-0.25.2+git.1750492315.966012b/include/scope.h#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 ident *name;
struct ident *ident;
enum scope_object_flags flags;
union {
const struct type *type;
struct expression *value; // For O_CONST
};
// Cannot be in union because type and idecl are needed at the same time
struct incomplete_decl *idecl;
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);
struct scope_object *scope_insert(struct scope *scope,
enum object_type otype, struct ident *ident, struct ident *name,
const struct type *type, struct expression *value);
struct scope_object *scope_lookup(struct scope *scope, struct ident *ident);
#endif
07070100014E56000081A40000000000000000000000016856649B0000073A000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/include/type_store.h#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;
struct dimensions {
size_t size;
size_t align;
};
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);
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, bool nullable);
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);
const struct type *type_store_lookup_alias(struct context *ctx,
struct ident *ident, struct ident *name,
const struct type *secondary, int flags, bool exported);
const struct type *type_store_lookup_tagged(struct context *ctx,
struct location loc, struct type_tagged_union *tags);
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
07070100014E55000081A40000000000000000000000016856649B000000D1000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/include/typedef.h#ifndef HARE_TYPEDEF_H
#define HARE_TYPEDEF_H
#include <stdio.h>
struct type;
struct unit;
void emit_type(const struct type *type, FILE *out);
void emit_typedefs(const struct unit *unit, FILE *out);
#endif
07070100014E50000081A40000000000000000000000016856649B0000153B000000000000002F00000000000000000000003400000000harec-0.25.2+git.1750492315.966012b/include/types.h#ifndef HARE_TYPES_H
#define HARE_TYPES_H
#include <stdbool.h>
#include <stdint.h>
#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_NOMEM,
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 ident *ident;
struct ident *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;
};
struct type_pointer {
const struct type *referent;
bool nullable;
};
struct struct_field {
const 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_ERROR = 1 << 0,
};
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;
};
};
const struct type *type_dereference(struct context *ctx, const struct type *type,
bool allow_nullable);
const struct type *type_dealias(struct context *ctx, const struct type *type);
bool type_is_done(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_done,
builtin_type_error,
builtin_type_f32,
builtin_type_f64,
builtin_type_i16,
builtin_type_i32,
builtin_type_i64,
builtin_type_i8,
builtin_type_int,
builtin_type_never,
builtin_type_nomem,
builtin_type_null,
builtin_type_opaque,
builtin_type_rune,
builtin_type_size,
builtin_type_u16,
builtin_type_u32,
builtin_type_u64,
builtin_type_u8,
builtin_type_uint,
builtin_type_uintptr,
builtin_type_void,
// etc
builtin_type_str,
builtin_type_valist;
#endif
07070100014E54000081A40000000000000000000000016856649B000001F5000000000000002F00000000000000000000003300000000harec-0.25.2+git.1750492315.966012b/include/utf8.h#ifndef HAREC_UTF8_H
#define HAREC_UTF8_H
#include <stdint.h>
#include <limits.h>
#include <stdio.h>
#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
07070100014E53000081A40000000000000000000000016856649B000006D9000000000000002F00000000000000000000003300000000harec-0.25.2+git.1750492315.966012b/include/util.h#ifndef HARE_UTIL_H
#define HARE_UTIL_H
#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#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;
// Sources unaffected by the -M option
extern const char **full_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);
#define FORMAT(FMT_PARAM, VA_PARAM)
#ifdef __has_attribute
#if __has_attribute(format)
#undef FORMAT
#define FORMAT(FMT_PARAM, VA_PARAM) __attribute__((format(printf, FMT_PARAM, VA_PARAM)))
#endif
#endif
int xfprintf(FILE *restrict f, const char *restrict fmt, ...) FORMAT(2, 3);
int xvfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) FORMAT(2, 0);
#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 append_buffer(char **buf, size_t *restrict ln, size_t *restrict cap,
const char *b, size_t sz);
void errline(struct location loc);
#endif
07070100014E49000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002E00000000harec-0.25.2+git.1750492315.966012b/makefiles07070100014E4E000081A40000000000000000000000016856649B000000C1000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/makefiles/freebsd.mkRTSCRIPT = 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
07070100014E4D000081A40000000000000000000000016856649B000000C9000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/makefiles/linux.mkRTSCRIPT = 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
07070100014E4C000081A40000000000000000000000016856649B000000CD000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/makefiles/netbsd.mkRTSCRIPT = 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
07070100014E4B000081A40000000000000000000000016856649B00000069000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/makefiles/openbsd.mkRTSCRIPT = rt/hare+$(PLATFORM).sc
_rt_ha = \
rt/malloc+libc.ha
_rt_s = \
rt/+openbsd/platformstart.s
07070100014E4A000081A40000000000000000000000016856649B00004ADF000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/makefiles/tests.mkTDENV = env HARE_TD_rt=$(HARECACHE)/rt.td HARE_TD_testmod=$(HARECACHE)/testmod.td
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/37-annotations
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: $(HARECACHE)/rt.o $(HARECACHE)/tests_30_reduction.o
@printf 'LD\t%s\t\n' '$@'
@$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_30_reduction.o
tests_30_reduction_ha = tests/30-reduction.ha
$(HARECACHE)/tests_30_reduction.ssa: $(tests_30_reduction_ha) $(HARECACHE)/rt.td $(BINOUT)/harec
@mkdir -p -- $(HARECACHE)
@printf 'HAREC\t%s\n' '$@'
@$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_30_reduction_ha)
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)
tests/37-annotations: $(HARECACHE)/rt.o $(HARECACHE)/tests_37_annotations.o
@printf 'LD\t%s\t\n' '$@'
@$(LD) $(LDLINKFLAGS) -T $(RTSCRIPT) -o $@ $(HARECACHE)/rt.o $(HARECACHE)/tests_37_annotations.o
tests_37_annotations_ha = tests/37-annotations.ha
$(HARECACHE)/tests_37_annotations.ssa: $(tests_37_annotations_ha) $(HARECACHE)/rt.td $(BINOUT)/harec
@mkdir -p -- $(HARECACHE)
@printf 'HAREC\t%s\n' '$@'
@$(TDENV) $(BINOUT)/harec $(HARECFLAGS) -o $@ $(tests_37_annotations_ha)
07070100014E11000041ED0000000000000000000000066856649B00000000000000000000002F00000000000000000000002700000000harec-0.25.2+git.1750492315.966012b/rt07070100014E3D000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000003000000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd07070100014E48000081A40000000000000000000000016856649B00000BFA000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/errno.haexport 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;
07070100014E47000081A40000000000000000000000016856649B00000138000000000000002F00000000000000000000003D00000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/segmalloc.ha// 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);
07070100014E46000081A40000000000000000000000016856649B00000058000000000000002F00000000000000000000004000000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/start+aarch64.s.text
.global _start
_start:
mov x29, #0
mov x30, #0
and sp, sp, #-16
b rt.start_ha
07070100014E45000081A40000000000000000000000016856649B00000041000000000000002F00000000000000000000004000000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/start+riscv64.s.text
.global _start
_start:
andi sp, sp, -16
tail rt.start_ha
07070100014E44000081A40000000000000000000000016856649B00000050000000000000002F00000000000000000000003F00000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/start+x86_64.s.text
.global _start
_start:
xor %rbp, %rbp
andq $-16, %rsp
call rt.start_ha
07070100014E43000081A40000000000000000000000016856649B00000372000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/start.ha@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 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(0);
};
07070100014E42000081A40000000000000000000000016856649B0000034E000000000000002F00000000000000000000004200000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/syscall+aarch64.s.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
07070100014E41000081A40000000000000000000000016856649B00000332000000000000002F00000000000000000000004200000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/syscall+riscv64.s.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
07070100014E40000081A40000000000000000000000016856649B000003E3000000000000002F00000000000000000000004100000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/syscall+x86_64.s.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
07070100014E3F000081A40000000000000000000000016856649B00003D7D000000000000002F00000000000000000000003D00000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/syscallno.haexport 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;
07070100014E3E000081A40000000000000000000000016856649B00000A96000000000000002F00000000000000000000003C00000000harec-0.25.2+git.1750492315.966012b/rt/+freebsd/syscalls.hafn 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;
07070100014E2F000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002E00000000harec-0.25.2+git.1750492315.966012b/rt/+linux07070100014E3C000081A40000000000000000000000016856649B00001004000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/rt/+linux/errno.haexport 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;
07070100014E3B000081A40000000000000000000000016856649B00000139000000000000002F00000000000000000000003B00000000harec-0.25.2+git.1750492315.966012b/rt/+linux/segmalloc.ha// 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);
07070100014E3A000081A40000000000000000000000016856649B00000064000000000000002F00000000000000000000003E00000000harec-0.25.2+git.1750492315.966012b/rt/+linux/start+aarch64.s.text
.global _start
_start:
mov x29, #0
mov x30, #0
mov x0, sp
add sp, x0, #-16
b rt.start_ha
07070100014E39000081A40000000000000000000000016856649B0000004C000000000000002F00000000000000000000003E00000000harec-0.25.2+git.1750492315.966012b/rt/+linux/start+riscv64.s.text
.global _start
_start:
mv a0, sp
andi sp, sp, -16
tail rt.start_ha
07070100014E38000081A40000000000000000000000016856649B00000061000000000000002F00000000000000000000003D00000000harec-0.25.2+git.1750492315.966012b/rt/+linux/start+x86_64.s.text
.global _start
_start:
xor %rbp, %rbp
movq %rsp, %rdi
andq $-16, %rsp
call rt.start_ha
07070100014E37000081A40000000000000000000000016856649B00000372000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/rt/+linux/start.ha@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 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(0);
};
07070100014E36000081A40000000000000000000000016856649B0000034E000000000000002F00000000000000000000004000000000harec-0.25.2+git.1750492315.966012b/rt/+linux/syscall+aarch64.s.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
07070100014E35000081A40000000000000000000000016856649B00000332000000000000002F00000000000000000000004000000000harec-0.25.2+git.1750492315.966012b/rt/+linux/syscall+riscv64.s.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
07070100014E34000081A40000000000000000000000016856649B000003E3000000000000002F00000000000000000000003F00000000harec-0.25.2+git.1750492315.966012b/rt/+linux/syscall+x86_64.s.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
07070100014E33000081A40000000000000000000000016856649B00002BBA000000000000002F00000000000000000000004300000000harec-0.25.2+git.1750492315.966012b/rt/+linux/syscallno+aarch64.haexport 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;
07070100014E32000081A40000000000000000000000016856649B00002AE8000000000000002F00000000000000000000004300000000harec-0.25.2+git.1750492315.966012b/rt/+linux/syscallno+riscv64.haexport 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;
07070100014E31000081A40000000000000000000000016856649B0000338D000000000000002F00000000000000000000004200000000harec-0.25.2+git.1750492315.966012b/rt/+linux/syscallno+x86_64.haexport 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;
07070100014E30000081A40000000000000000000000016856649B00000FAC000000000000002F00000000000000000000003A00000000harec-0.25.2+git.1750492315.966012b/rt/+linux/syscalls.hafn 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;
07070100014E27000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/rt/+netbsd07070100014E2E000081A40000000000000000000000016856649B00000BBA000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/rt/+netbsd/errno.haexport 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;
07070100014E2D000081A40000000000000000000000016856649B00000138000000000000002F00000000000000000000003C00000000harec-0.25.2+git.1750492315.966012b/rt/+netbsd/segmalloc.ha// 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);
07070100014E2C000081A40000000000000000000000016856649B00000103000000000000002F00000000000000000000003E00000000harec-0.25.2+git.1750492315.966012b/rt/+netbsd/start+x86_64.s.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
07070100014E2B000081A40000000000000000000000016856649B00000372000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/rt/+netbsd/start.ha@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 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(0);
};
07070100014E2A000081A40000000000000000000000016856649B000003E3000000000000002F00000000000000000000004000000000harec-0.25.2+git.1750492315.966012b/rt/+netbsd/syscall+x86_64.s.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
07070100014E29000081A40000000000000000000000016856649B00004291000000000000002F00000000000000000000003C00000000harec-0.25.2+git.1750492315.966012b/rt/+netbsd/syscallno.haexport 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;
07070100014E28000081A40000000000000000000000016856649B000009FB000000000000002F00000000000000000000003B00000000harec-0.25.2+git.1750492315.966012b/rt/+netbsd/syscalls.hafn 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;
07070100014E22000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000003000000000harec-0.25.2+git.1750492315.966012b/rt/+openbsd07070100014E26000081A40000000000000000000000016856649B00000BAF000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/rt/+openbsd/errno.haexport 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;
07070100014E25000081A40000000000000000000000016856649B00000053000000000000002F00000000000000000000004000000000harec-0.25.2+git.1750492315.966012b/rt/+openbsd/platformstart.s.section ".preinit_array"
.balign 8
.init.initfunc.0:
.quad preinit_hare+0
07070100014E24000081A40000000000000000000000016856649B00000382000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/rt/+openbsd/start.ha// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
let argc: size = 0;
let argv: *[*]*const u8 = null: *[*]*const u8;
let envp: *[*]nullable *const u8 = null: *[*]nullable *const u8;
// 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 intialization and finalization functions are not run from here,
// they are ran by crt0.
export @symbol("main") fn _main() void = {
main();
exit(0);
};
07070100014E23000081A40000000000000000000000016856649B00000885000000000000002F00000000000000000000003C00000000harec-0.25.2+git.1750492315.966012b/rt/+openbsd/syscalls.haexport @symbol("write") fn write(fd: int, buf: *const opaque, count: size) size;
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;
07070100014E21000081A40000000000000000000000016856649B00004156000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/rt/COPYINGMozilla 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.
07070100014E20000081A40000000000000000000000016856649B00000060000000000000002F00000000000000000000002E00000000harec-0.25.2+git.1750492315.966012b/rt/READMEThis is a basic Hare runtime. It is capable of running the harec test suite, but
not much else.
07070100014E1F000081A40000000000000000000000016856649B0000049E000000000000002F00000000000000000000003000000000harec-0.25.2+git.1750492315.966012b/rt/abort.hafn write_stderr(s: str) size = write(2, *(&s: **opaque), len(s));
export @symbol("rt.abort") fn _abort(
path: *str,
line: u64,
col: u64,
msg: str,
) void = {
write_stderr("Abort: ");
write_stderr(*path);
write_stderr(":");
write_stderr(u64tos(line));
write_stderr(":");
write_stderr(u64tos(col));
write_stderr(": ");
write_stderr(msg);
write_stderr("\n");
kill(getpid(), SIGABRT);
};
// See harec:include/expr.h enum fixed_aborts
const reasons: [_]str = [
"slice or array access out of bounds", // 0
"type assertion failed", // 1
"unreachable code", // 2
"slice allocation capacity smaller than initializer", // 3
"assertion failed", // 4
"error occurred", // 5
];
export fn abort_fixed(path: *str, line: u64, col: u64, i: u64) void = {
_abort(path, line, col, reasons[i]);
};
fn u64tos(u: u64) str = {
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: *str);
};
07070100014E1E000081A40000000000000000000000016856649B00000989000000000000002F00000000000000000000003200000000harec-0.25.2+git.1750492315.966012b/rt/compile.haexport type status = enum {
SUCCESS,
USER,
LEX,
PARSE,
CHECK,
_EXEC_FAILED,
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 status::_EXEC_FAILED =>
return "internal error: compiler execve failed";
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);
// FIXME use $BINOUT variable
let harec = alloc_cstr("./.bin/harec");
let argv: []nullable *u8 = alloc([harec]...)!;
for (let i = 0z; i < len(flags); i += 1) {
append(argv, alloc_cstr(flags[i]))!;
};
append(argv, [alloc_cstr("-o/dev/null"), alloc_cstr("-"), null]...)!;
execve(harec, *(&argv: **[*]nullable *u8), envp);
exit(status::_EXEC_FAILED);
} else {
assert(child != -1, "fork(2) failed");
close(pipefd[0]);
const buf = alloc_cstr(src): *const [*]u8;
defer free(buf);
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));
write_stderr("signaled ");
write_stderr(itos(wtermsig(wstatus)));
write_stderr("\n");
return error;
};
match (expected) {
case void =>
const status = wexitstatus(wstatus);
if (status == status::SUCCESS) {
write_stderr("expected any failure, got success\n");
return error;
};
case let expected: status =>
const exit_status = wexitstatus(wstatus);
if (exit_status == status::_EXEC_FAILED) {
write_stderr(strstatus(exit_status));
write_stderr("\n");
return error;
};
if (exit_status != expected) {
let s = "expected ";
write_stderr("expected ");
write_stderr(strstatus(expected));
write_stderr(", got ");
write_stderr(strstatus(exit_status));
write_stderr("\n");
return error;
};
};
};
07070100014E1D000081A40000000000000000000000016856649B000000BA000000000000002F00000000000000000000003300000000harec-0.25.2+git.1750492315.966012b/rt/cstrings.haexport fn toutf8(s: str) []u8 = *(&s: *[]u8);
fn alloc_cstr(s: str) *u8 = {
let c: []u8 = alloc([], len(s) + 1)!;
append(c, *(&s: *[]u8)...)!;
append(c, 0)!;
return *(&c: **u8);
};
07070100014E1C000081A40000000000000000000000016856649B000003AF000000000000002F00000000000000000000003100000000harec-0.25.2+git.1750492315.966012b/rt/ensure.ha// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
export type slice = struct {
data: nullable *opaque,
length: size,
capacity: size,
};
export fn ensure(s: *slice, membsz: size) bool = {
let cap = s.capacity;
if (cap >= s.length) {
return true;
};
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;
let data = realloc(s.data, s.capacity * membsz);
if (data == null) {
if (s.capacity * membsz == 0) {
s.data = null;
return true;
} else {
return false;
};
};
s.data = data;
return true;
};
export fn unensure(s: *slice, membsz: size) void = {
let cap = s.capacity;
for (cap > s.length) {
cap /= 2;
};
cap *= 2;
s.capacity = cap;
let data = realloc(s.data, s.capacity * membsz);
if (data != null || s.capacity * membsz == 0) {
s.data = data;
};
};
07070100014E1B000081A40000000000000000000000016856649B00000322000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/rt/hare+netbsd.scPHDRS {
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
}
07070100014E1A000081A40000000000000000000000016856649B00000032000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/rt/hare+openbsd.sc/* empty linker script; not needed for OpenBSD */
07070100014E19000081A40000000000000000000000016856649B000002B7000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/rt/hare.scPHDRS {
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
}
07070100014E18000081A40000000000000000000000016856649B00000216000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/rt/itos.ha// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
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);
};
07070100014E17000081A40000000000000000000000016856649B0000045A000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/rt/malloc+libc.ha// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
// 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;
07070100014E16000081A40000000000000000000000016856649B00002140000000000000002F00000000000000000000003100000000harec-0.25.2+git.1750492315.966012b/rt/malloc.ha// 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");
};
07070100014E15000081A40000000000000000000000016856649B000000A5000000000000002F00000000000000000000003100000000harec-0.25.2+git.1750492315.966012b/rt/memcpy.haexport 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];
};
};
07070100014E14000081A40000000000000000000000016856649B0000014C000000000000002F00000000000000000000003200000000harec-0.25.2+git.1750492315.966012b/rt/memmove.haexport 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];
};
};
};
07070100014E13000081A40000000000000000000000016856649B0000008E000000000000002F00000000000000000000003100000000harec-0.25.2+git.1750492315.966012b/rt/memset.haexport fn memset(dest: *opaque, val: u8, amt: size) void = {
let a = dest: *[*]u8;
for (let i = 0z; i < amt; i += 1) {
a[i] = val;
};
};
07070100014E12000081A40000000000000000000000016856649B00000101000000000000002F00000000000000000000003100000000harec-0.25.2+git.1750492315.966012b/rt/strcmp.haexport fn strcmp(a: str, b: str) bool = {
if (len(a) != len(b)) {
return false;
};
let ln = len(a);
let a = toutf8(a): *[*]u8, b = toutf8(b): *[*]u8;
for (let i = 0z; i < ln; i += 1) {
if (a[i] != b[i]) {
return false;
};
};
return true;
};
07070100014E0F000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002C00000000harec-0.25.2+git.1750492315.966012b/scripts07070100014E10000081ED0000000000000000000000016856649B0000019F000000000000002F00000000000000000000003400000000harec-0.25.2+git.1750492315.966012b/scripts/version#!/bin/sh
# Distro packagers may set the LOCALVER variable to add their distribution to
# the version, e.g. 1.0-alpine.
VERSION=${VERSION:-0.25.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
07070100014DFA000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002800000000harec-0.25.2+git.1750492315.966012b/src07070100014DFF000081A40000000000000000000000016856649B000226F3000000000000002F00000000000000000000003000000000harec-0.25.2+git.1750492315.966012b/src/check.c#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#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"
struct ident *
mkident(struct context *ctx, struct ident *in, const char *symbol)
{
if (symbol) {
return intern_name(ctx->itbl, symbol);
} else if (ctx->ns && in->ns == NULL) {
return intern_ident(ctx->itbl, in->name, ctx->ns);
} else {
return in;
}
}
static struct ident *
intern_generated(struct context *ctx, const char *template)
{
const char *s = intern_owned(ctx->itbl, gen_name(&ctx->id, template));
return intern_name(ctx->itbl, s);
}
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_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(struct expression *expr)
{
expr->type = EXPR_LITERAL;
expr->result = &builtin_type_error;
expr->literal.uval = 0;
expr->loc = (struct location){0};
}
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(expr);
expr->loc = loc;
}
va_list ap;
va_start(ap, fmt);
verror(ctx, loc, fmt, ap);
va_end(ap);
}
FORMAT(3, 4) 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->loc = expr->loc;
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 scope_object *obj);
static const struct type *
check_autodereference(struct context *ctx, struct location loc,
const struct type *type)
{
const struct type *dtype = type_dereference(ctx, type, false);
if (dtype == NULL) {
error(ctx, loc, NULL, "Cannot autodereference a nullable pointer");
}
return type_dereference(ctx, type, true);
}
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];
ident_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 = ident_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 = check_autodereference(ctx,
aexpr->access.array->loc, expr->access.array->result);
atype = type_dealias(ctx, atype);
if (atype->storage == STORAGE_ERROR) {
mkerror(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 be less 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 = check_autodereference(ctx,
aexpr->access._struct->loc, expr->access._struct->result);
stype = type_dealias(ctx, stype);
if (stype->storage == STORAGE_ERROR) {
mkerror(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 = check_autodereference(ctx,
aexpr->access.tuple->loc, expr->access.tuple->result);
ttype = type_dealias(ctx, ttype);
if (ttype->storage == STORAGE_ERROR) {
mkerror(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 '%" PRIu64 "'",
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 *inithint,
bool nullable)
{
// alloc(initializer) case
check_expression(ctx, aexpr->alloc.init, expr->alloc.init, inithint);
if (expr->alloc.init->result->storage == STORAGE_ERROR) {
mkerror(expr);
return;
}
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) {
uint32_t objtype_id = type_dealias(ctx, objtype)->id;
uint32_t inithint_id = type_dealias(ctx, inithint)->id;
if (objtype_id == inithint_id) {
objtype = inithint;
}
}
expr->alloc.allocation_result = type_store_lookup_pointer(ctx,
aexpr->loc, objtype, nullable);
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_cap(struct context *ctx,
const struct ast_expression *aexpr,
struct expression *expr,
const struct type *inithint)
{
// alloc(init, length/capacity) case
check_expression(ctx, aexpr->alloc.init, expr->alloc.init, inithint);
if (expr->alloc.init->result->storage == STORAGE_ERROR) {
mkerror(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->alloc.allocation_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 *inithint)
{
// alloc(init...) case
check_expression(ctx, aexpr->alloc.init, expr->alloc.init, inithint);
if (expr->alloc.init->result->storage == STORAGE_ERROR) {
mkerror(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;
}
result = type_dealias(ctx, expr->alloc.init->result);
expr->alloc.allocation_result = type_store_lookup_slice(ctx,
aexpr->alloc.init->loc, result->array.members);
}
static void
alloc_inithint(struct context *ctx,
const struct type *hint,
enum alloc_kind kind,
const struct type **inithint,
bool *nullable)
{
const struct type *htype = NULL;
hint = type_dealias(ctx, hint);
switch (hint->storage) {
case STORAGE_TAGGED:;
const struct type_tagged_union *first = &hint->tagged,
*second = first->next;
if (second->next != NULL) {
*inithint = NULL;
return;
}
if (first->type == &builtin_type_nomem) {
htype = second->type;
} else if (second->type == &builtin_type_nomem) {
htype = first->type;
} else {
*inithint = NULL;
return;
}
break;
case STORAGE_POINTER:
case STORAGE_SLICE:
// handle cases such as
// let a: alloc(0) as *u8;
// let b: []u8 = alloc([0])!;
htype = hint;
break;
default:
*inithint = NULL;
return;
}
switch (htype->storage) {
case STORAGE_POINTER:
if (kind == ALLOC_OBJECT) {
*inithint = htype->pointer.referent;
*nullable = htype->pointer.nullable;
}
break;
case STORAGE_SLICE:
*inithint = hint;
break;
default:
*inithint = NULL;
return;
};
}
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->result = &builtin_type_void;
expr->alloc.init = xcalloc(1, sizeof(struct expression));
expr->alloc.kind = aexpr->alloc.kind;
const struct type *inithint = NULL;
bool nullable = false;
if (hint != NULL) {
alloc_inithint(ctx, hint, expr->alloc.kind, &inithint, &nullable);
}
switch (aexpr->alloc.kind) {
case ALLOC_OBJECT:
check_expr_alloc_init(ctx, aexpr, expr, inithint, nullable);
break;
case ALLOC_CAP:
check_expr_alloc_cap(ctx, aexpr, expr, inithint);
break;
case ALLOC_COPY:
check_expr_alloc_copy(ctx, aexpr, expr, inithint);
break;
case ALLOC_LEN:
abort(); // Not determined by parse
}
if (expr->result == &builtin_type_error) {
return;
}
struct type_tagged_union nomem_tag = {
.type = &builtin_type_nomem,
.next = NULL,
};
struct type_tagged_union tags = {
.type = expr->alloc.allocation_result,
.next = &nomem_tag,
};
expr->result = type_store_lookup_tagged(ctx, aexpr->loc, &tags);
}
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;
struct type_tagged_union nomem_tag = {
.type = &builtin_type_nomem,
.next = NULL,
};
struct type_tagged_union tags = {
.type = &builtin_type_void,
.next = &nomem_tag,
};
expr->result = type_store_lookup_tagged(ctx, aexpr->loc, &tags);
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(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 the subject of for-each loop", exprtype_name);
}
sltype = check_autodereference(ctx, aexpr->append.object->loc, sltypename);
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->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) {
error(ctx, aexpr->append.value->loc, expr,
"Value must be an array or a slice 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 (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(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",
(int)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_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_ARRAY:
case STORAGE_BOOL:
case STORAGE_DONE:
case STORAGE_FUNCTION:
case STORAGE_NOMEM:
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:
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 scope_object *obj);
static bool
type_has_default(struct context *ctx, const struct type *type)
{
switch (type->storage) {
case STORAGE_BOOL:
case STORAGE_DONE:
case STORAGE_ERROR:
case STORAGE_F32:
case STORAGE_F64:
case STORAGE_I16:
case STORAGE_I32:
case STORAGE_I64:
case STORAGE_I8:
case STORAGE_INT:
case STORAGE_NOMEM:
case STORAGE_RUNE:
case STORAGE_SIZE:
case STORAGE_SLICE:
case STORAGE_STRING:
case STORAGE_U16:
case STORAGE_U32:
case STORAGE_U64:
case STORAGE_U8:
case STORAGE_UINT:
case STORAGE_UINTPTR:
case STORAGE_VOID:
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.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(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 %s and %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) {
if (unpack->object != NULL) {
unpack->next = xcalloc(1,
sizeof(struct binding_unpack));
unpack = unpack->next;
}
if (is_static) {
// Generate a static declaration ident
unpack->object = scope_insert(ctx->scope, O_DECL,
intern_generated(ctx, "static.%d"),
aunpack->name, type_tuple->type, NULL);
} else {
unpack->object = scope_insert(ctx->scope,
O_BIND, aunpack->name, aunpack->name,
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);
}
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;
}
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 initializer at compile time");
type = &builtin_type_error;
}
binding->initializer = value;
binding->object = scope_insert(ctx->scope, O_CONST,
abinding->name, abinding->name, NULL, value);
goto done;
}
if (!type) {
type = initializer->result;
// XXX why is this needed?
type = type_store_lookup_with_flags(ctx, type, 0);
}
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 ident
binding->object = scope_insert(ctx->scope, O_DECL,
intern_generated(ctx, "static.%d"),
abinding->name, type, NULL);
} else {
binding->object = scope_insert(ctx->scope, O_BIND,
abinding->name, abinding->name, 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 = check_autodereference(ctx,
aexpr->loc, lvalue->result);
fntype = type_dealias(ctx, fntype);
if (fntype->storage == STORAGE_ERROR) {
mkerror(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 = {
.object = NULL,
.slice.array = NULL,
.slice.len = 0,
},
};
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
|| secondary->storage == STORAGE_ERROR) {
mkerror(expr);
return;
}
switch (aexpr->cast.kind) {
case C_ASSERTION:
case C_TEST:
if (primary->storage == STORAGE_POINTER) {
if (!primary->pointer.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.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))) {
char *typename1 = gen_typename(secondary);
char *typename2 = gen_typename(primary);
error(ctx, aexpr->cast.type->loc, expr,
"Type %s is not a valid member of tagged union type %s",
typename1, typename2);
free(typename1);
free(typename2);
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 = aexpr->compound.label;
scope->label = 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);
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_DONE:
case STORAGE_NOMEM:
case STORAGE_NULL:
case STORAGE_VOID:
// 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 the 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 = check_autodereference(ctx, aexpr->loc, otype);
otype = type_dealias(ctx, otype);
if (otype->storage != STORAGE_SLICE) {
error(ctx, aexpr->delete.expr->loc, expr,
"delete must operate on a 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, NULL,
"Selected expression must%s be a loop",
want == SCOPE_COMPOUND ? " not" : "");
}
} else {
scope = scope_lookup_class(ctx->scope, want);
}
if (scope) {
if (expr->type == EXPR_BREAK) {
scope->has_break = true;
}
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, NULL,
"Cannot jump out of defer expression");
// continue checking so other errors can be reported
}
}
} else {
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, NULL, "%s", msg);
// continue checking so other errors can be reported
}
expr->control.scope = scope;
if (expr->type != EXPR_YIELD) {
return;
}
expr->control.value = xcalloc(1, sizeof(struct expression));
if (aexpr->control.value) {
const struct type *hint = scope ? scope->hint : NULL;
check_expression(ctx, aexpr->control.value,
expr->control.value, hint);
} else {
expr->control.value->type = EXPR_LITERAL;
expr->control.value->result = &builtin_type_void;
}
if (scope == NULL) {
return;
}
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);
// 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, check_autodereference(ctx,
abinding->initializer->loc, 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, false);
}
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 {
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,
abinding->name, abinding->name, 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 = aexpr->_for.label;
scope->label = 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 the subject of for-each loop");
}
enum type_storage storage = type_dealias(ctx, expr->free.expr->result)->storage;
if (storage == STORAGE_ERROR) {
mkerror(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;
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);
false_branch = xcalloc(1, sizeof(struct expression));
if (aexpr->_if.false_branch) {
check_expression(ctx, aexpr->_if.false_branch, false_branch, hint);
} else {
false_branch->type = EXPR_LITERAL;
false_branch->result = &builtin_type_void;
}
const struct type *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);
false_branch = lower_implicit_cast(ctx, expr->result, false_branch);
if (cond->result->storage == STORAGE_ERROR) {
mkerror(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 const char *
check_match_case_nullable_ptr(struct context *ctx,
const struct type *ctype,
const struct type *ref_type)
{
// match (e: nullable *ref_type) {
// case ctype =>
// Null has already been handled.
if (ctype->storage != STORAGE_POINTER) {
return "Match on nullable pointer: case is not null or pointer type";
} else if (ref_type != type_dealias(ctx, ctype->pointer.referent)) {
return "Match on nullable pointer: case has invalid pointer type";
}
return NULL;
}
static const char *
check_match_case_tagged(struct context *ctx,
const struct type *ctype,
const struct type *type)
{
// match (e: type) {
// case ctype =>
// TODO: Assign a score to tagged compatibility
// and choose the branch with the highest score.
if (!type_is_assignable(ctx, type, ctype)) {
return "Match on tagged union: case is not assignable to match type";
}
return NULL;
}
static const char *
check_match_case_tagged_ptr(struct context *ctx,
const struct type *ctype,
const struct type *ref_type)
{
// match (e: *ref_type) {
// case ctype =>
if (ctype->size == 0) {
if (!type_is_assignable(ctx, ref_type, ctype)) {
return "Match on pointer to tagged union: zero-sized case type is not assignable to match type";
}
} else if (ctype->storage != STORAGE_POINTER) {
return "Match on pointer to tagged union: finite-sized case type is not a pointer";
} else if (!type_is_assignable(ctx, ref_type, ctype->pointer.referent)) {
return "Match on pointer to tagged union: case is not assignable to match type";
}
return NULL;
}
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(expr);
return;
}
bool is_tagged = type->storage == STORAGE_TAGGED;
bool is_nullable_ptr = false, is_tagged_ptr = false;
const struct type *ref_type = NULL;
if (type->storage == STORAGE_POINTER) {
is_nullable_ptr = type->pointer.nullable;
ref_type = type_dealias(ctx, type->pointer.referent);
if (ref_type->storage == STORAGE_ERROR) {
mkerror(expr);
return;
}
is_tagged_ptr = ref_type->storage == STORAGE_TAGGED;
}
if (!is_tagged && !is_nullable_ptr && !is_tagged_ptr) {
error(ctx, aexpr->match.value->loc, expr,
"Match value is not tagged union, pointer to 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);
const char *err_msg = NULL;
if (ctype->storage == STORAGE_NULL && is_nullable_ptr) {
// Ok in all cases.
} else if (is_nullable_ptr && !is_tagged_ptr) {
err_msg = check_match_case_nullable_ptr(ctx,
ctype, ref_type);
} else if (is_tagged_ptr) {
err_msg = check_match_case_tagged_ptr(ctx,
ctype, ref_type);
} else {
assert(is_tagged);
err_msg = check_match_case_tagged(ctx,
ctype, type);
}
if (err_msg) {
error(ctx, acase->type->loc, expr, "%s", err_msg);
return;
}
}
if (acase->name != NULL) {
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 scope *scope = scope_push(&ctx->scope, SCOPE_MATCH);
_case->object = scope_insert(scope, O_BIND, acase->name,
acase->name, 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,
.loc = acase->exprs.expr->loc,
.compound = {
.label = aexpr->match.label,
.list = acase->exprs,
},
};
check_expression(ctx, &compound, _case->value, hint);
if (acase->name != NULL) {
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 (hint && !type_is_assignable(ctx, hint, _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 = check_autodereference(ctx,
aexpr->measure.value->loc, expr->len.value->result);
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(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(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 ident *id = intern_generated(ctx, "ok.%d");
ok_obj = scope_insert(scope, O_BIND, id, id, 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;
case_ok->value->loc = expr->loc;
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));
case_err->value->loc = expr->loc;
if (aexpr->propagate.abort) {
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 ident *id = intern_generated(ctx, "err.%d");
err_obj = scope_insert(scope, O_BIND, id, id, 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;
rval->loc = expr->loc;
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, NULL,
"Cannot return inside a defer expression");
// continue checking so other errors can be reported
}
expr->type = EXPR_RETURN;
expr->result = &builtin_type_never;
struct expression *rval = expr->_return.value =
xcalloc(1, sizeof(struct expression));
if (aexpr->_return.value) {
const struct type *hint = NULL;
if (ctx->fntype) {
hint = ctx->fntype->func.result;
}
check_expression(ctx, aexpr->_return.value, rval, hint);
} else {
rval->type = EXPR_LITERAL;
rval->result = &builtin_type_void;
}
if (ctx->fntype == NULL) {
error(ctx, aexpr->loc, NULL, "Cannot return outside a function body");
return;
}
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, NULL,
"Return type %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, false);
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(expr);
return;
}
const struct type *atype = check_autodereference(ctx,
aexpr->slice.object->loc, expr->slice.object->result);
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;
}
if (dtype->array.members->size == SIZE_UNDEFINED) {
error(ctx, aexpr->slice.start->loc, expr,
"Cannot use left subslicing operand on a slice with member type of unknown size");
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 != NULL) {
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,
"Type named is not a struct or union type");
return;
}
}
struct ast_type satype = {
.storage = STORAGE_STRUCT,
};
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);
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_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:;
struct scope_object *obj = type->_enum.values->objects;
assert(obj != NULL);
size_t n = 0;
for (struct scope_object *o = obj; o; o = o->lnext, ++n) {
if (o->otype == O_SCAN) {
wrap_resolver(ctx, o, resolve_enum_field);
}
assert(o->otype == O_CONST);
}
struct expression **cases_array =
xcalloc(n, sizeof(struct expression *));
size_t i = 0;
for (struct scope_object *o = obj; o; o = o->lnext, ++i) {
cases_array[i] = o->value;
}
qsort(cases_array, n, sizeof(struct expression *), &casecmp);
for (size_t i = 1; i < n; ++i) {
if (casecmp(&cases_array[i - 1], &cases_array[i]) == 0) {
--n;
}
}
free(cases_array);
return n;
default:
assert(type_is_integer(ctx, type)
|| 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_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 *tagged = NULL, *result = NULL;
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 (tagged == NULL) {
result = tagged = xcalloc(1, sizeof *tagged);
} else if (tagged->type && tagged->type != _case->value->result) {
tagged = tagged->next = xcalloc(1, sizeof *tagged);
}
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);
if (opt->value->result->storage != STORAGE_ERROR) {
cases_array[i] = opt->value;
i++;
}
}
}
n = i;
qsort(cases_array, n, sizeof(struct expression *), &casecmp);
bool has_duplicate = false;
for (size_t i = 1; i < n; i++) {
if (casecmp(&cases_array[i - 1], &cases_array[i]) == 0) {
error(ctx, cases_array[i - 1]->loc, cases_array[i - 1],
"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 (hint) {
expr->result = hint;
} else {
expr->result = type_store_reduce_result(
ctx, aexpr->loc, result);
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;
}
while (result) {
struct type_tagged_union *next = result->next;
free(result);
result = 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);
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) {
uint32_t result_id = type_dealias(ctx, operand->result)->id;
uint32_t ptrhint_id = type_dealias(ctx, ptrhint)->id;
if (result_id == ptrhint_id) {
operand->result = ptrhint;
}
}
expr->result = type_store_lookup_pointer(
ctx, aexpr->loc, operand->result, false);
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.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;
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 = type_store_lookup_atype(ctx, aexpr->vaarg.type);
if (expr->result->size == SIZE_UNDEFINED) {
error(ctx, aexpr->loc, expr, "vaarg type must have defined size");
return;
}
}
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);
}
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);
}
}
static 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(ctx->itbl, obj->ident);
decl->ident = mkident(ctx, 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 == NULL) {
error(ctx, params->loc, NULL,
"Function parameters must be named");
return;
}
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, params->name,
params->name, 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);
} else if (obj->type->func.variadism != VARIADISM_NONE) {
error(ctx, adecl->loc, NULL, "%s function cannot be variadic", 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,
"Expression result type %s is not assignable to function result type %s",
restypename, fntypename);
free(restypename);
free(fntypename);
return;
}
if (obj->type->func.result->storage != STORAGE_NEVER &&
obj->type->func.result->size == SIZE_UNDEFINED) {
char *fntypename = gen_typename(obj->type->func.result);
error(ctx, afndecl->body->loc, body,
"Types with undefined size such as %s cannot be returned, consider using a pointer instead",
fntypename);
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 scope_object *
incomplete_decl_create(struct context *ctx, struct location loc,
struct scope *scope, struct ident *ident, struct ident *name)
{
struct scope *subunit = ctx->unit->parent;
ctx->unit->parent = NULL;
struct scope_object *obj = scope_lookup(scope, name);
ctx->unit->parent = subunit;
if (obj) {
error_norec(ctx, loc, "Duplicate global ident '%s'",
ident_unparse(ident));
}
obj = scope_insert(scope, O_SCAN, ident, name, NULL, NULL);
obj->idecl = xcalloc(1, sizeof(struct incomplete_decl));
return obj;
}
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 ident *name = intern_ident(ctx->itbl, f->name->name, etype->alias.name);
struct scope_object *obj = incomplete_decl_create(
ctx, f->loc, enum_scope, name, f->name);
obj->idecl->type = IDECL_ENUM_FLD;
obj->idecl->imports = imports;
obj->type = etype,
obj->idecl->field = field;
}
static void
check_hosted_main(struct context *ctx,
struct location loc,
const struct ast_decl *decl,
struct ident *ident,
const char *symbol)
{
if (*ctx->mainsym == '\0' || ctx->is_test) {
return;
}
if (symbol != ctx->mainsym && (symbol != NULL || ident != ctx->mainident)) {
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.variadism != VARIADISM_NONE) {
error(ctx, loc, NULL,
"main must not be variadic 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 ident *with_ns = mkident(ctx, t->ident, NULL);
check_hosted_main(ctx, decl->loc, NULL, with_ns, NULL);
struct scope_object *obj = incomplete_decl_create(ctx,
decl->loc, ctx->scope, with_ns, t->ident);
obj->idecl->decl = (struct ast_decl){
.decl_type = ADECL_TYPE,
.loc = decl->loc,
.type = *t,
.exported = decl->exported,
};
obj->idecl->imports = imp;
if (t->type->storage == STORAGE_ENUM) {
bool exported = obj->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;
obj->otype = O_TYPE;
obj->type = type;
append_decl(ctx, &(struct declaration){
.decl_type = DECL_TYPE,
.file = decl->loc.file,
.ident = obj->ident,
.exported = exported,
.type = type,
});
} else {
obj->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_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_NEVER:
case STORAGE_NOMEM:
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:
break;
}
}
static void
resolve_const(struct context *ctx, struct scope_object *obj)
{
const struct ast_global_decl *decl = &obj->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));
obj->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, obj->value,
"Null is not a valid type for a constant");
type = &builtin_type_error;
goto end;
}
}
if (obj->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, obj->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, obj->value)) {
error(ctx, decl->init->loc, obj->value,
"Unable to evaluate initializer at compile time");
type = &builtin_type_error;
goto end;
}
end:
obj->otype = O_CONST;
if (!ctx->defines || ctx->errors) {
return;
}
struct scope_object *shadow_obj = scope_lookup(ctx->defines, obj->ident);
if (shadow_obj && obj != shadow_obj) {
// Shadowed by define
if (type_is_flexible(obj->value->result)
|| type_is_flexible(shadow_obj->value->result)) {
const struct type *promoted = promote_flexible(ctx,
obj->value->result, shadow_obj->value->result);
if (promoted == NULL) {
const char *msg;
char *typename = NULL;
if (!type_is_flexible(obj->value->result)) {
msg = "Constant of type %s is shadowed by define of incompatible flexible type";
typename = gen_typename(obj->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, obj->idecl->decl.loc, NULL, msg, typename);
free(typename);
} else {
shadow_obj->value = lower_implicit_cast(ctx,
promoted, shadow_obj->value);
}
} else if (obj->value->result != shadow_obj->value->result) {
char *typename = gen_typename(obj->value->result);
char *shadow_typename = gen_typename(shadow_obj->value->result);
error(ctx, obj->idecl->decl.loc, NULL,
"Constant of type %s is shadowed by define of incompatible type %s",
typename, shadow_typename);
free(typename);
free(shadow_typename);
}
obj->value = shadow_obj->value;
}
append_decl(ctx, &(struct declaration){
.decl_type = DECL_CONST,
.file = obj->idecl->decl.loc.file,
.ident = obj->ident,
.exported = obj->idecl->decl.exported,
.constant = {
.type = type,
.value = obj->value,
}
});
}
void
resolve_function(struct context *ctx, struct scope_object *obj)
{
const struct ast_function_decl *decl = &obj->idecl->decl.function;
const struct ast_type fn_atype = {
.loc = obj->idecl->decl.loc,
.storage = STORAGE_FUNCTION,
.flags = 0,
.func = decl->prototype,
};
const struct type *fntype = type_store_lookup_atype(ctx, &fn_atype);
if (obj->idecl->decl.exported) {
check_exported_type(ctx, obj->idecl->decl.loc, fntype);
}
obj->otype = O_DECL;
obj->type = fntype;
}
void
resolve_global(struct context *ctx, struct scope_object *obj)
{
const struct ast_global_decl *decl = &obj->idecl->decl.global;
const struct type *type = NULL;
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 initializer");
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,
"Can't initialize global as null without explicit type hint");
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 (obj->idecl->decl.exported) {
struct location loc =
decl->type ? decl->type->loc : decl->init->loc;
check_exported_type(ctx, loc, type);
}
end:;
struct ident *name = mkident(ctx, obj->name, NULL);
obj->otype = O_DECL;
obj->type = type;
if (decl->threadlocal) {
obj->flags |= SO_THREADLOCAL;
}
append_decl(ctx, &(struct declaration){
.decl_type = DECL_GLOBAL,
.file = obj->idecl->decl.loc.file,
.ident = name,
.symbol = ident_to_sym(ctx->itbl, obj->ident),
.exported = obj->idecl->decl.exported,
.global = {
.type = type,
.value = value,
.threadlocal = obj->idecl->decl.global.threadlocal,
}
});
}
static void
resolve_enum_field(struct context *ctx, struct scope_object *obj)
{
assert(obj->idecl->type == IDECL_ENUM_FLD);
const struct type *type = obj->type;
struct ident *localname = intern_name(ctx->itbl, obj->ident->name);
struct scope_object *new =
scope_lookup(obj->idecl->field->enum_scope, localname);
if (new != obj) {
wrap_resolver(ctx, new, resolve_enum_field);
assert(new->otype == O_CONST);
obj->otype = O_CONST;
obj->value = new->value;
return;
}
ctx->scope = obj->idecl->field->enum_scope;
obj->value = xcalloc(1, sizeof(struct expression));
obj->value->result = type;
if (obj->idecl->field->field->value) { // explicit value
struct expression *initializer =
xcalloc(1, sizeof(struct expression));
check_expression(ctx, obj->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, obj->idecl->field->field->value->loc,
"Enum value type (%s) is not assignable from initializer type (%s) for value %s",
builtintypename, inittypename, obj->ident->name);
}
initializer = lower_implicit_cast(ctx, type, initializer);
if (!eval_expr(ctx, initializer, obj->value)) {
error_norec(ctx, obj->idecl->field->field->value->loc,
"Unable to evaluate constant initializer at compile time");
}
} else { // implicit value
struct scope_object *next = obj->lnext;
// find previous enum value
wrap_resolver(ctx, next, resolve_enum_field);
obj->value->type = EXPR_LITERAL;
if (type_is_signed(ctx, type_dealias(ctx, type))) {
if (next == NULL) {
obj->value->literal.ival = 0;
} else {
obj->value->literal.ival = next->value->literal.ival + 1;
}
} else {
if (next == NULL) {
obj->value->literal.uval = 0;
} else {
obj->value->literal.uval = next->value->literal.uval + 1;
}
}
}
obj->otype = O_CONST;
}
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: {
if (obj->idecl->in_progress) {
// Type alias cycle will be handled in check
return NULL;
}
if (obj->idecl->type != IDECL_DECL ||
obj->idecl->decl.decl_type != ADECL_TYPE) {
return NULL;
}
if (obj->idecl->decl.type.type->storage == STORAGE_ENUM) {
assert(false);
} else if (obj->idecl->decl.type.type->storage == STORAGE_ALIAS) {
ctx->scope->parent = obj->idecl->imports;
const struct scope_object *new = scope_lookup(ctx->scope,
obj->idecl->decl.type.type->alias);
if (new) {
obj->idecl->in_progress = true;
enum_type = lookup_enum_type(ctx, new);
obj->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 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 = (struct ident *)val->name,
};
struct incomplete_enum_field *field =
xcalloc(1, sizeof(struct incomplete_enum_field));
*field = (struct incomplete_enum_field){
.field = afield,
.enum_scope = val->idecl->field->enum_scope,
};
struct ident *name =
intern_ident(ctx->itbl, val->name->name, obj->name);
struct scope_object *new = incomplete_decl_create(ctx,
(struct location){0}, ctx->scope, name, name);
new->idecl->type = IDECL_ENUM_FLD;
new->type = obj->type;
new->idecl->field = field;
}
}
void
resolve_dimensions(struct context *ctx, struct scope_object *obj)
{
if (obj->idecl->type != IDECL_DECL || obj->idecl->decl.decl_type != ADECL_TYPE) {
struct location loc;
if (obj->idecl->type == IDECL_ENUM_FLD) {
loc = obj->idecl->field->field->loc;
} else {
loc = obj->idecl->decl.loc;
}
char *ident = ident_unparse(obj->name);
error(ctx, loc, NULL, "'%s' is not a type", ident);
free(ident);
obj->type = &builtin_type_error;
return;
}
struct dimensions dim = type_store_lookup_dimensions(ctx,
obj->idecl->decl.type.type);
obj->type = xcalloc(1, sizeof(struct type));
*(struct type *)obj->type = (struct type){
.size = dim.size,
.align = dim.align,
};
}
void
resolve_type(struct context *ctx, struct scope_object *obj)
{
struct location loc;
if (obj->idecl->type == IDECL_ENUM_FLD) {
loc = obj->idecl->field->field->loc;
} else {
loc = obj->idecl->decl.loc;
}
if (obj->idecl->type != IDECL_DECL || obj->idecl->decl.decl_type != ADECL_TYPE) {
error_norec(ctx, loc, "'%s' is not a type",
ident_unparse(obj->name));
}
// compute type dimensions
struct errors **cur_err = ctx->next;
struct dimensions dim = type_store_lookup_dimensions(
ctx, obj->idecl->decl.type.type);
obj->idecl->in_progress = false;
// compute type representation and store it
struct type *alias = (struct type *)type_store_lookup_alias(ctx, obj->ident,
obj->name, NULL, obj->idecl->decl.type.type->flags,
obj->idecl->decl.exported);
obj->otype = O_TYPE;
obj->type = alias;
if (ctx->next == cur_err) {
alias->size = dim.size;
alias->align = dim.align;
alias->alias.type = type_store_lookup_atype(
ctx, obj->idecl->decl.type.type);
} else {
alias->alias.type = &builtin_type_error;
}
assert(alias->alias.type != NULL);
if (obj->idecl->decl.exported) {
check_exported_type(ctx, obj->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;
}
append_decl(ctx, &(struct declaration){
.decl_type = DECL_TYPE,
.file = obj->idecl->decl.loc.file,
.ident = obj->ident,
.exported = obj->idecl->decl.exported,
.type = alias,
});
}
static struct scope_object *
scan_const(struct context *ctx, struct scope *imports, bool exported,
struct location loc, const struct ast_global_decl *decl)
{
struct ident *with_ns = mkident(ctx, decl->ident, NULL);
check_hosted_main(ctx, loc, NULL, with_ns, NULL);
struct scope_object *obj = incomplete_decl_create(ctx, loc,
ctx->scope, with_ns, decl->ident);
obj->idecl->type = IDECL_DECL;
obj->idecl->decl = (struct ast_decl){
.decl_type = ADECL_CONST,
.loc = loc,
.constant = *decl,
.exported = exported,
};
obj->idecl->imports = imports;
return obj;
}
static void
scan_decl(struct context *ctx, struct scope *imports, const struct ast_decl *decl)
{
struct scope_object *obj;
struct ident *ident;
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) {
ident = mkident(ctx, g->ident, g->symbol);
check_hosted_main(ctx, decl->loc, NULL, ident, g->symbol);
obj = incomplete_decl_create(ctx, decl->loc,
ctx->scope, ident, g->ident);
obj->idecl->type = IDECL_DECL;
obj->idecl->decl = (struct ast_decl){
.decl_type = ADECL_GLOBAL,
.loc = decl->loc,
.global = *g,
.exported = decl->exported,
};
obj->idecl->imports = imports;
}
break;
case ADECL_FUNC:;
const struct ast_function_decl *func = &decl->function;
struct ident *name;
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 = intern_generated(ctx, template);
} else {
ident = mkident(ctx, func->ident, func->symbol);
name = func->ident;
}
obj = incomplete_decl_create(ctx, decl->loc,
ctx->scope, ident, name);
check_hosted_main(ctx, decl->loc, decl, ident, func->symbol);
obj->idecl->type = IDECL_DECL;
obj->idecl->decl = (struct ast_decl){
.decl_type = ADECL_FUNC,
.loc = decl->loc,
.function = *func,
.exported = decl->exported,
};
obj->idecl->imports = imports;
break;
case ADECL_TYPE:
scan_types(ctx, imports, decl);
break;
case ADECL_ASSERT:;
struct ident *id = intern_generated(ctx, "static_assert.%d");
obj = incomplete_decl_create(ctx, decl->loc, ctx->scope, id, id);
obj->idecl->type = IDECL_DECL;
obj->idecl->decl = (struct ast_decl){
.decl_type = ADECL_ASSERT,
.loc = decl->loc,
.assert = decl->assert,
.exported = decl->exported,
};
obj->idecl->imports = imports;
break;
}
}
static void
resolve_decl(struct context *ctx, struct scope_object *obj)
{
switch (obj->idecl->type) {
case IDECL_ENUM_FLD:
resolve_enum_field(ctx, obj);
return;
case IDECL_DECL:
break;
}
switch (obj->idecl->decl.decl_type) {
case ADECL_CONST:
resolve_const(ctx, obj);
return;
case ADECL_GLOBAL:
resolve_global(ctx, obj);
return;
case ADECL_FUNC:
resolve_function(ctx, obj);
return;
case ADECL_TYPE:
resolve_type(ctx, obj);
return;
case ADECL_ASSERT:;
struct expression expr = {0};
check_assert(ctx, obj->idecl->decl.assert, obj->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;
// load this declaration's subunit context
ctx->scope = ctx->defines;
ctx->unit->parent = obj->idecl->imports;
// resolving a declaration that is already in progress -> cycle
if (obj->idecl->in_progress) {
struct location loc;
if (obj->idecl->type == IDECL_ENUM_FLD) {
loc = obj->idecl->field->field->loc;
} else {
loc = obj->idecl->decl.loc;
}
error_norec(ctx, loc, "Circular dependency for '%s'",
ident_unparse(obj->name));
}
obj->idecl->in_progress = true;
resolver(ctx, obj);
obj->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 ident *ident = intern_ident(ctx->itbl,
member->name->name, import->ident);
const struct scope_object *obj = scope_lookup(mod, ident);
if (!obj) {
error_norec(ctx, member->loc, "Unknown object '%s'",
ident_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, member->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 ident *value_ident =
intern_ident(ctx->itbl, o->name->name, ident);
struct ident *value_name =
intern_ident(ctx->itbl, o->name->name, member->name);
scope_insert(scope, o->otype, value_ident,
value_name, NULL, o->value);
}
}
return;
}
struct ident *prefix;
switch (import->mode) {
case IMPORT_NORMAL:
prefix = intern_name(ctx->itbl, import->ident->name);
break;
case IMPORT_ALIAS:
prefix = intern_name(ctx->itbl, 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 ident *name;
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",
ident_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 ident if object is an enum
// constant
struct ident *ns =
intern_ident(ctx->itbl, obj->name->ns->name, prefix);
name = intern_ident(ctx->itbl, obj->name->name, ns);
} else {
name = intern_ident(ctx->itbl, obj->name->name, prefix);
}
// 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,
struct ident *mainident,
const struct ast_global_decl *defines,
const struct ast_unit *aunit,
struct unit *unit,
struct intern_table *itbl,
bool scan_only)
{
struct context ctx = {0};
ctx.ns = unit->ns;
ctx.is_test = is_test;
ctx.mainsym = mainsym;
ctx.mainident = mainident;
ctx.store = ts;
ctx.next = &ctx.errors;
ctx.modcache = cache;
ctx.itbl = itbl;
// 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 scope_object *obj =
scan_const(&ctx, NULL, false , defineloc, def);
resolve_const(&ctx, obj);
}
ctx.defines = ctx.scope;
ctx.scope = NULL;
ctx.defines->parent = ctx.unit = scope_push(&ctx.scope, SCOPE_UNIT);
sources[0] = "<unknown>";
// 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 (uimports->ident == imports->ident) {
found = true;
break;
}
}
if (!found) {
struct identifiers *uimport = *inext =
xcalloc(1, sizeof(struct identifiers));
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) {
if (shadowed_obj->idecl->type == IDECL_DECL &&
shadowed_obj->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
if (obj->idecl->type == IDECL_DECL && obj->idecl->decl.decl_type == ADECL_FUNC) {
ctx.unit->parent = obj->idecl->imports;
check_function(&ctx, obj, &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,
struct ident *mainident,
const struct ast_global_decl *defines,
const struct ast_unit *aunit,
struct unit *unit,
struct intern_table *itbl)
{
struct modcache *modcache[MODCACHE_BUCKETS] = {0};
return check_internal(ts, modcache, is_test, mainsym, mainident, defines, aunit, unit, itbl, false);
}
07070100014E0E000081A40000000000000000000000016856649B0000204A000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/src/emit.c#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#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, bool sign, FILE *out)
{
assert(type);
switch (type->stype) {
case Q_BYTE:
case Q_HALF:
xfprintf(out, "%s", sign ? (type->sgn ? "s" : "u") : "");
/* fallthrough */
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, false, 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, 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, false, 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, 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, true, out);
}
xfprintf(out, " $%s(", def->name);
const struct qbe_func_param *param = def->func.params;
while (param) {
emit_qtype(param->type, true, 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, false, 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", full_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;
}
}
07070100014E0D000081A40000000000000000000000016856649B0000896A000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/src/eval.c#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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_SIZE:
case STORAGE_UINTPTR:
return 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_BOOL:
case STORAGE_DONE:
case STORAGE_F32:
case STORAGE_F64:
case STORAGE_FCONST:
case STORAGE_FUNCTION:
case STORAGE_NEVER:
case STORAGE_NOMEM:
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_VALIST:
case STORAGE_VOID:
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_dealias(ctx, lvalue.result)->storage == STORAGE_POINTER) {
return false;
} else 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)) {
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 if (type_dealias(ctx, in->result)->storage == STORAGE_POINTER) {
return false;
} else {
assert(type_is_integer(ctx, in->result));
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_DONE:
case STORAGE_ERROR:
case STORAGE_F64:
case STORAGE_FCONST:
case STORAGE_NOMEM:
case STORAGE_NULL:
case STORAGE_POINTER:
case STORAGE_SLICE:
case STORAGE_VOID:
out->literal = in->literal;
break;
case STORAGE_F32:
out->literal.fval = (float)in->literal.fval;
break;
case STORAGE_I16:
case STORAGE_I32:
case STORAGE_I64:
case STORAGE_I8:
case STORAGE_ICONST:
case STORAGE_INT:
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:
out->literal.uval = itrunc(ctx, in->result, in->literal.uval);
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_dealias(ctx, from)->storage == STORAGE_POINTER) {
return false;
} else 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_DONE:
case STORAGE_ERROR:
case STORAGE_NOMEM:
case STORAGE_VOID:
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, false);
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};
const struct type *t = type_dealias(ctx, v->result);
switch (t->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:
assert(!t->array.expandable); // Invariant
if (t->array.length == SIZE_UNDEFINED) {
return false;
}
struct array_literal **next = &v->literal.array;
for (size_t i = 0; i < t->array.length; i++) {
*next = xcalloc(1, sizeof(struct array_literal));
(*next)->value = xcalloc(1, sizeof(struct expression));
(*next)->value->type = EXPR_LITERAL;
(*next)->value->result = t->array.members;
if (!literal_default(ctx, (*next)->value)) {
return false;
}
next = &(*next)->next;
}
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_DONE:
case STORAGE_NOMEM:
case STORAGE_VOID:
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_address_object(struct context *ctx,
const struct expression *restrict in,
struct expression *restrict out)
{
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;
}
return true;
}
static bool
eval_address_other(struct context *ctx,
const struct expression *restrict in,
struct expression *restrict out)
{
struct expression *value = xcalloc(1, sizeof(struct expression));
if (!eval_expr(ctx, in->unarithm.operand, value)) {
return false;
}
static int serial = 0;
char *symbol = gen_name(&serial, "static.%d");
struct ident *name = mkident(ctx, NULL, symbol);
append_decl(ctx, &(struct declaration){
.decl_type = DECL_GLOBAL,
.file = in->loc.file,
.ident = name,
.symbol = symbol,
.exported = false,
.global = {
.type = value->result,
.value = value,
.threadlocal = false,
}
});
struct scope_object *obj = scope_insert(ctx->scope,
O_DECL, name, name, value->result, NULL);
struct expression shadow = *in;
shadow.unarithm.operand = &(struct expression){
.type = EXPR_ACCESS,
.access = (struct expression_access){
.type = ACCESS_IDENTIFIER,
.object = obj,
},
};
bool r = eval_address_object(ctx, &shadow, out);
assert(r);
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;
}
switch (in->unarithm.operand->type) {
case EXPR_ACCESS:
return eval_address_object(ctx, in, out);
default:
return eval_address_other(ctx, in, out);
}
}
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->loc = in->loc;
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); // Unreachable
}
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
}
07070100014E0C000081A40000000000000000000000016856649B000009AA000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/src/expr.c#include <math.h>
#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_DONE:
case STORAGE_ERROR:
case STORAGE_NOMEM:
case STORAGE_NULL:
case STORAGE_VOID:
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;
}
07070100014E0B000081A40000000000000000000000016856649B0001D5C0000000000000002F00000000000000000000002E00000000harec-0.25.2+git.1750492315.966012b/src/gen.c#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "check.h"
#include "expr.h"
#include "gen.h"
#include "scope.h"
#include "types.h"
#include "util.h"
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 void gen_expr_branch(struct gen_context *ctx,
const struct expression *expr, struct gen_value merged,
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);
if (value.type->size == 0 || value.type->storage == STORAGE_NEVER) {
return; // no storage
}
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
}
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)
{
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_expr(struct gen_context *ctx, const struct expression *expr)
{
struct gen_value val = gen_expr(ctx, expr);
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;
}
}
abort();
case O_DECL:
return (struct gen_value){
.kind = GV_GLOBAL,
.type = obj->type,
.name = ident_to_sym(ctx->itbl, 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_autoderef_expr(ctx, expr->access.array);
struct gen_value out = mkgtemp(ctx, expr->result, ".%d");
struct qbe_value qival = mklval(ctx, &out);
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 out;
}
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_autoderef_expr(ctx, expr->access._struct);
if (field->type->size == 0) {
return gv_void;
}
struct gen_value out = mkgtemp(ctx, field->type, "field.%d");
struct qbe_value qfval = mklval(ctx, &out);
struct qbe_value qlval = mkqval(ctx, &glval);
struct qbe_value offs = constl(field->offset);
pushi(ctx->current, &qfval, Q_ADD, &qlval, &offs, NULL);
return out;
}
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_autoderef_expr(ctx, expr->access.tuple);
if (tuple->type->size == 0) {
return gv_void;
}
struct gen_value out = mkgtemp(ctx, tuple->type, "field.%d");
struct qbe_value qfval = mklval(ctx, &out);
struct qbe_value qlval = mkqval(ctx, &glval);
struct qbe_value offs = constl(tuple->offset);
pushi(ctx->current, &qfval, Q_ADD, &qlval, &offs, NULL);
return out;
}
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:
if (expr->access.object->type->size == 0) {
return gv_void;
}
addr = gen_access_ident(ctx, expr->access.object);
break;
case ACCESS_INDEX:
addr = gen_access_index(ctx, expr);
break;
case ACCESS_FIELD:
if (expr->access._struct->result->size == 0) {
return gv_void;
}
addr = gen_access_field(ctx, expr);
break;
case ACCESS_TUPLE:
if (expr->access.tuple->result->size == 0) {
return gv_void;
}
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 gv_void;
}
return gen_load(ctx, addr);
}
static struct gen_value
gen_expr_alloc_slice_array_at(struct gen_context *ctx,
const struct expression *expr, struct gen_value out)
{
struct qbe_value qout = mklval(ctx, &out);
enum alloc_kind kind = expr->alloc.kind;
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_value qnull = constl(0);
struct qbe_value qisnull = mkqtmp(ctx, &qbe_word, ".%d");
struct gen_value newdata = mkgtemp(ctx, inittype, ".%d");
struct qbe_value qnewdata = mklval(ctx, &newdata);
struct qbe_statement lzero, lnonzero, loom, lvalid, lend;
struct qbe_value bzero = mklabel(ctx, &lzero, "zero.%d");
struct qbe_value bnonzero = mklabel(ctx, &lnonzero, "nonzero.%d");
struct qbe_value boom = mklabel(ctx, &loom, "oom.%d");
struct qbe_value bvalid = mklabel(ctx, &lvalid, "valid.%d");
struct qbe_value bend = mklabel(ctx, &lend, "end.%d");
const struct type *sltype = type_dealias(NULL, expr->alloc.allocation_result);
assert(sltype->storage == STORAGE_SLICE);
struct qbe_value sltype_id = constw(sltype->id);
pushi(ctx->current, &qnewdata, Q_COPY, &qnull, NULL);
// don't allocate if cap == 0
pushi(ctx->current, &qisnull, Q_CEQL, &qcap, &qnull, NULL);
pushi(ctx->current, NULL, Q_JNZ, &qisnull, &bzero, &bnonzero, NULL);
push(&ctx->current->body, &lnonzero);
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, &qnewdata, Q_CALL, &ctx->rt.malloc, &sz, NULL);
pushi(ctx->current, &qisnull, Q_CEQL, &qnewdata, &qnull, NULL);
pushi(ctx->current, NULL, Q_JNZ, &qisnull, &boom, &bvalid, NULL);
push(&ctx->current->body, &lvalid);
if (inittype->storage == STORAGE_ARRAY) {
gen_expr_at(ctx, expr->alloc.init, newdata);
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, &qnewdata, 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);
length = qcap;
}
} else {
struct qbe_value bytelen = mkqtmp(ctx, ctx->arch.sz, ".%d");
pushi(ctx->current, &bytelen, Q_MUL, &length, &membsz, NULL);
pushi(ctx->current, NULL, Q_CALL, &ctx->rt.memcpy,
&qnewdata, &initdata, &bytelen, NULL);
}
// cap == 0 case
push(&ctx->current->body, &lzero);
struct gen_value gslice_out = mkgtemp(ctx, sltype, ".%d");
struct qbe_value qslice_out = mklval(ctx, &gslice_out);
struct qbe_value qslice_offset = compute_tagged_memb_offset(sltype);
pushi(ctx->current, &qslice_out, Q_ADD, &qout, &qslice_offset, NULL);
struct gen_slice sl = gen_slice_ptrs(ctx, gslice_out);
store_slice_data(ctx, &sl, &qnewdata, &length, &qcap);
gen_store_tag(ctx, &qout, expr->result, &sltype_id);
pushi(ctx->current, NULL, Q_JMP, &bend, NULL);
// out of memory (OOM) case
push(&ctx->current->body, &loom);
struct qbe_value nomem_id = constw(builtin_type_nomem.id);
gen_store_tag(ctx, &qout, expr->result, &nomem_id);
push(&ctx->current->body, &lend);
return out;
}
static struct gen_value
gen_expr_alloc_with(struct gen_context *ctx,
const struct expression *expr, struct gen_value *out)
{
struct gen_value ret;
struct qbe_value qout;
if (!out) {
ret = mkgtemp(ctx, expr->result, "object.%d");
out = &ret;
qout = mkqval(ctx, out);
struct qbe_value sz = constl(expr->result->size);
enum qbe_instr alloc = alloc_for_align(expr->result->align);
pushprei(ctx->current, &qout, alloc, &sz, NULL);
} else {
qout = mklval(ctx, out);
}
if (expr->alloc.kind != ALLOC_OBJECT) {
return gen_expr_alloc_slice_array_at(ctx, expr, *out);
}
// alloc(init) case
struct qbe_statement loom, lvalid, lend;
struct qbe_value boom = mklabel(ctx, &loom, "oom.%d");
struct qbe_value bvalid = mklabel(ctx, &lvalid, "valid.%d");
struct qbe_value bend = mklabel(ctx, &lend, "end.%d");
assert(expr->alloc.cap == NULL);
const struct type *objtype =
expr->alloc.allocation_result->pointer.referent;
struct qbe_value sz = constl(objtype->size);
struct gen_value result = mkgtemp(ctx, objtype, ".%d");
struct qbe_value qresult = mklval(ctx, &result);
pushi(ctx->current, &qresult, Q_CALL, &ctx->rt.malloc, &sz, NULL);
struct qbe_value qnull = constl(0);
struct qbe_value qisnull = mkqtmp(ctx, &qbe_word, ".%d");
pushi(ctx->current, &qisnull, Q_CEQL, &qresult, &qnull, NULL);
pushi(ctx->current, NULL, Q_JNZ, &qisnull, &boom, &bvalid, NULL);
push(&ctx->current->body, &lvalid);
struct qbe_value qoffs = compute_tagged_memb_offset(expr->alloc.allocation_result);
struct qbe_value qptr = mkqtmp(ctx, ctx->arch.ptr, ".%d");
struct qbe_value qtag = constw(expr->alloc.allocation_result->id);
pushi(ctx->current, &qptr, Q_ADD, &qout, &qoffs, NULL);
// XXX: should use gen_store()
enum qbe_instr store = store_for_type(ctx, &builtin_type_uintptr);
pushi(ctx->current, NULL, store, &qresult, &qptr, NULL);
gen_store_tag(ctx, &qout, expr->result, &qtag);
gen_expr_at(ctx, expr->alloc.init, result);
pushi(ctx->current, NULL, Q_JMP, &bend, NULL);
push(&ctx->current->body, &loom);
qtag = constw(builtin_type_nomem.id);
gen_store_tag(ctx, &qout, expr->result, &qtag);
pushi(ctx->current, NULL, Q_JMP, &bend, NULL);
push(&ctx->current->body, &lend);
return *out;
}
static void
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);
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);
}
}
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 void
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, rvalue->result, ".%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);
}
static void
gen_expr_assign_slice(struct gen_context *ctx, const struct expression *expr)
{
assert(expr->assign.object->type == EXPR_SLICE);
struct gen_value obj = gen_autoderef_expr(ctx, expr->assign.object->slice.object);
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) {
gen_expr_assign_slice_expandable(ctx, optr, ostart,
olen, expr->assign.value);
return;
}
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);
}
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 void
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) {
gen_expr_assign_slice(ctx, expr);
return;
}
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 (value->result->storage == STORAGE_NEVER || value->result->size == 0) {
gen_expr(ctx, value);
} else if (expr->assign.op == BIN_LEQUAL) {
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][1]) {
qrval = extend(ctx, qrval, rvalue.type);
}
pushi(ctx->current, &qlval, instr, &ilval, &qrval, NULL);
gen_store(ctx, obj, lvalue);
}
}
static struct gen_value
gen_expr_binarithm_gv(struct gen_context *ctx, const struct type *result,
const struct type *ltype, const struct type *rtype, int op,
struct gen_value lvalue, struct gen_value rvalue)
{
struct qbe_value qlval = mkqval(ctx, &lvalue);
struct qbe_value qrval = mkqval(ctx, &rvalue);
if (bin_extend[op][0]) {
qlval = extend(ctx, qlval, ltype);
}
if (bin_extend[op][1]) {
qrval = extend(ctx, qrval, rtype);
}
struct gen_value gresult = mkgtemp(ctx, result, ".%d");
struct qbe_value qresult = mkqval(ctx, &gresult);
ltype = type_dealias(NULL, ltype);
rtype = type_dealias(NULL, 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 (op == BIN_NEQUAL) {
struct qbe_value one = constl(1);
pushi(ctx->current, &qresult, Q_XOR, &qresult, &one, NULL);
} else {
assert(op == BIN_LEQUAL);
}
return gresult;
}
enum qbe_instr instr = binarithm_for_op(ctx, op, ltype);
pushi(ctx->current, &qresult, instr, &qlval, &qrval, NULL);
return gresult;
}
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 (rtype->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);
return gen_expr_binarithm_gv(ctx, expr->result, ltype, rtype,
expr->binarithm.op, lvalue, rvalue);
}
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 void
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);
}
}
static void
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) {
gen_expr_branch(ctx, expr->control.value, scope->result, scope->out);
if (expr->control.value->result->storage == STORAGE_NEVER) {
return;
}
}
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
}
}
static struct gen_value
gen_expr_call(struct gen_context *ctx, const struct expression *expr)
{
struct gen_value lvalue = gen_autoderef_expr(ctx, expr->call.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->storage != STORAGE_NEVER) {
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->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 (!carg) {
break;
}
struct gen_value arg = gen_expr(ctx, carg->value);
if (param) {
param = param->next;
}
if (carg->value->result->size == 0) {
continue;
}
args = *next = xcalloc(1, sizeof(struct qbe_arguments));
if (carg->value->result->storage == STORAGE_NEVER) {
return gv_void;
}
args->value = mkqval(ctx, &arg);
args->value.type = qtype_lookup(ctx, carg->value->result, true);
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, const struct type *subtype);
static void
gen_type_assertion_at(struct gen_context *ctx, const struct expression *expr,
struct gen_value base)
{
gen_expr_at(ctx, expr->cast.value, base);
assert(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;
bpassed = mklabel(ctx, &passedl, "passed.%d");
bfailed = mklabel(ctx, &failedl, "failed.%d");
if (tagged_select_subtype(NULL, expr->cast.value->result, want, true)) {
gen_nested_match_tests(ctx, base, bpassed,
bfailed, tag, expr->cast.value->result, want);
} else if (tagged_subset_compat(NULL, expr->cast.value->result, want)) {
gen_subset_match_tests(ctx, bpassed, bfailed, tag,
type_dealias(NULL, want));
} else {
abort();
}
push(&ctx->current->body, &failedl);
gen_fixed_abort(ctx, expr->loc, ABORT_TYPE_ASSERTION);
push(&ctx->current->body, &passedl);
}
static struct gen_value
gen_type_test(struct gen_context *ctx, const struct expression *expr)
{
struct gen_value base = gen_expr(ctx, expr->cast.value);
assert(expr->cast.kind == C_TEST);
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 dummy;
struct qbe_value bdummy = mklabel(ctx, &dummy, "failed.%d");
struct gen_value result;
if (tagged_select_subtype(NULL, expr->cast.value->result, want, true)) {
result = gen_nested_match_tests(ctx, base, bdummy,
bdummy, tag, expr->cast.value->result, want);
} else if (tagged_subset_compat(NULL, expr->cast.value->result, want)) {
result = gen_subset_match_tests(ctx, bdummy, bdummy, tag,
type_dealias(NULL, want));
} else {
abort();
}
push(&ctx->current->body, &dummy);
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
if (expr->cast.kind == C_ASSERTION) {
gen_type_assertion_at(ctx, expr, out);
} else {
gen_expr_at(ctx, expr->cast.value, out);
}
} 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_test(ctx, expr);
}
}
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: tagged => non-tagged
if (type_dealias(NULL, from)->storage == STORAGE_TAGGED) {
struct gen_value gbase;
if (expr->cast.kind == C_ASSERTION) {
gbase = mkgtemp(ctx, expr->cast.value->result, ".%d");
struct qbe_value qbase = mkqval(ctx, &gbase);
enum qbe_instr alloc =
alloc_for_align(expr->cast.value->result->align);
struct qbe_value size =
constl(expr->cast.value->result->size);
pushprei(ctx->current, &qbase, alloc, &size, NULL);
gen_type_assertion_at(ctx, expr, gbase);
} else {
gbase = gen_expr(ctx, expr->cast.value);
}
struct qbe_value base = mkcopy(ctx, &gbase, ".%d");
if (type_dealias(NULL, to)->size == 0) {
return gv_void;
}
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: cast to type with size zero
if (type_dealias(NULL, to)->size == 0) {
gen_expr(ctx, expr->cast.value); // Side-effects
return gv_void;
}
// 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_DONE:
case STORAGE_ERROR:
case STORAGE_FCONST:
case STORAGE_FUNCTION:
case STORAGE_ICONST:
case STORAGE_NEVER:
case STORAGE_NOMEM:
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:
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);
}
gen_expr_branch(ctx, exprs->expr, 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)
{
struct array_literal *aexpr = expr->literal.slice.array;
struct qbe_value obj;
if (expr->literal.object == NULL && aexpr != NULL) {
// slicing a literal array
struct expression *first = aexpr->value;
obj = mkqtmp(ctx, ctx->arch.ptr, "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, &obj, &offs, NULL);
gen_expr_at(ctx, ac->value, item);
++n;
}
struct qbe_value asz = constl(n * first->result->size);
enum qbe_instr alloc = alloc_for_align(first->result->align);
pushprei(ctx->current, &obj, alloc, &asz, NULL);
struct qbe_value offset =
constl(expr->literal.slice.start * first->result->size);
pushi(ctx->current, &obj, Q_ADD, &obj, &offset, NULL);
} else if (expr->literal.object == NULL) {
// slicing an empty array
obj = constl(0);
} else {
// slicing an access expression
const struct scope_object *sobj = expr->literal.object;
struct gen_value gobj = gen_access_ident(ctx, sobj);
struct qbe_value tmp = mkqval(ctx, &gobj);
const struct type *otype =
sobj->otype == O_CONST ? sobj->value->result : sobj->type;
size_t offs = expr->literal.slice.offset;
offs += expr->literal.slice.start * otype->size;
struct qbe_value qoffs = constl(offs);
obj = mkqtmp(ctx, ctx->arch.ptr, "object.%d");
pushi(ctx->current, &obj, Q_ADD, &tmp, &qoffs, NULL);
}
struct gen_slice sl = gen_slice_ptrs(ctx, out);
struct qbe_value len = constl(expr->literal.slice.len);
struct qbe_value cap = constl(expr->literal.slice.cap);
store_slice_data(ctx, &sl, &obj, &len, &cap);
}
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 *s = xcalloc(1, sizeof(struct qbe_def));
s->kind = Q_DATA;
s->data.align = ALIGN_UNDEFINED;
s->exported = false;
s->name = gen_name(&ctx->id, "strliteral.%d");
s->file = expr->loc.file;
gen_data_item(ctx, expr, &s->data.items);
qbe_append_def(ctx->out, s);
return (struct gen_value){
.kind = GV_GLOBAL,
.type = expr->result,
.name = xstrdup(s->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 void
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;
}
static void
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_autoderef_expr(ctx, dexpr->slice.object);
} else {
assert(dexpr->type == EXPR_ACCESS
&& dexpr->access.type == ACCESS_INDEX);
object = gen_autoderef_expr(ctx, dexpr->access.array);
struct gen_value start = gen_expr(ctx, dexpr->access.index);
qstart = mkqval(ctx, &start);
}
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);
}
}
static void
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_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, var_type, ".%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 = var_type,
.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_store(ctx, gcur_object, gen_load(ctx, 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);
}
static void
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;
}
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);
}
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);
gen_expr_branch(ctx, expr->_if.true_branch, gvout, out);
if (expr->_if.true_branch->result->storage != STORAGE_NEVER) {
pushi(ctx->current, NULL, Q_JMP, &bend, NULL);
}
push(&ctx->current->body, &lfalse);
gen_expr_branch(ctx, expr->_if.false_branch, gvout, out);
push(&ctx->current->body, &lend);
return gvout;
}
static struct gen_value
gen_expr_append_insert_with(struct gen_context *ctx,
const struct expression *expr,
struct gen_value *out)
{
struct qbe_statement lvalid, loom, lend;
struct qbe_value bvalid = mklabel(ctx, &lvalid, "valid.%d");
struct qbe_value bend = mklabel(ctx, &lend, "end.%d");
struct qbe_value boom = mklabel(ctx, &loom, "oom.%d");
struct gen_value gvout = gv_void;
if (!out) {
gvout = mkgtemp(ctx, expr->result, ".%d");
}
struct qbe_value qout = mkqval(ctx, out ? out : &gvout);
if (!out) {
enum qbe_instr alloc = alloc_for_align(expr->result->align);
struct qbe_value sz = constl(expr->result->size);
pushprei(ctx->current, &qout, alloc, &sz, NULL);
}
struct gen_value slice;
if (expr->type == EXPR_APPEND) {
slice = gen_autoderef_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_autoderef_expr(ctx, objexpr->access.array);
}
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;
if (expr->type == EXPR_APPEND) {
qindex = prevlen;
} else {
struct gen_value index = gen_expr(ctx,
expr->append.object->access.index);
qindex = mkqval(ctx, &index);
gen_fixed_assert(ctx, expr->loc, ABORT_OOB, Q_CULEL, &qindex, &prevlen);
}
struct qbe_value qvoid_id = constw(builtin_type_void.id);
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 gvout;
}
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 gvout;
}
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);
const struct type *mtype = type_dealias(NULL, slice.type)->array.members;
struct qbe_value membsz = constl(mtype->size);
struct qbe_value cmpres = mkqtmp(ctx, &qbe_word, ".%d");
if (expr->append.is_static) {
pushi(ctx->current, &cmpres, Q_CULEL, &newlen, &cap, NULL);
} else {
struct qbe_value lval = mklval(ctx, &slice);
pushi(ctx->current, &cmpres, Q_CALL, &ctx->rt.ensure, &lval, &membsz, NULL);
}
pushi(ctx->current, NULL, Q_JNZ, &cmpres, &bvalid, &boom, NULL);
push(&ctx->current->body, &lvalid);
struct gen_value item = mkgtemp(ctx, mtype, ".%d");
struct qbe_value ptr = mklval(ctx, &item);
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, &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, 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);
}
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);
}
gen_store_tag(ctx, &qout, expr->result, &qvoid_id);
pushi(ctx->current, NULL, Q_JMP, &bend, NULL);
push(&ctx->current->body, &loom);
struct qbe_value qnomem_id = constw(builtin_type_nomem.id);
gen_store_tag(ctx, &qout, expr->result, &qnomem_id);
push(&ctx->current->body, &lend);
return gvout;
}
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, const struct type *subtype)
{
// 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 *test = subtype;
do {
struct qbe_statement lsubtype;
struct qbe_value bsubtype = mklabel(ctx, &lsubtype, "subtype.%d");
if (type_dealias(NULL, type)->storage != STORAGE_TAGGED) {
break;
}
test = tagged_select_subtype(NULL, type, subtype, 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 != subtype->id
&& type_dealias(NULL, test)->id != subtype->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;
}
type = test;
} while (test->id != subtype->id && type_dealias(NULL, test)->id != subtype->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_expr_match_with(struct gen_context *ctx,
const struct expression *expr,
struct gen_value *out)
{
struct gen_value object = gen_expr(ctx, expr->match.value);
struct qbe_value qobject = mkqval(ctx, &object);
const struct type *objtype = type_dealias(NULL,
expr->match.value->result);
bool is_tagged = objtype->storage == STORAGE_TAGGED;
bool is_nullable_ptr = false, is_tagged_ptr = false;
// If objtype is a pointer, ref_type is the dealiased referent type.
const struct type *ref_type = objtype;
if (objtype->storage == STORAGE_POINTER) {
is_nullable_ptr = objtype->pointer.nullable;
ref_type = type_dealias(NULL, objtype->pointer.referent);
is_tagged_ptr = ref_type->storage == STORAGE_TAGGED;
}
assert(is_tagged || is_nullable_ptr || is_tagged_ptr);
// Pass over the list once to find the default case and null case, if
// they exist. For matching on nullable pointers to tagged unions, we
// need to check for null before we dereference the pointer to check the
// tag.
const struct match_case *default_case = NULL;
const struct match_case *null_case = NULL;
for (const struct match_case *c = expr->match.cases; c; c = c->next) {
if (!c->type) {
default_case = c;
} else if (c->type->storage == STORAGE_NULL) {
null_case = c;
}
}
if (is_nullable_ptr) {
assert(default_case || null_case);
}
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 qbe_statement ldefault;
struct qbe_value bdefault = mklabel(ctx, &ldefault, "default.%d");
struct qbe_value zero = constl(0);
if (is_nullable_ptr) {
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");
// If there is no null case, go to the default case on null.
struct qbe_value *where = null_case ? &bmatch : &bdefault;
pushi(ctx->current, &cmpres, Q_CEQL, &qobject, &zero, NULL);
pushi(ctx->current, NULL, Q_JNZ, &cmpres, where, &bnext, NULL);
push(&ctx->current->body, &lmatch);
if (null_case) {
gen_expr_branch(ctx, null_case->value, gvout, out);
if (null_case->value->result->storage != STORAGE_NEVER) {
pushi(ctx->current, NULL, Q_JMP, &bout, NULL);
}
}
push(&ctx->current->body, &lnext);
}
struct qbe_value tag = mkqtmp(ctx, &qbe_word, "tag.%d");
if (is_tagged || is_tagged_ptr) {
gen_load_tag(ctx, &tag, &qobject, ref_type);
}
for (const struct match_case *c = expr->match.cases; c; c = c->next) {
if (c == null_case || c == 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");
if (is_nullable_ptr && !is_tagged_ptr) {
struct qbe_value cmpres = mkqtmp(ctx, &qbe_word, ".%d");
pushi(ctx->current, &cmpres, Q_CNEL, &qobject, &zero,
NULL);
pushi(ctx->current, NULL, Q_JNZ, &cmpres, &bmatch,
&bnext, NULL);
push(&ctx->current->body, &lmatch);
if (c->object) {
struct gen_binding *gb = xcalloc(1,
sizeof(struct gen_binding));
gb->value = mkgtemp(ctx, c->type, "binding.%d");
gb->object = c->object;
gb->next = ctx->bindings;
ctx->bindings = gb;
enum qbe_instr store = store_for_type(ctx,
c->type);
enum qbe_instr alloc = alloc_for_align(
c->type->align);
struct qbe_value qv = mkqval(ctx, &gb->value);
struct qbe_value sz = constl(c->type->size);
pushprei(ctx->current, &qv, alloc, &sz, NULL);
pushi(ctx->current, NULL, store, &qobject, &qv,
NULL);
}
} else {
const struct type *case_tag_type = c->type;
if (is_tagged_ptr && c->type->size > 0) {
case_tag_type = c->type->pointer.referent;
};
const struct type *subtype = tagged_select_subtype(NULL,
ref_type, case_tag_type, false);
enum match_compat compat = COMPAT_SUBTYPE;
if (subtype) {
gen_nested_match_tests(ctx, object,
bmatch, bnext, tag, ref_type, case_tag_type);
} else {
assert(type_dealias(NULL, case_tag_type)
->storage == STORAGE_TAGGED);
assert(tagged_subset_compat(NULL, ref_type,
case_tag_type));
compat = COMPAT_SUBSET;
const struct type *casetype = type_dealias(NULL,
case_tag_type);
gen_subset_match_tests(ctx, bmatch, bnext, tag,
casetype);
}
push(&ctx->current->body, &lmatch);
if (c->object && c->type->size > 0) {
struct gen_binding *gb = xcalloc(1,
sizeof(struct gen_binding));
gb->value = mkgtemp(ctx, c->type, "binding.%d");
gb->object = c->object;
gb->next = ctx->bindings;
ctx->bindings = gb;
struct qbe_value qv = mklval(ctx, &gb->value);
enum qbe_instr alloc = alloc_for_align(
c->type->align);
struct qbe_value sz = constl(c->type->size);
pushprei(ctx->current, &qv, alloc, &sz, NULL);
struct gen_value src = mkgtemp(ctx, c->type, ".%d");
struct qbe_value ptr = mklval(ctx, &src);
struct gen_value load;
struct qbe_value offset;
switch (compat) {
case COMPAT_SUBTYPE:
offset = nested_tagged_offset(ref_type,
case_tag_type);
pushi(ctx->current, &ptr, Q_ADD,
&qobject, &offset, NULL);
break;
case COMPAT_SUBSET:
pushi(ctx->current, &ptr, Q_COPY,
&qobject, NULL);
break;
}
if (is_tagged) {
load = gen_load(ctx, src);
gen_store(ctx, gb->value, load);
} else {
gen_store(ctx, gb->value, src);
}
}
}
gen_expr_branch(ctx, c->value, gvout, out);
if (c->value->result->storage != STORAGE_NEVER) {
pushi(ctx->current, NULL, Q_JMP, &bout, NULL);
}
push(&ctx->current->body, &lnext);
}
if (default_case) {
push(&ctx->current->body, &ldefault);
gen_expr_branch(ctx, default_case->value, gvout, out);
if (default_case->value->result->storage != STORAGE_NEVER) {
pushi(ctx->current, NULL, Q_JMP, &bout, NULL);
}
} else {
// We can remove this abort once we have exhaustivity checking.
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_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, false);
assert(type != NULL);
type = type_dealias(NULL, type);
assert(type->storage == STORAGE_SLICE || type->storage == STORAGE_STRING);
struct gen_value gv = gen_autoderef_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 void
gen_expr_return(struct gen_context *ctx, const struct expression *expr)
{
if (expr->_return.value->result->storage == STORAGE_NEVER) {
gen_expr(ctx, expr->_return.value);
return;
}
struct gen_value ret = gen_expr(ctx, expr->_return.value);
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);
}
}
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");
if (expr->result->storage != STORAGE_NEVER && expr->result->size != 0) {
// XXX: hack, to make gvout appear initialized to QBE
struct qbe_value qout = mkqval(ctx, &gvout);
struct qbe_value zero = constl(0);
pushi(ctx->current, &qout, Q_COPY, &zero, NULL);
}
}
struct qbe_statement lout;
struct qbe_value bout = mklabel(ctx, &lout, ".%d");
struct gen_value value = gen_expr(ctx, expr->_switch.value);
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 gen_value match = gen_expr_binarithm_gv(ctx,
&builtin_type_bool, expr->_switch.value->result,
opt->value->result, BIN_LEQUAL, value, test);
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);
gen_expr_branch(ctx, _case->value, gvout, out);
if (_case->value->result->storage != STORAGE_NEVER) {
pushi(ctx->current, NULL, Q_JMP, &bout, NULL);
}
push(&ctx->current->body, &lnextcase);
}
if (_default) {
gen_expr_branch(ctx, _default->value, 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_autoderef_expr(ctx, expr->slice.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);
if (srctype->array.members->size != SIZE_UNDEFINED) {
struct qbe_value data = mkqtmp(ctx, ctx->arch.sz, ".%d");
struct qbe_value isz = constl(srctype->array.members->size);
pushi(ctx->current, &data, Q_MUL, &qstart, &isz, NULL);
pushi(ctx->current, &qbase, Q_ADD, &qbase, &data, NULL);
}
struct gen_slice sl = gen_slice_ptrs(ctx, out);
store_slice_data(ctx, &sl, &qbase, &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
assert(expr->result->size != SIZE_UNDEFINED);
if (expr->result->size == 0) {
return gv_void;
}
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 = gv_void;
switch ((int)expr->type) {
case EXPR_ACCESS:
out = gen_expr_access(ctx, expr);
break;
case EXPR_APPEND:
case EXPR_INSERT:
out = gen_expr_append_insert_with(ctx, expr, NULL);
break;
case EXPR_ALLOC:
out = gen_expr_alloc_with(ctx, expr, NULL);
break;
case EXPR_ASSERT:
gen_expr_assert(ctx, expr);
break;
case EXPR_ASSIGN:
gen_expr_assign(ctx, expr);
break;
case EXPR_BINARITHM:
out = gen_expr_binarithm(ctx, expr);
break;
case EXPR_BINDING:
gen_expr_binding(ctx, expr);
break;
case EXPR_BREAK:
case EXPR_CONTINUE:
case EXPR_YIELD:
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:
gen_expr_defer(ctx, expr);
break;
case EXPR_DELETE:
gen_expr_delete(ctx, expr);
break;
case EXPR_FOR:
gen_expr_for(ctx, expr);
break;
case EXPR_FREE:
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:
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;
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);
break;
case EXPR_DEFINE:
case EXPR_VAEND:
break;
}
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);
return gv_void;
}
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_APPEND:
case EXPR_INSERT:
out = gen_expr_append_insert_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
}
if (expr->result->storage == STORAGE_NEVER) {
gen_expr(ctx, expr);
return;
}
gen_store(ctx, out, gen_expr(ctx, expr));
}
static void
gen_expr_branch(struct gen_context *ctx,
const struct expression *expr,
struct gen_value merged,
struct gen_value *out)
{
// Branching expressions written in the _with style may need to
// consolidate each branch's result into a single temporary to return to
// the caller. This function facilitates that.
if (expr->result->storage == STORAGE_NEVER || expr->result->size == 0) {
gen_expr(ctx, expr);
} else if (out) {
gen_expr_at(ctx, expr, *out);
} else {
assert(expr->result == merged.type);
struct gen_value result = gen_expr(ctx, expr);
struct qbe_value qresult = mkqval(ctx, &result);
struct qbe_value qmerged = mkqval(ctx, &merged);
pushi(ctx->current, &qmerged, Q_COPY, &qresult, NULL);
}
}
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 ? decl->symbol : ident_to_sym(ctx->itbl, 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->storage != STORAGE_NEVER) {
qdef->func.returns = qtype_lookup(
ctx, fntype->func.result, true);
} 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_HLT, 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);
char *s = xcalloc(n + 1, 1);
snprintf(s, n + 1, ".init.%s", qdef->name);
init->name = intern_owned(ctx->itbl, s);
struct qbe_data_item dataitem = {
.type = QD_VALUE,
.value = {
.kind = QV_GLOBAL,
.type = ctx->arch.ptr,
.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);
char *s = xcalloc(n + 1, 1);
snprintf(s, n + 1, ".fini.%s", qdef->name);
fini->name = intern_owned(ctx->itbl, s);
struct qbe_data_item dataitem = {
.type = QD_VALUE,
.value = {
.kind = QV_GLOBAL,
.type = ctx->arch.ptr,
.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);
char *s = xcalloc(n + 1, 1);
snprintf(s, n + 1, ".test.%s", qdef->name);
test->name = intern_owned(ctx->itbl, s);
char *ident = ident_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 = ctx->arch.ptr;
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(ctx->itbl, 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 = type_is_signed(NULL, type) ? &qbe_sbyte : &qbe_ubyte;
break;
case STORAGE_BOOL:
item->type = QD_VALUE;
item->value = constw(literal->bval ? 1 : 0);
item->value.type = &qbe_ubyte;
break;
case STORAGE_I16:
case STORAGE_U16:
item->type = QD_VALUE;
item->value = constw((uint16_t)literal->uval);
item->value.type = type_is_signed(NULL, type) ? &qbe_shalf : &qbe_uhalf;
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 = ctx->arch.ptr;
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_DONE:
case STORAGE_NOMEM:
case STORAGE_VOID:
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 ? decl->symbol : ident_to_sym(ctx->itbl, 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 intern_table *itbl)
{
struct gen_context ctx = {
.out = out,
.ns = unit->ns,
.itbl = itbl,
.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;
}
}
07070100014E0A000081A40000000000000000000000016856649B00000ABB000000000000002F00000000000000000000003200000000harec-0.25.2+git.1750492315.966012b/src/genutil.c#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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),
};
}
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);
}
07070100014E09000081A40000000000000000000000016856649B00000D5F000000000000002F00000000000000000000003500000000harec-0.25.2+git.1750492315.966012b/src/identifier.c#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "identifier.h"
#include "util.h"
uint32_t
ident_hash(uint32_t init, const struct ident *ident)
{
init = fnv1a_s(init, ident->name);
init = fnv1a(init, 0);
if (ident->ns != NULL) {
init = ident_hash(init, ident->ns);
}
return init;
}
static void
ident_unparse_ex(const struct ident *id, const char *delim,
size_t delimlen, char **buf, size_t *len, size_t *cap)
{
const struct ident *ident = id;
if (ident->ns != NULL) {
ident_unparse_ex(ident->ns, delim,
delimlen, buf, len, cap);
append_buffer(buf, len, cap, delim, delimlen);
}
size_t namelen = strlen(ident->name);
append_buffer(buf, len, cap, ident->name, namelen);
}
char *
ident_unparse(const struct ident *ident)
{
size_t len = 0;
size_t cap = strlen(ident->name) + 1;
char *buf = xcalloc(cap, sizeof(char));
ident_unparse_ex(ident, "::", 2, &buf, &len, &cap);
return buf;
}
int
ident_unparse_static(const struct ident *ident, char *buf)
{
if (ident->ns != NULL) {
int prefix = ident_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;
}
const char *
ident_to_sym(struct intern_table *itbl, const struct ident *ident)
{
size_t len = 0;
size_t cap = strlen(ident->name) + 1;
char *buf = xcalloc(cap, sizeof(char));
ident_unparse_ex(ident, ".", 1, &buf, &len, &cap);
return intern_owned(itbl, buf);
}
#define SZ 0x2000
static_assert((SZ & (SZ - 1)) == 0, "SZ must be a power of 2");
static const char *
intern_str(struct intern_table *itbl, char *s, bool owned)
{
uint32_t h = fnv1a_s(FNV1A_INIT, s);
struct bucket *b = &itbl->sbuckets[h & (SZ - 1)];
for (size_t i = 0; i < b->sz; i++) {
if (!strcmp(b->ids[i], s)) {
if (owned) {
free(s);
}
return b->ids[i];
}
}
if (!owned) {
s = xstrdup(s);
}
if (b->sz == b->cap) {
if (b->cap == 0) {
b->cap = 4;
}
b->cap *= 2;
b->ids = xrealloc(b->ids, b->cap * sizeof b->ids[0]);
}
return b->ids[b->sz++] = s;
}
const char *
intern_owned(struct intern_table *itbl, char *s)
{
return intern_str(itbl, s, true);
}
const char *
intern_copy(struct intern_table *itbl, const char *s)
{
return intern_str(itbl, (char *)s, false);
}
void
intern_init(struct intern_table *itbl)
{
itbl->sbuckets = xcalloc(SZ, sizeof (struct bucket));
itbl->ibuckets = xcalloc(SZ, sizeof (struct bucket));
}
struct ident *
intern_ident(struct intern_table *itbl, const char *name, struct ident *ns)
{
struct ident ident = { .name = name, .ns = ns };
uint32_t h = ident_hash(FNV1A_INIT, &ident);
struct bucket *b = &itbl->ibuckets[h & (SZ - 1)];
for (size_t i = 0; i < b->sz; i++) {
const struct ident *id1 = b->ids[i], *id2 = &ident;
for (; id1 && id2 && id1 != id2; id1 = id1->ns, id2 = id2->ns) {
if (id1->name != id2->name) {
break;
}
}
if (id1 == id2) {
return b->ids[i];
}
}
if (b->sz == b->cap) {
if (b->cap == 0) {
b->cap = 4;
}
b->cap *= 2;
b->ids = xrealloc(b->ids, b->cap * sizeof b->ids[0]);
}
struct ident *new = xcalloc(1, sizeof (struct ident));
*new = ident;
return b->ids[b->sz++] = new;
}
struct ident *
intern_name(struct intern_table *itbl, const char *name)
{
return intern_ident(itbl, name, NULL);
}
07070100014DFE000081A40000000000000000000000016856649B000068AD000000000000002F00000000000000000000002E00000000harec-0.25.2+git.1750492315.966012b/src/lex.c#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#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_NOMEM] = "nomem",
[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");
FORMAT(2, 3) 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, struct intern_table *itbl)
{
lexer->in = f;
lexer->buflen = 0;
lexer->bufsz = 256;
lexer->buf = xcalloc(lexer->bufsz, 1);
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;
lexer->require_int = false;
lexer->in_annotation = false;
lexer->itbl = itbl;
}
void
lex_finish(struct lexer *lexer)
{
fclose(lexer->in);
free(lexer->buf);
}
static void
update_loc(struct location *loc, uint32_t c)
{
if (c == '\n') {
loc->lineno++;
loc->colno = 0;
} else if (c == '\t') {
// set column to next multiple of 8
loc->colno += 8;
loc->colno &= ~7;
} else {
// XXX: this doesn't correctly handle all unicode
// codepoints/glyphs
loc->colno++;
}
}
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_loc(&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_loc(&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, &lexer->buflen, &lexer->bufsz, 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;
}
static void
consume(struct lexer *lexer, size_t n)
{
for (size_t i = 0; i < n; i++) {
while ((lexer->buf[--lexer->buflen] & 0xC0) == 0x80) ;
}
assert(lexer->buflen < lexer->bufsz); // underflow check
}
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;
}
}
lexer->buf[lexer->buflen] = '\0'; // for cmp_keyword
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 = intern_copy(lexer->itbl, 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<<FLT, 0},
['E'] = {DEC, DEC | 1<<FLT, 0},
['p'] = {HEX, HEX | 1<<FLT, 0},
['P'] = {HEX, HEX | 1<<FLT, 0},
['+'] = {DEC | 1<<EXP | 1<<DIG, DEC | 1<<FLT | 1<<EXP | 1<<DIG, 0},
['-'] = {DEC | 1<<EXP | 1<<DIG, DEC | 1<<FLT | 1<<EXP | 1<<DIG, 0},
['i'] = {BIN, OCT, HEX, DEC, DEC | 1<<EXP, 0},
['u'] = {BIN, OCT, HEX, DEC, DEC | 1<<EXP, 0},
['z'] = {BIN, OCT, HEX, DEC, DEC | 1<<EXP, 0},
['f'] = {DEC, DEC | 1<<FLT, DEC | 1<<EXP, DEC | 1<<FLT | 1<<EXP, 0},
['_'] = {BIN, OCT, HEX, DEC, DEC | 1<<FLT, HEX | 1<<FLT, 0},
};
int state = DEC, base = 10, oldstate = DEC;
uint32_t c = next(lexer, &out->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_LITERAL;
lexer->require_int = false;
lexer->buf[lexer->buflen] = '\0';
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) == 0) {
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);
// need isxdigit check to disallow sign
if (*endptr != '\0' || !isxdigit(buf[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);
// need isxdigit check to disallow sign
if (*endptr != '\0' || !isxdigit(buf[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);
// need isxdigit check to disallow sign
if (*endptr != '\0' || !isxdigit(buf[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, &lexer->buflen,
&lexer->bufsz, buf, sz);
} else {
next(lexer, NULL, true);
}
}
out->token = T_LITERAL;
out->storage = STORAGE_STRING;
lexer->buf[lexer->buflen] = '\0';
out->string.len = lexer->buflen;
out->string.value = lexer->buf;
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_LITERAL;
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;
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;
}
static void
lex_annotation(struct lexer *lexer)
{
// Scan and discard annotations
if (lexer->in_annotation) {
error(lexer->loc, "cannot nest annotations");
return;
}
lexer->in_annotation = true;
struct token tok;
if (next(lexer, NULL, false) != '[') {
error(lexer->loc, "invalid annotation (expected '[')");
return;
}
enum lexical_token ltok;
// identifier
ltok = lex(lexer, &tok);
if (ltok != T_NAME) {
error(lexer->loc, "invalid annotation (expected identifier)");
}
while (true) {
ltok = lex(lexer, &tok);
if (ltok != T_DOUBLE_COLON) {
unlex(lexer, &tok);
break;
}
ltok = lex(lexer, &tok);
if (ltok != T_NAME) {
error(lexer->loc, "invalid annotation (expected identifier)");
}
}
switch (lex(lexer, &tok))
{
case T_LPAREN:
break;
case T_RBRACKET:
goto exit;
default:
error(lexer->loc, "invalid annotation (expected '(' or ']')");
}
// balanced list of tokens
int sp = 0;
enum lexical_token stack[32] = {T_LPAREN, 0};
do {
if (sp + 1 >= 32) {
error(lexer->loc, "annotation is too nested");
}
enum lexical_token want = 0;
ltok = lex(lexer, &tok);
switch (ltok) {
case T_LPAREN:
case T_LBRACE:
case T_LBRACKET:
stack[++sp] = ltok;
break;
case T_RPAREN:
want = T_LPAREN;
break;
case T_RBRACE:
want = T_LBRACE;
break;
case T_RBRACKET:
want = T_LBRACKET;
break;
default:
break;
}
if (want != 0) {
ltok = stack[sp--];
if (ltok != want) {
error(lexer->loc, "unbalanced tokens in annotation");
}
}
} while (sp >= 0);
if (lex(lexer, &tok) != T_RBRACKET) {
error(lexer->loc, "invalid annotation (expected ']')");
}
exit:
lexer->in_annotation = false;
}
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 = 0;
do {
c = wgetc(lexer, &out->loc);
if (c == C_EOF) {
out->token = T_EOF;
return out->token;
} else if (c == '#') {
lex_annotation(lexer);
} else if (c == '/') {
c = next(lexer, NULL, false);
if (c != '/') {
push(lexer, c, false);
lexer->require_int = false;
return lex2(lexer, out, '/');
}
// comment
while ((c = next(lexer, NULL, false)) != C_EOF && c != '\n') ;
} else {
break;
}
} while (true);
if (c <= 0x7F && isdigit(c)) {
push(lexer, c, false);
lex_number(lexer, out);
return T_LITERAL;
}
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 '=': // = == =>
return lex2(lexer, out, c);
case '/': // / /=
// already handled above, because of special case for comment
assert(0);
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;
}
const char *
lexical_token_str(enum lexical_token tok)
{
switch (tok) {
case T_NAME:
return "name";
case T_LITERAL:
return "literal";
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[128];
assert(tok->token == T_LITERAL && tok->storage == STORAGE_STRING);
buf[0] = '"';
int bytes = 1;
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)) {
// subtract 1 from sizeof(buf) to leave room for closing quote
// at the end
bytes += snprintf(&buf[bytes], sizeof(buf) - 1 - bytes, "%s",
rune_unparse(c));
if (bytes >= (int)sizeof(buf) - 1) {
return "string literal";
}
}
snprintf(&buf[bytes], sizeof(buf) - bytes, "\"");
return buf;
}
const char *
token_str(const struct token *tok)
{
static char buf[128];
switch (tok->token) {
case T_NAME:;
int n = snprintf(buf, sizeof(buf), "name %s", tok->name);
return n < (int)sizeof(buf) ? buf : "name";
case T_LITERAL:
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:
snprintf(buf, sizeof(buf), "'%s'",
rune_unparse(tok->rune));
break;
case STORAGE_STRING:
return string_unparse(tok);
case STORAGE_ALIAS:
case STORAGE_ARRAY:
case STORAGE_BOOL:
case STORAGE_DONE:
case STORAGE_ENUM:
case STORAGE_ERROR:
case STORAGE_FUNCTION:
case STORAGE_NEVER:
case STORAGE_NOMEM:
case STORAGE_NULL:
case STORAGE_OPAQUE:
case STORAGE_POINTER:
case STORAGE_RUNE:
case STORAGE_SLICE:
case STORAGE_STRUCT:
case STORAGE_TAGGED:
case STORAGE_TUPLE:
case STORAGE_UNION:
case STORAGE_VALIST:
case STORAGE_VOID:
assert(0);
}
return buf;
default:
return lexical_token_str(tok->token);
}
}
void
unlex(struct lexer *lexer, const struct token *in)
{
assert(lexer->un.token == T_NONE);
lexer->un = *in;
}
07070100014E08000081A40000000000000000000000016856649B000014C4000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/src/main.c#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#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 intern_table *itbl)
{
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, itbl);
def->ident = parse_identifier(&lexer, NULL, NULL);
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;
bool is_test = false;
struct unit unit = {0};
struct lexer lexer;
struct ast_global_decl *defines = NULL, **next_def = &defines;
struct intern_table itbl;
intern_init(&itbl);
const char *mainsym = intern_copy(&itbl, "main");
struct ident *mainident = intern_name(&itbl, mainsym);
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, &itbl);
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':
if (strlen(optarg) != 0) {
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, &itbl);
unit.ns = parse_identifier(&lexer, NULL, NULL);
lex_finish(&lexer);
} else {
const char *s = intern_copy(&itbl, "");
unit.ns = intern_name(&itbl, s);
}
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;
}
}
mainsym = intern_copy(&itbl, mainsym);
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] = "<unknown>";
full_sources = xcalloc(nsources + 2, sizeof(char **));
memcpy((char **)full_sources, sources, sizeof(char **) * (nsources + 1));
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] = "<stdin>";
} 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, &itbl);
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, mainident, defines, &aunit, &unit, &itbl);
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, &itbl);
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;
}
07070100014E07000081A40000000000000000000000016856649B00000754000000000000002F00000000000000000000002E00000000harec-0.25.2+git.1750492315.966012b/src/mod.c#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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,
struct ident *ident)
{
uint32_t hash = ident_hash(FNV1A_INIT, ident);
struct modcache **bucket = &ctx->modcache[hash % MODCACHE_BUCKETS];
for (; *bucket; bucket = &(*bucket)->next) {
if ((*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_";
ident_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, ctx->itbl);
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, ctx->mainident, defines, &aunit,
&u, ctx->itbl, true);
sources[0] = old;
bucket = &ctx->modcache[hash % MODCACHE_BUCKETS];
struct modcache *item = xcalloc(1, sizeof(struct modcache));
item->ident = ident;
item->scope = scope;
item->next = *bucket;
*bucket = item;
return scope;
}
07070100014DFD000081A40000000000000000000000016856649B0000E4B3000000000000002F00000000000000000000003000000000harec-0.25.2+git.1750492315.966012b/src/parse.c#include <assert.h>
#include <ctype.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#include "ast.h"
#include "identifier.h"
#include "lex.h"
#include "parse.h"
#include "types.h"
#include "utf8.h"
#include "util.h"
FORMAT(2, 3) 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_LITERAL || 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);
}
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);
struct ident *
parse_identifier(struct lexer *lexer, const char *name, bool *trailing)
{
struct token tok;
struct ident *i = &(struct ident){ .name = name, .ns = NULL };
size_t len = 0;
do {
switch (lex(lexer, &tok)) {
case T_NAME:
if (i->name) {
error(tok.loc, "unexpected %s", token_str(&tok));
}
len += strlen(tok.name);
i->name = tok.name;
break;
case T_DOUBLE_COLON:
synassert(i->name, &tok, T_NAME, T_EOF);
len++;
i->ns = intern_ident(lexer->itbl, i->name, i->ns);
i->name = NULL;
break;
default:
unlex(lexer, &tok);
break;
}
if (len > IDENT_MAX) {
error(tok.loc, "identifier exceeds maximum length");
}
} while (tok.token == T_NAME || tok.token == T_DOUBLE_COLON);
if (trailing) {
*trailing = !i->name;
if (!i->name) {
return (struct ident *)i->ns;
}
}
synassert(i->name, &tok, T_NAME, T_EOF);
return intern_ident(lexer->itbl, i->name, i->ns);
}
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 = intern_name(lexer->itbl, 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;
import->ident = parse_identifier(lexer, NULL, &trailing_colon);
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;
import->ident = parse_identifier(lexer, NULL, NULL);
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:;
switch (lex(lexer, &tok2)) {
case T_COLON:
(*next)->name = intern_name(lexer->itbl, tok.name);
(*next)->type = parse_type(lexer);
break;
default:
unlex(lexer, &tok2);
(*next)->type = mktype(tok.loc);
(*next)->type->storage = STORAGE_ALIAS;
(*next)->type->alias = parse_identifier(lexer,
tok.name, NULL);
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_NOMEM:
type->storage = STORAGE_NOMEM;
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 ident *ident, struct lexer *lexer)
{
struct token tok = {0};
struct ast_type *type = mktype(lexer->loc);
type->storage = STORAGE_ENUM;
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 = intern_name(lexer->itbl, 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);
}
switch (lex(lexer, &tok)) {
case T_NAME:;
const char *name = tok.name;
struct location loc = tok.loc;
switch (lex(lexer, &tok)) {
case T_COLON:
next->name = name;
next->type = parse_type(lexer);
break;
default:
unlex(lexer, &tok);
next->type = mktype(loc);
next->type->storage = STORAGE_ALIAS;
next->type->unwrap = false;
next->type->alias = parse_identifier(lexer, name, NULL);
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;
if (lex(lexer, &tok) != T_CONST) {
unlex(lexer, &tok);
}
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_DONE:
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_NOMEM:
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:
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);
type->pointer.nullable = 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;
type->alias = parse_identifier(lexer, NULL, NULL);
break;
default:
synassert_msg(false, "expected type", &tok);
break;
}
type->flags |= flags;
return type;
}
static struct ast_expression *
parse_access(struct lexer *lexer, struct ident *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_NOMEM:
exp->literal.storage = STORAGE_NOMEM;
return exp;
case T_LITERAL:
exp->literal.storage = tok.storage;
break;
default:
synerr(&tok, T_LITERAL, T_TRUE, T_FALSE, T_NULL,
T_VOID, T_DONE, T_NOMEM, 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:;
size_t ln = 0, cap = 0;
char *s = NULL;
do {
append_buffer(&s, &ln, &cap, tok.string.value, tok.string.len);
} while (lex(lexer, &tok) == T_LITERAL && tok.storage == STORAGE_STRING);
unlex(lexer, &tok);
exp->literal.string.value = s;
exp->literal.string.len = ln;
// check for invalid UTF-8 (possible when \x is used)
while (s - exp->literal.string.value < (ptrdiff_t)ln) {
if (utf8_decode((const char **)&s) == UTF8_INVALID) {
error(loc, "invalid UTF-8 in string literal");
}
}
break;
case STORAGE_BOOL:
case STORAGE_DONE:
case STORAGE_NOMEM:
case STORAGE_NULL:
case STORAGE_VOID:
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 ident *ident);
static struct ast_field_value *
parse_field_value(struct lexer *lexer)
{
struct ast_field_value *exp =
xcalloc(1, sizeof(struct ast_field_value));
struct token tok = {0};
switch (lex(lexer, &tok)) {
case T_NAME:;
const char *name = tok.name;
switch (lex(lexer, &tok)) {
case T_COLON:
exp->type = parse_type(lexer);
want(lexer, T_EQUAL, NULL);
/* fallthrough */
case T_EQUAL:
exp->name = name;
exp->initializer = parse_expression(lexer);
break;
default:
unlex(lexer, &tok);
struct ident *id = parse_identifier(lexer, name, NULL);
exp->initializer = parse_struct_literal(lexer, id);
break;
}
break;
case T_STRUCT:
exp->initializer = parse_struct_literal(lexer, NULL);
break;
default:
assert(0);
}
return exp;
}
static struct ast_expression *
parse_struct_literal(struct lexer *lexer, struct ident *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 != 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_DONE:
case T_FALSE:
case T_LITERAL:
case T_NOMEM:
case T_NULL:
case T_TRUE:
case T_VOID:
unlex(lexer, &tok);
return parse_literal(lexer);
case T_NAME:
unlex(lexer, &tok);
struct ident *id = parse_identifier(lexer, NULL, NULL);
switch (lex(lexer, &tok)) {
case T_LBRACE:
unlex(lexer, &tok);
return parse_struct_literal(lexer, id);
default:
unlex(lexer, &tok);
return parse_access(lexer, id);
}
assert(0);
case T_LBRACKET:
unlex(lexer, &tok);
return parse_array_literal(lexer);
case T_STRUCT:
return parse_struct_literal(lexer, NULL);
// 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
default:
synerr(&tok, T_LITERAL, 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 && expr->append.object->access.type != ACCESS_INDEX) {
error(expr->append.object->loc,
"syntax error: expected indexing expression");
}
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_postfix_expression(struct lexer *lexer, struct ast_expression *lvalue);
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);
struct ast_expression *lval = parse_slice_mutation(lexer, true);
// XXX: this is a hack around #954
return parse_postfix_expression(lexer, lval);
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_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_COMMA, NULL);
expr->vaarg.type = parse_type(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_plain_expression(lexer);
}
static struct ast_expression *
parse_postfix_expression(struct lexer *lexer, struct ast_expression *lvalue)
{
if (lvalue == NULL) {
lvalue = parse_builtin_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_LITERAL:
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_LITERAL, 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 (ident, indexing, or field access)",
&tok);
return exp;
}
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_postfix_expression(lexer, NULL);
}
}
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};
switch (lex(lexer, &tok)) {
case T_CONST:
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) {
switch (lex(lexer, &tok)) {
case T_NAME:
binding->name = intern_name(lexer->itbl, 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);
} 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);
struct location caseloc = tok.loc;
_case->options = parse_case_options(lexer);
switch (lex(lexer, &tok)) {
case T_CASE:
case T_RBRACE:
error(caseloc, "syntax error: case cannot be empty");
default:
unlex(lexer, &tok);
break;
}
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 location caseloc = tok.loc;
_case->name = NULL;
struct ast_type *type = NULL;
switch (lex(lexer, &tok)) {
case T_LET:
want(lexer, T_NAME, &tok);
_case->name = intern_name(lexer->itbl, 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);
switch (lex(lexer, &tok)) {
case T_CASE:
case T_RBRACE:
error(caseloc, "syntax error: case cannot be empty");
default:
unlex(lexer, &tok);
break;
}
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;
while (true) {
struct ast_binding_unpack *new = xcalloc(1, sizeof *new);
*next = new;
next = &new->next;
switch (lex(lexer, &tok)) {
case T_NAME:
new->name = intern_name(lexer->itbl, tok.name);
break;
case T_UNDERSCORE:
break;
default:
synerr(&tok, T_NAME, T_UNDERSCORE, T_EOF);
}
switch (lex(lexer, &tok)) {
case T_COMMA:
break;
case T_RPAREN:
return;
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);
struct token tok = {0};
switch (lex(lexer, &tok)) {
case T_DEF:
exp->type = EXPR_DEFINE;
break;
case T_CONST:
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;
do {
switch (lex(lexer, &tok)) {
case T_NAME:
binding->name = intern_name(lexer->itbl, tok.name);
break;
case T_LPAREN:
synassert(exp->type == EXPR_BINDING, &tok, T_NAME, T_EOF);
parse_binding_unpack(lexer, &binding->unpack);
break;
default:
synerr(&tok, T_NAME, T_LPAREN, T_EOF);
}
binding->is_static = is_static;
switch (lex(lexer, &tok)) {
case T_COLON:
binding->type = parse_type(lexer);
want(lexer, T_EQUAL, &tok);
break;
case T_EQUAL:
break;
default:
synerr(&tok, T_COLON, T_EQUAL, T_EOF);
}
binding->initializer = parse_expression(lexer);
if (lex(lexer, &tok) == T_COMMA) {
binding->next = xcalloc(1, sizeof *binding->next);
binding = binding->next;
}
} while (tok.token == T_COMMA);
unlex(lexer, &tok);
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;
}
struct location loc = tok.loc;
switch (lex(lexer, &tok)) {
case T_RBRACE:
error(loc, "syntax error: cannot have empty block");
default:
unlex(lexer, &tok);
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 const char *
parse_attr_symbol(struct lexer *lexer)
{
want(lexer, T_LPAREN, NULL);
struct ast_expression *exp = parse_literal(lexer);
want(lexer, T_RPAREN, NULL);
if (exp->type != EXPR_LITERAL || exp->literal.storage != STORAGE_STRING
|| exp->literal.string.len == 0) {
error(exp->loc, "expected nonempty string literal");
}
char *s = exp->literal.string.value;
if ((uint32_t)s[0] > 0x7F || isdigit(s[0]) || s[0] == '$') {
error(exp->loc, "invalid symbol %s", s);
}
for (size_t i = 0; i < exp->literal.string.len; i++) {
uint32_t c = s[i];
if (c > 0x7F || !(isalnum(c) || c == '_' || c == '$' || c == '.')) {
error(exp->loc, "invalid symbol %s", s);
}
}
free(exp);
return intern_owned(lexer->itbl, s);
}
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;
}
}
i->ident = parse_identifier(lexer, NULL, NULL);
switch (lex(lexer, &tok)) {
case T_COLON:
i->type = parse_type(lexer);
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) {
i->ident = parse_identifier(lexer, NULL, NULL);
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);
decl->ident = parse_identifier(lexer, NULL, NULL);
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);
}
07070100014E06000081A40000000000000000000000016856649B00001467000000000000002F00000000000000000000002E00000000harec-0.25.2+git.1750492315.966012b/src/qbe.c#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qbe.h"
#include "util.h"
// Simple type singletons
const struct qbe_type
qbe_sbyte = {
.stype = Q_BYTE,
.sgn = true,
.size = 1,
},
qbe_ubyte = {
.stype = Q_BYTE,
.sgn = false,
.size = 1,
},
qbe_shalf = {
.stype = Q_HALF,
.sgn = true,
.size = 2,
},
qbe_uhalf = {
.stype = Q_HALF,
.sgn = false,
.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,
};
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 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 = xcalloc(1, sizeof(struct qbe_value));
*stmt->out = *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,
};
}
07070100014E05000081A40000000000000000000000016856649B00001479000000000000002F00000000000000000000003100000000harec-0.25.2+git.1750492315.966012b/src/qinstr.c#include <assert.h>
#include <stdlib.h>
#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_DONE:
case STORAGE_ERROR:
case STORAGE_FCONST:
case STORAGE_FUNCTION:
case STORAGE_ICONST:
case STORAGE_NEVER:
case STORAGE_NOMEM:
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:
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_NOMEM:
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)
{
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
}
07070100014E04000081A40000000000000000000000016856649B00002342000000000000002F00000000000000000000003000000000harec-0.25.2+git.1750492315.966012b/src/qtype.c#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#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 = xstrdup(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 = xstrdup(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;
// We'll need an extra field for 0-aligned values
if (align < maxalign || minalign == 0) {
field->next = xcalloc(1, sizeof(struct qbe_field));
field = field->next;
}
}
// Which is initialized here
if (minalign == 0) {
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 = xstrdup(def->name);
struct qbe_field *field = &def->type.fields;
if (type->size == SIZE_UNDEFINED
|| type->size == 0
|| type->size % type->align != 0) {
/* Not straightforwardly compatible with the C ABI */
field->type = NULL;
field->count = type->size;
qbe_append_def(ctx->out, def);
return &def->type;
}
switch (type->storage) {
case STORAGE_ARRAY:
if (type->array.length == SIZE_UNDEFINED) {
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:
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_DONE:
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_FUNCTION:
case STORAGE_OPAQUE:
case STORAGE_NEVER:
case STORAGE_NOMEM:
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_I8:
return xtype ? &qbe_sbyte : &qbe_word;
case STORAGE_U8:
case STORAGE_BOOL:
return xtype ? &qbe_ubyte : &qbe_word;
case STORAGE_I16:
return xtype ? &qbe_shalf : &qbe_word;
case STORAGE_U16:
return xtype ? &qbe_uhalf : &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_NOMEM:
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_DONE:
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_NOMEM:
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:
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
}
07070100014E03000081A40000000000000000000000016856649B00000A20000000000000002F00000000000000000000003000000000harec-0.25.2+git.1750492315.966012b/src/scope.c#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "expr.h"
#include "identifier.h"
#include "scope.h"
#include "util.h"
static uint32_t
name_hash(uint32_t init, const struct ident *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;
}
}
struct scope_object *
scope_insert(struct scope *scope, enum object_type otype,
struct ident *ident, struct ident *name, const struct type *type,
struct expression *value)
{
assert(otype == O_SCAN || !type != !value);
struct scope_object *obj = xcalloc(1, sizeof(struct scope_object));
obj->ident = ident;
obj->name = name;
obj->otype = otype;
if (type) {
obj->type = type;
} else if (value) {
obj->value = value;
assert(otype == O_CONST);
assert(value->type == EXPR_LITERAL);
}
flexible_refer(type, &obj->type);
// Linked list
*scope->next = obj;
scope->next = &obj->lnext;
// Hash map
uint32_t hash = name_hash(FNV1A_INIT, obj->name);
struct scope_object **bucket = &scope->buckets[hash % SCOPE_BUCKETS];
if (*bucket) {
obj->mnext = *bucket;
}
*bucket = obj;
return obj;
}
struct scope_object *
scope_lookup(struct scope *scope, struct ident *ident)
{
uint32_t hash = name_hash(FNV1A_INIT, ident);
struct scope_object *bucket = scope->buckets[hash % SCOPE_BUCKETS];
while (bucket) {
if (bucket->name == ident) {
return bucket;
}
bucket = bucket->mnext;
}
if (scope->parent) {
return scope_lookup(scope->parent, ident);
}
return NULL;
}
07070100014DFC000081A40000000000000000000000016856649B000094D6000000000000002F00000000000000000000003500000000harec-0.25.2+git.1750492315.966012b/src/type_store.c#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "check.h"
#include "eval.h"
#include "identifier.h"
#include "scope.h"
#include "type_store.h"
#include "types.h"
#include "util.h"
static struct dimensions
dim_from_type(const struct type *type)
{
return (struct dimensions){ .size = type->size, .align = type->align };
}
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)
{
switch (storage) {
case STORAGE_BOOL:
return &builtin_type_bool;
case STORAGE_ERROR:
return &builtin_type_error;
case STORAGE_F32:
return &builtin_type_f32;
case STORAGE_F64:
return &builtin_type_f64;
case STORAGE_I8:
return &builtin_type_i8;
case STORAGE_I16:
return &builtin_type_i16;
case STORAGE_I32:
return &builtin_type_i32;
case STORAGE_I64:
return &builtin_type_i64;
case STORAGE_INT:
return &builtin_type_int;
case STORAGE_NEVER:
return &builtin_type_never;
case STORAGE_NOMEM:
return &builtin_type_nomem;
case STORAGE_OPAQUE:
return &builtin_type_opaque;
case STORAGE_RUNE:
return &builtin_type_rune;
case STORAGE_SIZE:
return &builtin_type_size;
case STORAGE_U8:
return &builtin_type_u8;
case STORAGE_U16:
return &builtin_type_u16;
case STORAGE_U32:
return &builtin_type_u32;
case STORAGE_U64:
return &builtin_type_u64;
case STORAGE_UINT:
return &builtin_type_uint;
case STORAGE_UINTPTR:
return &builtin_type_uintptr;
case STORAGE_VALIST:
return &builtin_type_valist;
case STORAGE_VOID:
return &builtin_type_void;
case STORAGE_DONE:
return &builtin_type_done;
case STORAGE_NULL:
return &builtin_type_null;
case STORAGE_STRING:
return &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;
}
return builtin_type_for_storage(type->storage);
}
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 = afield->name;
}
struct dimensions dim = {0};
if (size_only) {
dim = type_store_lookup_dimensions(ctx, afield->type);
} else {
field->type = type_store_lookup_atype(ctx, afield->type);
dim = dim_from_type(field->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);
static 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;
}
static 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 = 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 = ident_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 = ident_unparse(obj->ident);
error(ctx, _atype->loc, NULL,
"Object '%s' is not a type", ident);
free(ident);
*type = &builtin_type_error;
return;
}
}
if (obj->idecl->type != IDECL_DECL
|| obj->idecl->decl.decl_type != ADECL_TYPE) {
char *ident = ident_unparse(obj->ident);
error(ctx, _atype->loc, NULL,
"Object '%s' is not a type", ident);
free(ident);
*type = &builtin_type_error;
return;
}
_atype = obj->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 = type_store_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 = type_store_lookup_dimensions(ctx, 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};
if (type) {
cur->type = type_store_lookup_atype(ctx, atuple->type);
memb = dim_from_type(cur->type);
} else {
memb = type_store_lookup_dimensions(ctx, 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};
}
size_t offset = dim.size;
if (memb.align != 0) {
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) {
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 (in->result->storage == STORAGE_ERROR) {
return false;
}
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_DONE:
case STORAGE_NEVER:
if (atype->flags & TYPE_ERROR) {
error(ctx, atype->loc, NULL,
"Error flag can't be used on %s type",
type_storage_unparse(atype->storage));
*type = builtin_type_error;
return (struct dimensions){0};
}
// fallthrough
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_NOMEM:
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:
builtin = builtin_type_for_storage(type->storage);
type->size = builtin->size;
type->align = builtin->align;
break;
case STORAGE_ALIAS:
obj = scope_lookup(ctx->scope, atype->alias);
if (!obj) {
char *ident = ident_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
if (size_only && obj->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 = ident_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;
}
if ((atype->flags & TYPE_ERROR) && type_is_done(ctx, obj->type)) {
error(ctx, atype->loc, NULL,
"Error flag can't be used on done type");
*type = builtin_type_error;
return (struct dimensions){0};
}
type->alias.ident = obj->ident;
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 = type_store_lookup_dimensions(ctx,
atype->array.members);
} else {
type->array.members = type_store_lookup_atype(ctx,
atype->array.members);
memb = dim_from_type(type->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 = type_store_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 = type_store_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.nullable = atype->pointer.nullable;
type->pointer.referent = type_store_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 = type_store_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};
}
if (type->storage == STORAGE_UNION || !type->struct_union.packed) {
add_padding(&type->size, type->align);
}
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);
}
add_padding(&type->size, type->align);
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);
}
add_padding(&type->size, type->align);
break;
}
return dim_from_type(type);
}
static const struct type *
type_store_lookup_type(struct context *ctx, const struct type *type)
{
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;
return &bucket->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;
}
struct type temp = {0};
type_init_from_atype(ctx, &temp, atype);
if (temp.storage == STORAGE_ALIAS) {
// References to type aliases always inherit the flags that the
// alias was defined with
const struct scope_object *obj = scope_lookup(
ctx->scope, temp.alias.name);
temp.flags |= obj->type->flags;
}
return type_store_lookup_type(ctx, &temp);
}
// 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);
}
const struct type *
type_store_lookup_pointer(struct context *ctx, struct location loc,
const struct type *referent, bool nullable)
{
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,
.nullable = nullable,
},
.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, struct ident *ident,
struct ident *name, const struct type *secondary, int flags,
bool exported)
{
struct type type = {
.storage = STORAGE_ALIAS,
.flags = flags,
.alias.type = secondary,
.alias.ident = ident,
.alias.name = name,
.alias.exported = exported,
};
return type_store_lookup_type(ctx, &type);
}
// Sorts members by id and deduplicates entries. Does not enforce usual tagged
// union invariants. The returned type is not a singleton.
static struct type *
lookup_tagged(struct context *ctx, struct type_tagged_union *tags)
{
struct type *ret = xcalloc(1, sizeof(struct type));
ret->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(ret, tu, nmemb);
return ret;
}
const struct type *
type_store_lookup_tagged(struct context *ctx, struct location loc,
struct type_tagged_union *tags)
{
struct type *type = lookup_tagged(ctx, tags);
if (!enforce_tagged_invariants(ctx, loc, type)) {
return &builtin_type_error;
}
add_padding(&type->size, type->align);
return type_store_lookup_type(ctx, type);
}
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;
}
t->offset = type.size;
if (t->type->align != 0) {
if (type.size % t->type->align != 0) {
t->offset += t->type->align - type.size % t->type->align;
}
type.size = t->offset + t->type->size;
}
}
type.tuple = *values;
add_padding(&type.size, type.align);
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;
type.alias.ident = mkident(ctx, atype->alias, NULL);
type.alias.name = atype->alias;
type.alias.exported = exported;
type.alias.type = builtin_type_for_storage(atype->_enum.storage);
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->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.nullable || jt->pointer.nullable) {
it = type_store_lookup_pointer(ctx, loc,
it->pointer.referent, true);
jt = type_store_lookup_pointer(ctx, loc,
jt->pointer.referent, true);
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, true);
}
if (in == NULL) {
return &builtin_type_never;
} else if (in->next == NULL) {
return in->type;
}
return type_store_lookup_tagged(ctx, loc, in);
}
07070100014DFB000081A40000000000000000000000016856649B00002E7C000000000000002F00000000000000000000003200000000harec-0.25.2+git.1750492315.966012b/src/typedef.c#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#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(t->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(t->storage));
break;
case STORAGE_POINTER:
xfprintf(out, "%" PRIu64 ": u64: uintptr: ", val->uval);
emit_type(expr->result, out);
break;
case STORAGE_NULL:
xfprintf(out, "null: ");
emit_type(expr->result, out);
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(t->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 = ident_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 = ident_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_NOMEM:
xfprintf(out, "nomem");
break;
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_ERROR) {
xfprintf(out, "!");
}
char *ident;
switch (type->storage) {
case STORAGE_BOOL:
case STORAGE_DONE:
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_NOMEM:
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:
xfprintf(out, "%s", type_storage_unparse(type->storage));
break;
case STORAGE_POINTER:
xfprintf(out, "%s*", type->pointer.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 = ident_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 = ident_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, "[flexible integer: min=%" PRIi64 " max=%" PRIi64 "]", type->flexible.min,
type->flexible.max);
break;
}
}
static void
emit_decl_const(const struct declaration *decl, FILE *out)
{
char *ident = ident_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 = ident_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 = ident_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);
emit_type(decl->global.type, out);
xfprintf(out, ";\n");
free(ident);
}
static void
emit_decl_type(const struct declaration *decl, FILE *out)
{
char *ident = ident_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 = ident_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;
}
}
}
07070100014E02000081A40000000000000000000000016856649B00007D98000000000000002F00000000000000000000003000000000harec-0.25.2+git.1750492315.966012b/src/types.c#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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, bool allow_nullable)
{
switch (type->storage) {
case STORAGE_ALIAS:
if (type_dealias(ctx, type)->storage != STORAGE_POINTER) {
return type;
}
return type_dereference(ctx, type_dealias(ctx, type), allow_nullable);
case STORAGE_POINTER:
if (!allow_nullable && type->pointer.nullable) {
return NULL;
}
return type_dereference(ctx, type->pointer.referent, allow_nullable);
default:
return type;
}
}
static const struct scope_object *
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);
assert(obj->idecl->type == IDECL_DECL);
if (!obj->idecl->dealias_in_progress) {
obj->idecl->dealias_in_progress = true;
type->alias.type = type_store_lookup_atype(
ctx, obj->idecl->decl.type.type);
obj->idecl->dealias_in_progress = false;
}
return obj;
}
const struct type *
type_dealias(struct context *ctx, const struct type *_type)
{
struct type *type = (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);
const struct scope_object *obj =
complete_alias(ctx, type);
if (type->alias.type == NULL) {
char *identstr = ident_unparse(obj->name);
error(ctx, obj->idecl->decl.loc, NULL,
"Circular dependency for '%s'",
identstr);
free(identstr);
type->alias.type = &builtin_type_error;
}
}
type = (struct type *)type->alias.type;
}
return type;
}
// checks if a type is `done`, or an alias thereof, without erroring out when a
// "circular dependency" is encountered (since that means the type isn't `done`)
bool
type_is_done(struct context *ctx, const struct type *type)
{
while (type->storage == STORAGE_ALIAS) {
if (type->alias.type == NULL) {
complete_alias(ctx, (struct type *)type);
if (type->alias.type == NULL) {
return false;
}
}
type = type->alias.type;
}
return type->storage == STORAGE_DONE;
}
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 "flexible float";
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 "flexible integer";
case STORAGE_INT:
return "int";
case STORAGE_NEVER:
return "never";
case STORAGE_NOMEM:
return "nomem";
case STORAGE_NULL:
return "null";
case STORAGE_OPAQUE:
return "opaque";
case STORAGE_POINTER:
return "pointer";
case STORAGE_RCONST:
return "flexible rune";
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_NOMEM:
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_NOMEM:
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_NOMEM:
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_NOMEM:
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:
hash = ident_hash(hash, type->alias.ident);
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, (unsigned char) type->pointer.nullable);
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->offset == 0; f = f->next) {
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) {
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.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.nullable) {
return to->pointer.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:
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_DONE:
case STORAGE_ENUM:
case STORAGE_F32:
case STORAGE_FUNCTION:
case STORAGE_NEVER:
case STORAGE_NOMEM:
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) {
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:
if (from->alias.type->storage == STORAGE_RUNE) {
return to->storage == STORAGE_RUNE ? to_orig : NULL;
}
return to->storage == STORAGE_ENUM || type_is_integer(ctx, to)
? 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_NEVER:
case STORAGE_NOMEM:
case STORAGE_OPAQUE:
case STORAGE_FUNCTION:
case STORAGE_TUPLE:
case STORAGE_STRUCT:
case STORAGE_UNION:
case STORAGE_VALIST:
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_f64.align = 8;
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_i64.align = 8;
builtin_type_u64.align = 8;
builtin_type_null.size = 8;
builtin_type_null.align = 8;
builtin_type_size.size = 8;
builtin_type_size.align = 8;
builtin_type_str.size = 24;
builtin_type_str.align = 8;
builtin_type_valist.size = 32;
builtin_type_valist.align = 8;
} else if (strcmp(target, "riscv64") == 0) {
builtin_type_f64.align = 8;
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_i64.align = 8;
builtin_type_u64.align = 8;
builtin_type_null.size = 8;
builtin_type_null.align = 8;
builtin_type_size.size = 8;
builtin_type_size.align = 8;
builtin_type_str.size = 24;
builtin_type_str.align = 8;
builtin_type_valist.size = 8;
builtin_type_valist.align = 8;
} else if (strcmp(target, "x86_64") == 0) {
builtin_type_f64.align = 8;
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_i64.align = 8;
builtin_type_u64.align = 8;
builtin_type_null.size = 8;
builtin_type_null.align = 8;
builtin_type_size.size = 8;
builtin_type_size.align = 8;
builtin_type_str.size = 24;
builtin_type_str.align = 8;
builtin_type_valist.size = 24;
builtin_type_valist.align = 8;
} else {
xfprintf(stderr, "Unsupported or unrecognized target: %s\n", 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_nomem,
&builtin_type_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,
},
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,
},
builtin_type_int = {
.storage = STORAGE_INT,
},
builtin_type_never = {
.storage = STORAGE_NEVER,
.size = SIZE_UNDEFINED,
.align = ALIGN_UNDEFINED,
},
builtin_type_nomem = {
.storage = STORAGE_NOMEM,
.flags = TYPE_ERROR,
.size = 0,
.align = 0,
},
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,
},
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_str = {
.storage = STORAGE_STRING,
},
builtin_type_valist = {
.storage = STORAGE_VALIST,
};
07070100014E01000081A40000000000000000000000016856649B00001015000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/src/utf8.c#include <stdint.h>
#include <stdio.h>
#include "utf8.h"
static const struct {
uint8_t mask;
uint8_t result;
int octets;
} sizes[] = {
{ 0x80, 0x00, 1 },
{ 0xE0, 0xC0, 2 },
{ 0xF0, 0xE0, 3 },
{ 0xF8, 0xF0, 4 },
};
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;
}
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);
}
// UTF-8 decoder:
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#define UTF8_ACCEPT 0
#define UTF8_REJECT 1
static const uint8_t utf8d[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,//00..1f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,//20..3f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,//40..5f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,//60..7f
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,//80..9f
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,//a0..bf
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,//c0..df
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3,//e0..ef
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,//f0..ff
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1,//s0..s0
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1,//s1..s2
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,//s3..s4
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1,//s5..s6
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,//s7..s8
};
uint32_t
utf8_decode(const char **char_str)
{
const uint8_t **s = (const uint8_t **)char_str;
uint32_t state = 0;
uint32_t cp = 0;
// Assumes char_str does not end with a partial code sequence.
while (1) {
uint8_t byte = **s;
(*s)++;
uint32_t type = utf8d[byte];
cp = (state != UTF8_ACCEPT) ?
(byte & 0x3fu) | (cp << 6) :
(0xff >> type) & (byte);
state = utf8d[256 + state * 16 + type];
if (state == UTF8_REJECT) {
return UTF8_INVALID;
} else if (state == UTF8_ACCEPT) {
return cp;
}
}
}
07070100014E00000081A40000000000000000000000016856649B00000D94000000000000002F00000000000000000000002F00000000harec-0.25.2+git.1750492315.966012b/src/util.c#include <sys/stat.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "util.h"
// Remove safety macros:
#undef malloc
#undef calloc
#undef realloc
#undef strdup
const char **sources;
const char **full_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
append_buffer(char **buf, size_t *restrict ln, size_t *restrict cap,
const char *b, size_t sz)
{
if (*ln + sz >= *cap) {
*cap += *ln + sz + 1;
*buf = xrealloc(*buf, *cap);
}
memcpy(*buf + *ln, b, sz);
*ln += sz;
(*buf)[*ln] = '\0';
}
void
errline(struct location loc)
{
const char *path = full_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);
}
07070100014DF7000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002C00000000harec-0.25.2+git.1750492315.966012b/testmod07070100014DF9000081A40000000000000000000000016856649B00000065000000000000002F00000000000000000000003B00000000harec-0.25.2+git.1750492315.966012b/testmod/measurement.hause rt;
// must take measurement of something in subunit scope
def SIZE_RT_SLICE = size(rt::slice);
07070100014DF8000081A40000000000000000000000016856649B00000433000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/testmod/testmod.haexport 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;
export type f64_alias = f64;
export def val5: f64_alias = 0.0;
export type uintptr_enum = enum uintptr {
MAX = ~0u64: uintptr,
};
// ensure rt isn't imported in this subunit
static assert(SIZE_RT_SLICE == size([]opaque));
// ensure this produces valid code in a typedef
export def ptr: *int = null: *int;
07070100014DCF000041ED0000000000000000000000026856649B00000000000000000000002F00000000000000000000002A00000000harec-0.25.2+git.1750492315.966012b/tests07070100014DF6000081A40000000000000000000000016856649B00004782000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/tests/00-literals.hause rt::{compile, 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 flexible type 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', '\\', '\'', '\"', '\U00103456', '\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 = '\U0000007';`)!;
compile(status::LEX, `let r = '\xah';`)!;
compile(status::LEX, `let r = '\uahij';`)!;
compile(status::LEX, `let r = '\Uahijklmn';`)!;
compile(status::LEX, `let r = '\x-0';`)!;
compile(status::LEX, `let r = '\x+a';`)!;
compile(status::LEX, `let r = '\x a';`)!;
compile(status::LEX, `let r = '\u-000';`)!;
compile(status::LEX, `let r = '\u+abc';`)!;
compile(status::LEX, `let r = '\u abc';`)!;
compile(status::LEX, `let r = '\U-abcdeff';`)!;
compile(status::LEX, `let r = '\U +12345';`)!;
// invalid unicode
compile(status::LEX, `let r = '\U00110000';`)!;
compile(status::LEX, `let r = '\ud800';`)!;
};
export fn main() void = {
// The interaction between literals and result type reduction is tested
// in 30-reduction.ha
basics();
numeric();
assignment();
aggregates();
};
07070100014DF5000081A40000000000000000000000016856649B000010DC000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/01-arrays.hause 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();
};
07070100014DF4000081A40000000000000000000000016856649B00000428000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/tests/02-integers.hafn 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();
};
07070100014DF3000081A40000000000000000000000016856649B000013B3000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/tests/03-pointers.hause 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;
};
`)!;
// eval
compile(status::CHECK, `
let a = 0;
let b = 0;
let c = &a == &b;
`)!;
compile(status::CHECK, `
let a = 0;
let b = 0;
let c = &a < &b;
`)!;
compile(status::CHECK, `
let a = 0;
let b = &a: uintptr;
`)!;
};
export fn main() void = {
basics();
_nullable();
casts();
reject();
};
07070100014DF2000081A40000000000000000000000016856649B00000BDB000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/tests/04-strings.hause 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 = "\U0000007";`)!;
compile(status::LEX, `let s = "\xah";`)!;
compile(status::LEX, `let s = "\uahij";`)!;
compile(status::LEX, `let s = "\Uahijklmn";`)!;
compile(status::LEX, `let s = "\x-0";`)!;
compile(status::LEX, `let s = "\x-0";`)!;
compile(status::LEX, `let s = "\x+c";`)!;
compile(status::LEX, `let s = "\u-000";`)!;
compile(status::LEX, `let s = "\u+abc";`)!;
compile(status::LEX, `let s = "\u abc";`)!;
compile(status::LEX, `let s = "\U-abcdeff";`)!;
compile(status::LEX, `let s = "\U +12345";`)!;
};
export fn main() void = {
measurements();
storage();
concat();
equality();
escapes();
raw();
reject();
};
07070100014DF1000081A40000000000000000000000016856649B00000E2A000000000000002F00000000000000000000003F00000000harec-0.25.2+git.1750492315.966012b/tests/05-implicit-casts.hause 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;
// non-void types cannot be assigned to void
compile(status::CHECK, `
fn disallow_1() void = "I am illegeal";
fn disallow_2() void = 12;
`)!;
};
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
};
07070100014DD2000081A40000000000000000000000016856649B00002D33000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/tests/06-structs.hause 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);
// ensure this gets through parse
compile(status::CHECK, "type z = struct { a, x: int };")!;
compile(status::CHECK, "type z = struct { a::b, x: int };")!;
compile(status::CHECK, "let z = s { a { x = y } };")!;
compile(status::CHECK, "let z = s { a::b { x = y } };")!;
};
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
};
07070100014DF0000081A40000000000000000000000016856649B00000E15000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/tests/07-aliases.hatype 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();
};
07070100014DEF000081A40000000000000000000000016856649B000024DA000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/08-slices.hause 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..], []);
let check_failures = [
"fn test() void = { let x = \"test\"; x[1..3]; };",
"fn test() void = { let x = 0; x[1..3]; };",
"fn test() void = { let x = [1, 2, 3]; x[\"hi\"..]; };",
"fn test() void = { let x = [1, 2, 3]; x[false..]; };",
"fn test() void = { let x = [1, 2, 3]; x[..false]; };",
"fn test() void = { let x = [1, 2, 3]; x[..\"hi\"]; };",
"fn test() void = { let x = [1, 2, 3]; x[2..1]; };",
"fn test() void = { let x = [1, 2, 3]; x[..4]; };",
"fn test() void = { let x = [1, 2, 3]; x[..4]; };",
"fn test(a: *[*]u8) void = { a[..]; };",
"fn test(a: *[*]u8) void = { a[2..]; };",
"fn test(opa: *[*]opaque) void = { opa[..]; };",
"type op = opaque; fn test(opa: *[*]op) void = { opa[..]; };",
"fn test(opsl: []opaque) void = { opsl[1..]; };",
"type op = opaque; fn test(sl: []op) void = { sl[1..]; };",
"fn test(opa: *[*]opaque) void = { opa[1..]; };",
"fn test(opa: *[*]opaque) void = { opa[1..1]; };",
"type op = opaque; fn test(opa: *[*]op) void = { opa[1..]; };",
"type op = opaque; fn test(opa: *[*]op) void = { opa[1..1]; };",
];
for (let tc .. check_failures) {
compile(status::CHECK, tc)!;
};
};
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();
};
07070100014DEE000081A40000000000000000000000016856649B00001928000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/tests/09-funcs.hause 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 = {
assert(x == 42);
x = 1337;
};
@init fn init() void = {
void; // Should be allowed to have the same name
};
fn initfini() void = {
assert(x == 1337);
x = 0;
};
@fini fn fini() void = {
assert(x == 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 = vaarg(ap, int);
assert(arg == i + 1);
};
for (let i = 0; i < n; i += 1) {
let arg = vaarg(ap2, int);
vaarg(ap2, void);
vaarg(ap2, [0]int);
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);",
// cannot return a type with undefined size
"fn f() fn() void = g; fn g() void = void;",
"fn f(ap: valist) void = { vaarg(ap, opaque); };",
];
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();
reject();
optional_params();
initfini();
_never();
};
07070100014DED000081A40000000000000000000000016856649B00001BC3000000000000002F00000000000000000000003B00000000harec-0.25.2+git.1750492315.966012b/tests/10-binarithms.hause 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);
};
type stralias = str;
fn strings() void = {
assert("" == "");
assert("\0" == "\0");
assert("" != "a");
assert("a" != "");
assert("a" == "a");
assert("a" != "b");
assert("ab" != "a");
assert("a" != "ab");
assert("aaaa" != "aaab");
assert("aaa\0a" != "aaa\0b");
assert("a": stralias == "a": stralias);
assert("a": stralias == "a");
assert("a" == "a": stralias);
assert("a": stralias != "b": stralias);
static assert("" == "");
static assert("\0" == "\0");
static assert("" != "a");
static assert("a" != "");
static assert("a" == "a");
static assert("a" != "b");
static assert("ab" != "a");
static assert("a" != "ab");
static assert("aaaa" != "aaab");
static assert("aaa\0a" != "aaa\0b");
static assert("a": stralias == "a": stralias);
static assert("a": stralias == "a");
static assert("a" == "a": stralias);
static assert("a": stralias != "b": stralias);
};
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;")!;
compile(status::CHECK, "let x = \"aaa\" < \"bbb\";")!;
compile(status::CHECK, "let x = \"aaa\" > \"bbb\";")!;
compile(status::CHECK, "let x = \"aaa\" <= \"bbb\";")!;
compile(status::CHECK, "let x = \"aaa\" >= \"bbb\";")!;
};
export fn main() void = {
// TODO: other binarithms
andorxor();
sar_shr();
arithmetic();
comparison();
strings();
eval();
reject();
};
07070100014DD1000081A40000000000000000000000016856649B00001941000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/tests/11-globals.hause 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 = 1268499444;
def u64_tag = 3181589295;
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;
const ptr_literal = struct {
x: *u32 = &1234u32,
};
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");
assert(*ptr_literal.x == 1234);
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);
static assert(align(u32) == 4 && align(u64) == 8); // XXX
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);
static assert(align(u32) == 4 && align(u64) == 8); // XXX
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();
};
07070100014DEC000081A40000000000000000000000016856649B00002159000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/tests/12-loops.hause 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 = [1, 2, 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 = 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); };};")!;
};
fn next() ((int, int) | done) = (4, 2);
export fn main() void = {
scope();
conditional();
afterthought();
binding();
_break();
_continue();
label();
alias();
result();
for_each();
};
07070100014DD0000081A40000000000000000000000016856649B00001CD3000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/13-tagged.hause rt::{compile, status};
def size_tag = 4119164483;
def f32_tag = 1568378015;
def int_tag = 1099590421;
def void_tag = 630802827;
def u8_tag = 2543892678;
def u16_tag = 1906196061;
def u32_tag = 1268499444;
def u64_tag = 3181589295;
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;
static assert(align(u32) == 4 && align(u64) == 8); // XXX
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();
};
07070100014DEB000081A40000000000000000000000016856649B000010F7000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/14-switch.hause rt::{compile, status, toutf8};
type t = int;
type e = !int;
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);
// 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 (&x) { case => void; };
")!;
compile(status::CHECK, "
let x = 0;
fn test() void = switch (null) { case => void; };
")!;
compile(status::CHECK, "
fn test() void = switch (0i32) {
case 0i64 => void;
case => void;
};
")!;
compile(status::SUCCESS, `
type alias = str;
fn test() void = switch ("asdf") {
case "fdsa": alias => void;
case => void;
};
`)!;
compile(status::SUCCESS, `
type alias = str;
fn test() void = switch ("asdf": alias) {
case "fdsa" => void;
case => void;
};
`)!;
compile(status::SUCCESS, `
type alias = str;
fn test() void = switch ("asdf": alias) {
case "fdsa": alias => 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, "
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;
};
")!;
};
// make sure this compiles and also passes through QBE successfully
fn f() int = switch (0) {
case => abort();
};
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();
};
07070100014DEA000081A40000000000000000000000016856649B0000120D000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/tests/15-enums.hause 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 runes() void = {
let val = '1': rune_storage;
let val = rune_storage::FOO: rune;
compile(status::CHECK, "type a = enum rune { A = 'x' }; fn f() void = { a::A: str; };")!;
};
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();
runes();
reject();
interdependent();
aliases();
resolution_order();
};
07070100014DE9000081A40000000000000000000000016856649B00000D4F000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/tests/16-defer.hause 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);
};
};
};
fn exit(x: int) never = {
assert(x == 0);
rt::exit(x);
};
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();
};
07070100014DE8000081A40000000000000000000000016856649B00000EF4000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/tests/17-alloc.hause rt::{compile, status, slice};
type my_struct = struct {
x: int,
y: int,
};
type my_struct_ptr = *my_struct;
type nomem_alias = nomem;
type my_int = int;
type my_u32 = u32;
fn allocation() void = {
let x = alloc(1234)!;
assert(*x == 1234);
free(x);
let y: (*int | nomem) = alloc(1234);
if (!(y is nomem)) {
assert(*(y as *int) == 1234);
};
free(y as *int);
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 failure() (void | nomem_alias) = {
let x = alloc([1u8...], 500000000000000); // 500 TB
assert(x is nomem);
let sl = *(&slice {
data = 0xdeaddead: uintptr: *opaque,
length = 500000000000000,
capacity = 500000000000000,
}: *[]u8);
let x = alloc(sl...);
assert(x is nomem);
let x: (*[500000000000000]u8 | nomem) = alloc([1...]);
assert(x is nomem);
let x: (*[500000000000000]u8 | nomem) = alloc([1...])?;
abort(); // unreachable
};
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: *aarray = alloc([0...])!;
free(aa);
let aa: *aarray = alloc([0...]: aarray)!;
free(aa);
let aa: *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_allocation() 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);
};
fn aliases() void = {
compile(status::CHECK,
"fn test() void = { let x: (*u32 | *u64 | nomem) = alloc(123); };"
)!;
};
export fn main() void = {
assert(size(nomem) == 0);
assignment();
assert(failure() is nomem_alias);
allocation();
double_pointer();
double_alloc();
array();
slice_allocation();
slice_copy();
string();
_null();
aliases();
};
07070100014DE7000081A40000000000000000000000016856649B000016C7000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/tests/18-match.hatype 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 match_tagged_simple(x: *(int | f64 | void)) int = {
match (x) {
case void =>
return 0;
case let i: *int =>
*i = *i + 1;
return 1;
case let f: *f64 =>
*f = *f / 2.0;
return 2;
};
};
fn tagged_ptr() void = {
let x: (int | f64 | void) = void;
assert(match_tagged_simple(&x) == 0);
assert(x is void);
x = 4;
assert(match_tagged_simple(&x) == 1);
assert(x as int == 5);
x = 4.0;
assert(match_tagged_simple(&x) == 2);
assert(x as f64 == 2.0);
x = 4;
match (&x) {
case void => abort();
case let t: *(int | f64) =>
assert(*t as int == 4);
let v: (int | f64) = 4.0;
*t = v;
};
assert(x as f64 == 4.0);
let y: nullable *(int | f64 | void) = null;
match (y) {
case null => yield;
case => abort();
};
match (y) {
case *(int | f64 | void) => abort();
case => yield;
};
match (y) {
case *int => abort();
case => yield;
};
x = 4;
y = &x;
match (y) {
case let i: *int => *i = *i + 1;
case => abort();
};
assert(x as int == 5);
};
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);
y = null;
z = match (y) {
case *int =>
abort();
case =>
yield 1337;
};
y = null;
z = match (y) {
case =>
abort();
case null =>
yield 1337;
};
assert(z == 1337);
let y: nullable *baz = &x;
let z: int = match (y) {
case let y: *int =>
yield *y;
case null =>
abort();
};
assert(z == 42);
};
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);
let x: (*int | nullable *int | void) = null;
// reduces to nullable *int
let x = match (x) {
case let x: (*int | nullable *int) =>
yield x;
case void => abort();
};
assert(x == null);
};
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_ptr();
tagged();
_never();
_default();
pointer();
alias();
tagged_result();
implicit_cast();
transitivity();
numeric();
alignment_conversion();
binding();
label();
// TODO: Test exhaustiveness and dupe detection
};
07070100014DE6000081A40000000000000000000000016856649B00000C4E000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/19-append.hause rt::{compile, status};
fn basics() void = {
let x: []int = [];
append(x, 1)!;
append(x, 2)!;
let res: (void | nomem) = append(x, 3)!;
assert(res is void);
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();
};
07070100014DE5000081A40000000000000000000000016856649B0000055E000000000000002F00000000000000000000003300000000harec-0.25.2+git.1750492315.966012b/tests/20-if.hause 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();
};
07070100014DE4000081A40000000000000000000000016856649B00001966000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/21-tuples.hause 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);
};
fn _offset() void = {
const x = (0i32, void, 0i64);
static assert(offset(x.0) == 0);
static assert(offset(x.1) == 4);
static assert(offset(x.2) == align(i64));
};
// 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();
_offset();
regression();
reject();
_never();
};
07070100014DE3000081A40000000000000000000000016856649B0000030D000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/22-delete.hause 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();
};
07070100014DE2000081A40000000000000000000000016856649B0000090E000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/23-errors.hause rt::{compile, status};
type err_int = !int;
type err_recur = !*err_recur;
fn assignability() void = {
// Error and non-error types are interchangable:
let a: !int = 10;
let b: int = a;
assert(a == b);
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
};
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 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));
};
type err_done_tagged_union = !(int | done);
type done_tagged_union = (int | done);
type err_done_tagged_union_alias = !done_tagged_union;
fn disallowed_types() void = {
compile(status::CHECK, `type t = !done;`)!;
compile(status::CHECK, `type t = done; type u = t; type v = !u;`)!;
compile(status::CHECK, `fn f() !never;`)!;
};
export fn main() void = {
assignability();
propagate();
cannotignore();
measurements();
disallowed_types();
};
07070100014DE1000081A40000000000000000000000016856649B00000598000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/tests/24-imports.hause 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();
};
07070100014DE0000081A40000000000000000000000016856649B00000180000000000000002F00000000000000000000003A00000000harec-0.25.2+git.1750492315.966012b/tests/25-promotion.haexport 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);
assert(300u8 < 200u16);
assert(300u8: f64 == 44.0);
static assert(300u8 < 200u16);
static assert(300u8: f64 == 44.0);
};
07070100014DDF000081A40000000000000000000000016856649B0000147C000000000000002F00000000000000000000003B00000000harec-0.25.2+git.1750492315.966012b/tests/26-regression.ha// 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,
},
};
// 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');
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, "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 }; };")!;
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; };")!;
compile(status::CHECK, "fn f(a: (int | uint) = invalid) void = void;")!;
compile(status::CHECK, "def X = void: done;")!;
// tuple indexing lexer thing: even after a comment, 0.0 should be lexed
// as three separate tokens, rather than as a float literal
assert(((1, 2), 3). // don't remove this comment
0.0 == 1);
compile(status::SUCCESS,
"type s = struct { a: [2]int }; def x = s { ... }; def y = x.a[1];")!;
def A: [](int | []str) = [
0, 0,
["a"],
[""],
];
assert((A[2] as []str)[0] == "a");
compile(status::CHECK, "type e = enum { A }; fn f() void = { e::A: bool; };")!;
let c: []u8 = alloc([]: []u8, 20)!;
free(c);
compile(status::CHECK, "let x: nullable *int = &(0: [*]int)[0];")!;
};
07070100014DDE000081A40000000000000000000000016856649B00000654000000000000002F00000000000000000000003300000000harec-0.25.2+git.1750492315.966012b/tests/27-rt.ha// 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();
};
07070100014DDD000081A40000000000000000000000016856649B00000DA8000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/28-insert.hause 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);
};
let res: (void | nomem) = insert(y[0], 42)!;
assert(res is void);
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();
};
07070100014DDC000081A40000000000000000000000016856649B00000585000000000000002F00000000000000000000003900000000harec-0.25.2+git.1750492315.966012b/tests/29-unarithm.hause 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);
static assert(-2u8 == 0xfeu8);
};
fn bnot() void = {
assert(~0 == -1);
assert(~0u8 == 0xffu8);
assert(~0b01010011i == 0b10101100i8);
static assert(~0 == -1);
static assert(~0u8 == 0xffu8);
static assert(~0b01010011i == 0b10101100i8);
};
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();
bnot();
deref();
};
07070100014DDB000081A40000000000000000000000016856649B000009AB000000000000002F00000000000000000000003A00000000harec-0.25.2+git.1750492315.966012b/tests/30-reduction.hause rt::{compile, status, toutf8};
fn assert_fail(expr: str) void = {
let buf: [1024]u8 = [0...];
let buf = buf[..0];
static append(buf, toutf8("export fn main() void = {\n")...)!;
static append(buf, toutf8(expr)...)!;
static append(buf, toutf8(";\n};")...)!;
compile(status::CHECK, *(&buf: *str))!;
};
export fn main() void = {
// format:
// let x = &expr;
// let x: *type = x;
let x = &(if (true) 0i else void);
let x: *(int | void) = x;
let x = &match (0u8: (u8 | u16 | u32 | u64)) {
case u8 =>
yield null: *int;
case u16 =>
yield null: nullable *int;
case u32 =>
yield null;
case u64 =>
yield;
};
let x: *(nullable *int | void) = x;
let x = &switch (0) {
case 42 =>
yield null: *int;
case 69 =>
yield null: nullable *int;
case 1337 =>
yield null;
case =>
yield;
};
let x: *(nullable *int | void) = x;
// if, match, and switch all use the same code for reduction, so we
// don't need to rigorously test all three
let x = &(if (true) null: *int else null);
let x: *nullable *int = x;
let x = &(if (true) null: *int else null: nullable *int);
let x: *nullable *int = x;
let x = &{
if (true) yield;
yield 0;
};
let x: *(int | void) = x;
assert_fail("if (true) null "
"else if (true) null: *int "
"else null: *opaque");
assert_fail("if (true) null else void");
assert_fail("if (true) null: *int else if (true) null");
// However, literals behave differently in if vs switch/match
let x = &(if (true) 0 else if (true) 1 else 2);
let x: *int = x;
let x = &(if (true) 0 else 9223372036854775807);
let x: *(int | i64) = x;
let x = &(if (true) 0 else 0z);
let x: *(int | size) = x;
let x = &(if (true) 0 else void);
let x: *(int | void) = x;
let x = &switch (0) {
case 0 =>
yield 0;
case 1 =>
yield 1;
case =>
yield 2;
};
let x: *int = x;
let x = &switch (0) {
case 0 =>
yield 0;
case =>
yield 9223372036854775807;
};
let x: *(int | i64) = x;
let x = &switch (0) {
case 0 =>
yield 0;
case =>
yield 0z;
};
let x: *(int | size) = x;
let x = &switch (0) {
case 0 =>
yield 0;
case =>
yield;
};
let x: *(int | void) = x;
let x = &switch (0) {
case 0 =>
yield 0;
case 1 =>
yield 1z;
case =>
yield 2u32;
};
let x: *(int | size | u32) = x;
let x = &switch (0) {
case 0 =>
yield 0;
case 1 =>
yield 1i;
case =>
yield 9223372036854775807;
};
let x: *(int | i64) = x;
let x = &{
for (false) yield 42;
};
let x: *(int | void) = x;
};
07070100014DDA000081A40000000000000000000000016856649B000006EB000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/tests/31-postfix.hatype 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();
};
07070100014DD9000081A40000000000000000000000016856649B00000529000000000000002F00000000000000000000003500000000harec-0.25.2+git.1750492315.966012b/tests/32-copy.hause rt;
type 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);
let x: []int = [1, 2, 3, 4];
let y = x;
let px = &x: *rt::slice;
let py = &y: *rt::slice;
assert(px.data == py.data);
assert(px.length == py.length);
assert(px.capacity == py.capacity);
};
07070100014DD8000081A40000000000000000000000016856649B0000032A000000000000002F00000000000000000000003600000000harec-0.25.2+git.1750492315.966012b/tests/33-yield.hause 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();
};
07070100014DD7000081A40000000000000000000000016856649B000047EE000000000000002F00000000000000000000003D00000000harec-0.25.2+git.1750492315.966012b/tests/34-declarations.hause 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(...) 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;",
// @init/@fini/@test with C variadism
"@init fn f(...) void = void;",
"@fini fn f(...) void = void;",
"@test fn f(...) void = 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(5) fn f() void;`,
`@symbol('a') fn f() void;`,
`@symbol("") fn f() void;`,
`@symbol(" ") fn f() void;`,
`@symbol("a\0b") fn f() void;`,
`@symbol("a\nb") fn f() void;`,
"@symbol(``) fn f() void;",
`@symbol("aが") fn f() void;`,
`@symbol("a" "が") fn f() void;`,
`@symbol("が") fn f() void;`,
`@symbol("が" "a") fn f() void;`,
`@symbol("#!@%") fn f() void;`,
"@symbol(`#!@%`) fn f() void;",
`@symbol("") let x: int;`,
`@symbol("#!@%") let x: int;`,
`@symbol("asdf#") let x: int;`,
`@symbol("asdf" "#") let x: int;`,
`@symbol("$asdf") let x: int;`,
`@symbol("2asdf") let x: int;`,
`@symbol("$" "asdf") let x: int;`,
`@symbol("2" "asdf") 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 = -42;
export let @symbol("s_y") s_c: int;
export let @symbol("s" `_` "x") s_e: int;
export let @symbol("x" "2") x2: int;
export let @symbol("x" "$") x_: int;
export let @symbol("x" "2x") x2x: int;
export let @symbol("x" "$x") x_x: int;
export let @symbol("y" "" "2") y2: int;
export let @symbol("y" `` `` "$") y_: int;
export let @symbol(`` "y" "2y") y2y: int;
export let @symbol("" "y" "$y") y_y: int;
export let @symbol("z" "2z" "") z2z: int;
export let @symbol("z" "$z" ``) z_z: 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);
// multipart strings are handled
assert(s_d == -42 && s_e == -42);
};
export fn main() void = {
constants();
local_constants();
sz();
reject();
imported();
hosted_main();
complete_graph();
};
07070100014DD6000081A40000000000000000000000016856649B000008B1000000000000002F00000000000000000000003700000000harec-0.25.2+git.1750492315.966012b/tests/35-floats.hafn 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);
};
fn eval() void = {
// TODO: more compile-time evaluation tests
assert(0.3f32: f64 != 0.3);
static assert(0.3f32: f64 != 0.3);
};
export fn main() void = {
// TODO: test parsing
measurements();
signed_casts();
unsigned_casts();
eval();
};
07070100014DD5000081A40000000000000000000000016856649B00000878000000000000002F00000000000000000000003800000000harec-0.25.2+git.1750492315.966012b/tests/36-defines.hause 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();
};
07070100014DD4000081A40000000000000000000000016856649B00000346000000000000002F00000000000000000000003C00000000harec-0.25.2+git.1750492315.966012b/tests/37-annotations.hause rt::{compile, status};
#[this::should::lex()]
#[test::annotation]
#[test::annotation(param, type, "string", 2 + 2)]
#[test::nested(foo(bar[() baz[{}[]]{bat}]))]
export #[annotations] fn #[can] main(#[appear]) #[anywhere] void = {
compile(status::LEX, "#[(this::should::error)]")!;
compile(status::LEX, "#[but this should not]")!;
compile(status::LEX, "#[nor:: ::should::this]")!;
compile(status::LEX, "# [no::space::allowed]")!;
compile(status::LEX, "#[mismatched(bracket]]")!;
compile(status::LEX, "#[mismatched(bracket}]")!;
compile(status::LEX, "#[mismatched[bracket)]")!;
compile(status::LEX, "#[mismatched[bracket}]")!;
compile(status::LEX, "#[mismatched{bracket)]")!;
compile(status::LEX, "#[mismatched{bracket]]")!;
compile(status::LEX, "#[mismatched::bracket)")!;
compile(status::LEX, "#[missing::bracket")!;
};
07070100014DD3000081ED0000000000000000000000016856649B0000021D000000000000002F00000000000000000000002E00000000harec-0.25.2+git.1750492315.966012b/tests/run#!/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
07070100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!