C3 0.8.6 发布:重构库获取功能,新增源码库打包目标
来源标题:C3 0.8.6 – Let's grab those libraries
C3 0.8.6 已发布,主要更新是重构了库获取功能,并新增 source-lib 目标,可将 C3 源码打包成可分发的 .c3l 文件。新版引入简单的仓库协议、版本号和校验和,替代此前仅支持 c3lang/vendor 且无版本管理的临时方案。同时编译速度提升,安全模式二进制的体积缩小三分之一。
以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。
C3 0.8.6 is now available. The two main features are the revamped library fetch functionality and a target for packaging full-source libraries. It also sees improvements to compilation speed and reduces the binary size of safe-mode binaries by one third. It also contains fixes from a systematic review of casts, vectors and calling conventions.
C3 libraries are distributed as .c3l files. These are zip archives with C3 sources, headers, prebuilt binaries per target and a manifest.json. Until now, we used a stop-gap solution: downloads from the c3lang/vendor repository. While this worked, it had no support for versioning and created unnecessary friction as well as a single supported source. Packaging libraries was also a manual affair with multiple steps.
The flagship features of 0.8.6 are two new additions to deal with that: a target for building a library from source, a simple repository protocol, versions and checksums.
There's a new target type, source-lib. It packs C3 source files into a .c3l, which can be shipped.
{ "version": "0.1.0", "authors": [ "John Doe <[email protected]>" ], "sources": [ "src/**" ], "targets": { "my_lib": { "type": "source-lib" } } }
c3c build my_lib writes my_lib.c3l with a generated manifest. A few files can also be packed without a project:
c3c source-lib src/my_lib.c3 src/extra.c3 -o my_lib
To go with this, a manifest without targets now works for every platform, which is what a library made only from source needs, and manifests may contain version, authors and langrev.
c3c fetch adds the ability to download libraries from custom repositories, addressed as <namespace>/<name>[@<version>]:
c3c fetch-list raylib # search c3c fetch-info c3lang/raylib6 # versions, checksums, sizes c3c fetch c3lang/raylib6 # download the latest version c3c fetch c3lang/[email protected] # or a specific one
Inside a project, the library is installed in the first dependency search path and its name is added to dependencies. Libraries are now unpacked into a <name>.c3l directory by default. Use --decompress=no to keep the zip.
A repository is deliberately basic: it only requires three paths that answer GET requests (index.json, info/<namespace>/<name> and download/<namespace>/<name>@<version>). That means any static file host works. The default repository is served directly from a branch of the vendor repository on GitHub. A version may give a url that the library is downloaded from instead, so the files themselves can live on GitHub releases. To use another repository, pass --fetch-url, or set fetch-url in project.json.
Every download is verified against the sha256 checksum in the repository, and you can also lock a dependency to an exact checksum, so that a library can't change under you even if the repository does:
c3c fetch c3lang/[email protected]:d96a68af8dc785ebbc2d7b9f90b024bb5f67829e1d7e890bf1aa5e3f5d794e5b
This builds on – and at the same time entirely replaces – vendor-fetch, which only supported pulling the latest version of a library from the vendor repository.
dependencies may now name repository libraries, optionally with a version, checksum and repository URL: [<url>:]<namespace>/<name>[@<version>][:<sha256>]. The build itself only uses the name – the rest is for project fetch:
"dependencies": [ "c3lang/[email protected]", "c3lang/sqlite3" ]
c3c project fetch without arguments lists what is missing, and with arguments fetches exactly those libraries:
$ c3c project fetch Missing dependencies, fetch them with 'c3c project fetch <library>...': raylib6 sqlite3 $ c3c project fetch raylib6 sqlite3
(Note: That project fetch doesn't automatically fetch dependencies is a deliberate decision.)
In safe mode, C3 checks array bounds, null dereferences, shifts, casts, unwraps and so on, and each check that can fail needs code to report the failure with a message, a file, a function and a line. In 0.8.5 each of those sites built its own message, and this adds up: in a large program there are tens of thousands of them.
In 0.8.6, the code for checks was optimized, and panic functions were created on demand and reused. The effect on executable size was quite substantial: binary sizes were reduced by 30% or more.
To support this, the panic function signature has changed. It now gets a short, always static summary, plus an optional format string and its arguments with the details:
alias PanicFn = fn void(String summary, String file, String function, int line, String fmt, any[] args);
If you have your own panic handler, it needs to be updated. resources/examples/panic_override.c3 shows how (try it in the playground).
--panicfn also works properly now (#3443). It names a function rather than a function pointer variable, and that function becomes the initial value of builtin::panic, so panics from the standard library and abort reach it too, not only the ones generated by the compiler.
Finally, the --panic-msg and --show-backtrace combinations were made consistent: with --panic-msg=no you still get the short summary with file, function and line, unless you also pass --show-backtrace=no, in which case a failed check just traps.
While the change to the panic function type is, strictly speaking, a break, note that changing the panic function did not quite work properly before this.
For this release, we did a fairly thorough review of the cast rules as implemented. This unearthed a bunch of corner-case crashes and miscompilations – but also some conversions that were simply too permissive:
ulong x = -1; // Error: out of range for 'ulong', just like 'uint x = -1' was int[3] a; uint[3] b = a; // Error: same size signed <-> unsigned in arrays, slices and vectors // now needs an explicit cast, just like 'int' to 'uint' float16 h = some_bf; // Error: 'bfloat' and 'float16' no longer convert implicitly void* p = (void*)some_int128; // Error: narrow it first, '(void*)(uptr)some_int128' int i = (int)1e30; // Error: a constant float that doesn't fit is now a compile error
In the other direction, an explicit cast from a slice to a pointer is now allowed for any pointer type, the same way as a cast between pointers.
C3 has always required { } around the statement of an else, but else if accidentally didn't (#3473). Now it does:
if (x == 0) { foo(); } else if (x == 1) foo(); // Error: An 'else if' must use '{ }' even around a single statement.
Duplicate designated initializers
Initializing the same member twice is now an error rather than silently using the last value (#3579):
Foo f = { .a = 1, .b = 2, .a = 3 }; // Error: This element is assigned twice in the same initializer.
This only applies when the initializer is literally used twice. Other uses, such as struct splatting or using .$eval(...), are not affected.
An error inside generics that only shows up for certain parameters used to be hard to track down, because it didn't say which instantiation caused it (#3588). Now there's a note pointing to it:
11: self.size += by; ^^ rect.c3:11:15: error: 'float' cannot implicitly be converted to 'int[<2>]', but you may use a cast.
18: alias IntRect = Rectangle{int}; ^^^^^^^^^ rect.c3:18:17: note: Instantiated here.
Compiler statistics with --stats
--stats=summary prints where the time and memory went during compilation, --stats=full adds lists of the largest modules and functions, and --stats=json (or --stats-file) saves everything as JSON for tooling. This replaces the more ad-hoc --print-large-functions and the previous stats.
━━ SUMMARY ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Wall time 758.1 ms Throughput 133.3 k physical lines/s Frontend 274.9 ms, 36.3 % (initialization, parsing and analysis) Codegen wall / thread time 96.8 ms / 351.4 ms Effective parallelism 3.63× of 8 threads, 24 tasks Arena memory 100.3 MB, upper bound for the peak, LLVM excluded Largest phase Linking, 46.2 % of wall time Largest module std.io, 27.0 % of summed module time
Project files: depends-on and environment variables
Targets can now depend on other targets, which are built first, in the listed order:
"targets": { "my_lib": { "type": "static-lib", "depends-on": [ "generate" ] }, "my_app": { "type": "executable", "depends-on": [ "my_lib" ] } }
Strings in project.json can use environment variables written as ${NAME}, and env / env-override set variables for the exec scripts (#3580):
{ "env": [ "MODE=release" ], "targets": { "my_target": { "type": "executable", "cc": "${CC}", "linker-search-paths": [ "${SDK_PATH}/lib" ] } } }
Smaller things
@asmname sets the exact symbol name, without the platform's C name mangling that @cname gets (such as the leading underscore on macOS). This is useful for linker-generated symbols, e.g. extern char start @asmname("section$start$__DATA$__mysect");. This is the same as the asm attribute in GCC.
LLVM IR (.ll) and bitcode (.bc) files can be passed to the compiler, just like .o files (#3339).
-gline-tables-only emits only line table debug info.
Selectors for @dynamic methods are now a stable 128-bit value computed from the method name, instead of the address of a symbol that the linker had to merge.
An empty infinite loop like for (;;) {} was compiled as a panic, which in optimized builds could break the code around the loop (#3630). It is now a real infinite loop, which is what embedded code waiting for an interrupt wants. The old check is available in safe mode with --infinite-loop-detect=yes.
Paths with non-ASCII characters now work on Windows, both for files and for processes started by the compiler (#3605, #3606).
An error in one of the compiler's worker threads, such as a failing C file, could crash the compiler (#3584).
The C backend
The experimental C backend from 0.8.5 has seen a lot of work: it's now about 3× faster, prunes unused types and declarations from the generated C, supports $$sysclock and @asmname, and fixes many codegen differences with the LLVM backend. There's some work done for compiling the output with the MSVC compiler.
So the backend is still experimental, but progress is being made.
std::compression and std::hash got a big round of work:
char[] packed = deflate::compress(mem, data, level: 9)!;
Regex
std::encoding::regex is faster, and integer overflows in the regex compiler were fixed (CVE-2026-107891). As part of this, the faults and flags were renamed to descriptive names, e.g. NOMATCH is now NO_MATCH and ICASE is IGNORE_CASE. NOSUB was removed.
The new windowing module got a good amount of real-world use and fixes: vsync can now be turned off with OpenGL (#3577), Win32 supports shared OpenGL contexts and respects more pixel format hints, several X11 problems were fixed, as were hidden and transparent mouse cursors, key mappings and mouse wheel deltas on WASM.
A selection of the fixes, the full list is in the change log below:
Most code will compile unchanged, but these changes may require updates:
As always, this release wouldn't have been possible without the C3 community. Thank you to everyone who contributed code, reported issues, tested, and discussed.
Special thanks to sponsors who went the extra mile: andromeda-fp, Josh Ring and konimarti.
andromeda-fp, Berat121, Manu Linares, Michael Pollind, Ricardo Tomasi.
Compiler & toolchain: Book-reader, Manu Linares, Michael Pollind, Tonis, yyxxryrx.
Check out the documentation or download it and try it out.
Have questions? Come and chat with us on Discord.
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。