C3 0.8.3 Feature flags
C3 0.8.3 is now available. The most notable change is the addition of the @feat attribute, which will be the new way to do top-level conditional compilation.
Language changes & improvements¶
@feat¶
This release adds the @feat attribute, which is replacing the older @if usage on non-generic top-level declarations. This attribute is essentially the same as @if($feature(...)) before 0.8.3, but top-level @if is now getting phased out except for use in generics.
The problem with @if¶
Before @if we had $if on the top level, mirroring C's #if and #ifdef. However, this gave very little information for the compiler when someone tried to use a declaration guarded by $if. So @if was introduced to give more granular support and to make it easier for IDEs to figure out "go-to definition" style functionality.
Unfortunately, @if still had the problem of $if, which is that its argument could be any arbitrary compile time constant expression. But what happens if we use something like @if(Foo::methods.len == 3)? Well, if the compiler hasn't yet reached a stage where Foo is analysed, its analysis is now forced. Furthermore, even if Foo is analysed, methods might not be registered yet, giving subtly wrong answers. Because C3 runs in multiple passes, there is no canonical ordering of which thing to analyse first, so it was quite unpredictable. Even though refinements were constantly made to the ordering, the underlying flaw was unsolvable.
The insight¶
Surveying the use of @if they roughly fell into one of three categories:
- Conditionally include depending on compilation target, e.g.
@if(env::WIN32) - Include "if no other implementation is found", e.g.
@if(!$defined(String)). - Conditionally add functionality depending on generic type, e.g.
@if($defined((Type){} + (Type){}))
Of these, the (3) didn't pose a problem in most cases, because in general it happened fairly late. In the case of (2), this was now mostly covered by the new behaviour of @weak (introduced in 0.7.11).
So what about @if(env::WIN32)? This was almost completely confined to the std::core::env module, which would define constants and derived constants based on compiler builtins, and all of those cases could actually be directly passed in by the compiler.
Introducing @feat¶
As previously mentioned, @feat is essentially $if($feature(...)) but to make this work, the compiler now provides many feature flags built-in, such as WIN32, LINUX, BIG_ENDIAN etc. In general any use of env::CONSTANT has a @feat(CONSTANT) counterpart.
This solves the ordering problem because all these constants are known before declarations are registered, so any symbols with @feat can be filtered early. This is not only beneficial for the compiler – LSPs and editors can take advantage of this and accurately determine what's available at compile time.
In more detail, @feat may take multiple constants, in which case it is satisfied on any match:
It's also possible to use | instead:
! may be used to negate a feature:
Multiple @feat means all must be satisfied:
It's possible to use & as an alternative:
More complex expressions using | & and ! are allowed:
Together with this the $feature function has been renamed $feat ($feature is now deprecated and will be removed in 0.9.0).
Possible keyword changes¶
0.7.0 introduced the family of "-def" keywords: constdef, typedef, faultdef and attrdef. After evaluating this for a little over a year, there has been some doubt whether these are really good enough. For this reason 0.8.3 introduces experimental aliases for each:
constdef->constset,cenumtypedef->distinctfaultdef->faultconst,faultset,excuseattrdef->attrgroup,attrmacro
These are considered experimental, meaning that there is no stability guarantee. Versions 0.8.4 and onwards are free to remove these aliases.
Please take time to try them out and offer feedback on them.
Fetching MacOS¶
Previously, automated fetching of the Windows SDK was added. This version adds SDK fetching for MacOS as well, allowing completely effortless cross-compilation to MacOS from any other platform.
Windows Aarch64 support¶
With this version the C3 compiler finally supports Aarch64 as a Windows target.
Standard Library Updates¶
Regex¶
With this release, C3 finally has regex support. Please note that the API is considered experimental and may change in a later 0.8.x version.
Other changes¶
JSON/object unmarshaling is now available through json::unmarshal, object::unmarshal, and related functions. json::temp_load is deprecated in favor of json::tload.
Geometry code should note one behavior change: Rect.contains_point is now exclusive on the maximum edge. A new Bounds type has also been added for rectangular regions represented by min and max, with operations inclusive along the boundary edge.
LinkedList and Deque now have a prepend method, FixedList has is_full(), and ranges gained range::upto, range::inclusive, range::exclusive, Range.to_array, and ExclusiveRange.to_array.
Other useful additions include io::read_buffer and comparison operators for DateTime.
Finally, the tracking allocator is now able to track cross-thread allocation.
Fixes¶
This release fixes a wide range of correctness issues, including generics, compile-time execution, JSON parsing, IO streams, compression, networking, AES CTR, RISC-V ABI handling, doc generation, and several compiler crashes.
Notable fixes include:
- Generic functions and values now correctly require a prefix.
- The arena allocator no longer errors in safe mode when freeing the last memory while the arena is full.
LinkedList.push_front_allnow preserves the expected order.BitSet.lennow reports the bit set length instead of the underlying type size.- RFC 3339 formatting now emits the correct value for microseconds.
- Timed
tcp::connectnow returns the real result instead of always failing withio::GENERAL_ERROR. - Generic methods are no longer checked before their generic type is fully registered.
_erffnow invokes Cerffinstead oferf.- AES CTR no longer loses sync for data whose size is not a multiple of 16.
- JSON no longer accepts
\vas whitespace. - JSONC parsing no longer loops indefinitely on unterminated comments.
io::read_allnow handles split data.Scannernow handles chunked data correctly.Gzipreaders can now handle streams without seek support.io::printf("%3d", 1)now returns the correct printed length.- RISC-V structs with mixed floating-point and integer fields are now passed and returned by value correctly.
Doc generation also received several fixes: attrdef declarations and docs are emitted, alias doc comments are included, @return contracts are rendered, compiler_rt is excluded when --emit-stdlib=no, and empty JSON fields are omitted for slimmer output.
Thank yous¶
Again, this release wouldn't have been possible without the C3 community. I'd like to extend a deep thank you to all who have contributed, both through filed issues, PRs and just plain discussions.
PR contributors for this release¶
Stdlib: cmann1, Darvisim, hchac, Lado, Manu Linares, Omar Alani
Compiler & toolchain: Darvisim, Fernando López Guevara, Lado, LowByteFox, Manu Linares,
CI/Infrastructure: LowByteFox, Manu Linares, ttambow
Change Log¶
Click for full change log
Changes / improvements¶
- Windows aarch64 is now supported.
- Tracking allocator can now accept cross-thread allocations.
- Filter test backtraces #3368
- Improved GDB compatibility for macros.
- Fail when "emcc" is unavailable instead of falling back to the built-in wasm linker.
- Support fetching MacSDK for easy cross compilation.
- Add
@featattribute, deprecate@ifon non-generic top level declarations. - Add
$featcompile time function.$featureis deprecated and replaced by$feat. - Experimental support for
constset,cenum,faultconst,faultset,excuse,attrgroup,attrmacro,distinct. - Defer resolution of typedef alignment and generics, allowing more recursive definitions.
- Improve error message on multiple < > in a row. #2971
- Add the
lgdtandlidtinstructions to x86 inline assembly.
Stdlib changes¶
- LinkedList and Deque added a
prependmethod. - Added
FixedList.is_full()method Rect.contains_pointis now exclusive on the maximum edge.- Add
Bounds- a rectangular region stored as aminandmaxvalue, with all operations being inclusive along the boundary edge. - Experimental regex support.
- Improved RFC 3986 compatibility.
- JSON unmarshaling support,
json::unmarshaland family. - Object unmarshaling support,
object::unmarshaland family. json::temp_loaddeprecated in favour ofjson::tload.Object::is_mapnow returns true for empty objects.- Add
range::upto,range::inclusiveandrange::exclusivemacros. - Add
io::read_buffer. DateTimeenable comparison operators.- Add
Range.to_arrayandExclusiveRange.to_arraymethods.
Fixes¶
- Generic functions and values incorrectly would not require a prefix. #3374
- Arena allocator would error in safe mode when freeing the last memory and the arena was full. #3378
- LinkedList
push_front_allwas appending in the wrong order. BitSet.lenwould yield the size of the underlying type, not the length.- RFC3339 formatting would yield incorrect value for microseconds.
--objwill always retain the object files. #3380- ThreadGroup with function returning
voidwas broken. - Timed
tcp::connectalways failed withio::GENERAL_ERRORinstead of the real result. - Compile time struct with zeroed union member access causes compiler error #3382.
- Generic methods checked before the generic type is fully registered.
- Math function
_erffinvoked Cerffunction instead oferfffunction #3391 - Defining local constants inside a macro causes it to fail to @const fold. #3397
- AES CTR would lose sync on data not multiples of 16.
- In some cases, on macros rethrowing optional values codegen could fail.
- Json accepted incorrectly accepted
\vas whitespace. - JSONC parsing on unterminated comments would loop indefinitely.
- ZII array constdef would cause an assert. #3411
- Calling a constant void macro inside a macro stops it from being constant. #3410
- Using io::struct_to_format with
$force_dump = truefailed to compile. $foo += 1would not do a copy, leading to incorrect update of$foo. #3400foreach (foo::Type t : x)would not parse properly. #3423- Docgen improvements and fixes: emit
attrdefdeclarations/docs, includealiasdoc comments, render@returncontracts, excludecompiler_rtwhen--emit-stdlib=no, and omit empty JSON fields for slimmer output #3422. - Several uses of InStream didn't properly handle io::EOF.
- Fixes to memory handling during zip loading.
- Multireader reading after a final empty read would crash.
- RISC-V structs with mixed FP and integer fields were corrupted when passed or returned by value. #3428
io::read_alldidn't handle split data.Scannerwould not correctly handle chunked data.Gzipreader couldn't handle a stream without seek.io::printf("%3d", 1)would return the wrong printed length. #3432- Crash when using ternary operator with vector type and inline constdef #3433
- Crash on assign-op to compile-time subscript with non-const result #3419
Want To Dive Into C3?¶
Check out the documentation or download it and try it out.
Have questions? Come and chat with us on Discord.