-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
additional tooling for ABI compatibility verification #20654
Comments
I like this idea but think ^ would be a critical mistake. |
I love this idea, but also agree re: binary format being a mistake.
As a real-world example of where this is valuable in text form, I would point to the .NET runtime. A randomly-selected pull request that implements an API proposal: dotnet/runtime#104651 Notice how the included changes to the ref assembly give you a neat overview of the public API changes without even needing to look at the concrete implementation. |
You've touched on this already, but the job of
Please justify assertions like this. Why would it be a mistake? As it is, this statement has absolutely no value. [@alexrp, that wasn't aimed at you at all, thank you for the discussion points; GitHub just did something odd and didn't show me your comment until I posted this one!] |
While not a standard, the best known attempt I've seen is abi-cafe. APIs are described in KDL to generate C ABI tests for multiple toolchains. You can have clang, gcc, msvc, rustc and Note that the project isn't about defining an ABI, the author gave up on that a while ago. |
I've accepted the proposal. I see there is some pushback on binary format. I still think binary format is the better option, but I'm not saying text is necessarily rejected. If a contributor with a strong vision and sense of taste wants to implement this with a text format, and the text format is nice, then I will accept that counter proposal. |
An important thing to consider is how this will harmonize with the upcoming improvements to the |
One thing I'd like clarification on: How does conditional compilation fit into this? |
A Zig binary exposing an API already needs to make sure that the namespaces containing When importing definitions from another ABI file, conditional compilation isn't relevant, aside from that it allows what I mentioned above, where definitions which aren't recognised due to being from future spec versions or something can be lowered to |
What I mean is, conditional compilation can affect almost every conceivable aspect of the public API/ABI of a Zig library; change a single command line flag and you could, in theory, end up with a completely different API surface. So is the intent here that users should create multiple ABI files depending on relevant flags? |
I mean, sure; the same holds for e.g. C projects, where you can use the preprocessor to include and exclude certain definitions. It doesn't seem particularly controversial that you need the build options to generate accurate API information. |
What if the abi file is just "a C header"? If I understand correctly, it is not a goal of this proposal that it can be used to facilitate interop between languages without going through the C ABI. So for example calling an arbitrary Rust or C++ function from Zig, or vice-versa, is not a goal. Benefits:
Nice-to-haves:
Drawbacks:
Concrete example of how that would work for many pointers, given the following Zig signature: pub extern fn my_func([*]c_int, usize) [*]c_int; // This code was auto-generated by zig emit-h
#include <stddef.h>
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#if __has_attribute(many_pointer)
#define MANY_PTR __attribute__((many_pointer))
#else
#define MANY_PTR
#endif
#if __has_attribute(nonnull)
#define NONNULL __attribute__((nonnull))
#else
#define NONNULL
#endif
#if __has_attribute(returns_nonnull)
#define RETURNS_NONNULL __attribute__((returns_nonnull))
#else
#define RETURNS_NONNULL
#endif
#define MANY NONNULL MANY_PTR
RETURNS_NONNULL MANY_PTR int *my_func(MANY int *, size_t);
#undef RETURNS_NONNULL
#undef MANY
#undef NONNULL
#undef MANY_PTR Functionality:
|
Nice idea! |
ABI mismatch is uncheckable undefined behavior, so it's important to get right. Currently, Zig lacks tooling to help identify such problems. In fact, when the canonical ABI definition lives in a C header file, Zig's safety here is inferior to C. A C compiler will provide errors if the .h file does not match the .c file, but Zig cannot provide errors when a .zig file does not match a .h file.
Observation: the missing primitive here is the ability to compare two Zig compilations with respect to their exported and external ABIs.
So let's start by adding such a primitive to
zig build-exe
and friends:I use the extension
.abi
as opposed to e.g..zabi
because it is a general-purpose format that could be embraced by other tooling; it is not Zig-specific.This file is itself a binary file but would have a corresponding textual representation. When using Zig tooling, textual representation would just render as Zig source code like this:
Of course it would be trivial to output a different language's bindings as a textual representation instead.
An important feature of this ABI description data is the ability to find out when they are incompatible. I envision this being a subcommand of the Zig compiler such as
zig abi-check
which lives inlib/compiler/*
along with the other lazily-compiled subcommands. A corresponding build system step would make it easy to wire things up in a build script like this:This would cause the step to fail if
my_zig_app
used ABI that mismatched the expected ABI.Doing this in the build system this way is nice because it gives a chance for third party projects to embrace the .abi standard and participate both on the giving and receiving end of this.
When an ABI check fails, a handy thing to do would be to convert both to textual representation and perform a line diff algorithm on them. This is already implemented I believe by
std.testing.expectEqualStrings
.After translating C code to Zig code, it is then possible to produce an ABI file. For example:
With this pattern, it is possible to verify that one's Zig code adheres to the ABI codified by a particular .h file, with a particular set of compilation settings.
I imagine a couple more pieces here to complete the puzzle.
One, a subcommand (perhaps part of
zig abi-check
) and corresponding build step that converts an .abi file to Zig source code:Here,
zig_source_file
could now be used in a module as auto-generated bindings. These bindings would be preferable to translated C header files because ABI description files would contain richer type information, such as optional pointers, and pointer sizes. Furthermore, ABI description files could be lowered to C header files, although it would lack the ability to output C macros (I would consider that a feature rather than a limitation).Finally, I imagine embedding these ABI description files into custom linker sections. The idea here is that it would be nice to know the ABI of already-compiled dynamic libraries that reside on one's system. This way you can use them without needing the .h files to be provided, and you don't get the problem that happens sometimes where you compile against one version of header files but the dynamic library on the system is a different version.
Open questions:
usize
but you want to have an enum on the other side, that is well-defined behavior according to the ABI. However, one might want to tweak what kinds of compatibility are or are not counted as mismatch.Related:
The text was updated successfully, but these errors were encountered: