Packed memory has a well-defined layout that doesn't require
conversion from an integer to read from. Let's use it :-)
This change means that for bitcasting to/from a packed value that
is N layers deep, we no longer have to create N temporary big-ints
and perform N copies.
Other miscellaneous improvements:
- Adds support for casting to packed enums and vectors
- Fixes bitcasting to/from vectors outside of a packed struct
- Adds a fast path for bitcasting <= u/i64
- Fixes bug when bitcasting f80 which would clear following fields
This also changes the bitcast memory layout of exotic integers on
big-endian systems to match what's empirically observed on our targets.
Technically, this layout is not guaranteed by LLVM so we should probably
ban bitcasts that reveal these padding bits, but for now this is an
improvement.
Adds a test for inferring features based on a different object file.
Also provides a test case where cpu features are explicitly set on
a library where the end result will output said target features.
This change extends the "lifetime" of the error return trace associated
with an error to continue throughout the block of a `const` variable
that it is assigned to.
This is necessary to support patterns like this one in test_runner.zig:
```zig
const result = foo();
if (result) |_| {
// ... success logic
} else |err| {
// `foo()` should be included in the error trace here
return error.TestFailed;
}
```
To make this happen, the majority of the error return trace popping logic
needed to move into Sema, since `const x = foo();` cannot be examined
syntactically to determine whether it modifies the error return trace. We
also have to make sure not to delete pertinent block information before it
makes it to Sema, so that Sema can pop/restore around blocks correctly.
* Why do this only for `const` and not `var`? *
There is room to relax things for `var`, but only a little bit. We could
do the same thing we do for const and keep the error trace alive for the
remainder of the block where the *assignment* happens. Any wider scope
would violate the stack discipline for traces, so it's not viable.
In the end, I decided the most consistent behavior for the user is just
to kill all error return traces assigned to a mutable `var`.
This change extends the "lifetime" of the error return trace associated
with an error to include the duration of a function call it is passed
to.
This means that if a function returns an error, its return trace will
include the error return trace for any error inputs. This is needed to
support `testing.expectError` and similar functions.
If a function returns a non-error, we have to clean up any error return
traces created by error-able call arguments.
This re-factor is intended to make it easier to track what kind of
operator/expression consumes a result location, without overloading the
ResultLoc union for this purpose.
This is used in the following commit to keep track of initializer
expressions of `const` variables to avoid popping error traces
pre-maturely. Hopefully this will also be useful for implementing
RLS temporaries in the future.
In order to enforce a strict stack discipline for error return traces,
we cannot track error return traces that are stored in variables:
```zig
const x = errorable(); // errorable()'s error return trace is killed here
// v-- error trace starts here instead
return x catch error.UnknownError;
```
In order to propagate error return traces, function calls need to be passed
directly to an error-handling expression (`if`, `catch`, `try` or `return`):
```zig
// When passed directly to `catch`, the return trace is propagated
return errorable() catch error.UnknownError;
// Using a break also works
return blk: {
// code here
break :blk errorable();
} catch error.UnknownError;
```
Why do we need this restriction? Without it, multiple errors can co-exist
with their own error traces. Handling that situation correctly means either:
a. Dynamically allocating trace memory and tracking lifetimes, OR
b. Allowing the production of one error to interfere with the trace of another
(which is the current status quo)
This is piece (3/3) of https://github.com/ziglang/zig/issues/1923#issuecomment-1218495574
This allows for errors to be "re-thrown" by yielding any error as the
result of a catch block. For example:
```zig
fn errorable() !void {
return error.FallingOutOfPlane;
}
fn foo(have_parachute: bool) !void {
return errorable() catch |err| b: {
if (have_parachute) {
// error trace will include the call to errorable()
break :b error.NoParachute;
} else {
return;
}
};
}
pub fn main() !void {
// Anything that returns a non-error does not pollute the error trace.
try foo(true);
// This error trace will still include errorable(), whose error was "re-thrown" by foo()
try foo(false);
}
```
This is piece (2/3) of https://github.com/ziglang/zig/issues/1923#issuecomment-1218495574
This implement trace "popping" for correctly handled errors within
`catch { ... }` and `else { ... }` blocks.
When breaking from these blocks with any non-error, we pop the error
trace frames corresponding to the operand. When breaking with an error,
we preserve the frames so that error traces "chain" together as usual.
```zig
fn foo(cond1: bool, cond2: bool) !void {
bar() catch {
if (cond1) {
// If baz() result is a non-error, pop the error trace frames from bar()
// If baz() result is an error, leave the bar() frames on the error trace
return baz();
} else if (cond2) {
// If we break/return an error, then leave the error frames from bar() on the error trace
return error.Foo;
}
};
// An error returned from here does not include bar()'s error frames in the trace
return error.Bar;
}
```
Notice that if foo() does not return an error it, it leaves no extra
frames on the error trace.
This is piece (1/3) of https://github.com/ziglang/zig/issues/1923#issuecomment-1218495574
It is possible to get comptime-known values from runtime-known values
for example the length of array. Allowing runtime only instructions to
be emitted outside function bodies allows these operations to happen.
In places where comptime-known values are required we have other methods
to ensure that and they usually result in more specific compile errors too.
Closes#12240