Skip to content
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

Runtime safety (panic interface, slices) #19764

Draft
wants to merge 125 commits into
base: master
Choose a base branch
from

Conversation

amp-59
Copy link
Contributor

@amp-59 amp-59 commented Apr 25, 2024

Overview

This initial section is only intended as a high-level explanation of the obvious features of the new panic interface.

This will be the overarching order of discussion (including later installments):

  1. New panic interface
  2. Slice syntax
  3. Implementation progress
  4. Performance measurements and examples
  5. Significant options and future patches

Note: names of functions, function parameters, fields, types, and compile commands, and texts of compile error messages and panic error messages will be changed if exact substitutes are provided. These changes will be applied last.

New panic interface

Resolves #17969

Below is the design suggested by @andrewrk in the linked issue:

pub const PanicCause = union(enum) {
    unreach,
    unwrap_null,
    cast_to_null,
    incorrect_alignment,
    out_of_bounds: struct {
        index: usize,
        len: usize,
    },
    sentinel_mismatch_usize: usize,
    sentinel_mismatch_isize: isize,
    sentinel_mismatch_other,
    /// This one comes from a call to `@panic`.
    application_msg: []const u8,
    // ...
};

pub fn panic(cause: PanicCause, error_return_trace: ?*StackTrace, ret_addr: ?usize) noreturn {
    // ...
}

Below is the final design, implemented by this patch:

pub const PanicCause = union(enum(u8)) {
    message = 0,
    unwrapped_error = 1,
    returned_noreturn = 2,
    reached_unreachable = 3,
    corrupt_switch = 4,
    accessed_out_of_bounds = 5,
    accessed_out_of_order = 6,
    accessed_out_of_order_extra = 7,
    accessed_inactive_field: type = 8,
    // ...
    cast_truncated_data: Cast = 26,
    cast_to_enum_from_invalid: type = 27,
    cast_to_error_from_invalid: Cast = 28,
    cast_to_ptr_from_invalid: usize = 29,
    cast_to_int_from_invalid: Cast = 30,
    cast_to_unsigned_from_negative: Cast = 31,
};
pub fn PanicData(comptime cause: PanicCause) type {
    switch (cause) {
        .message => {
            return []const u8;
        },
        .returned_noreturn,
        .reached_unreachable,
        .accessed_null_value,
        .divided_by_zero,
        .corrupt_switch,
        => {
            return void;
        },
        // ...
    }
}
pub fn panic(comptime cause: PanicCause, data: anytype) noreturn {
    // ...
}

Changes to the panic function signature

Exclusion of return address ?usize

The purpose of the ?usize parameter is to allow the special handler functions to forward their return addresses through debug.panicExtra, which calls back to builtin.default_panic to get to debug.panicImpl. See commit 694fab4.

Usage in detail ...

  • These panic IDs call their special handler functions with context data: inactive_union_field, sentinel_mismatch, index_out_of_bounds, start_index_greater_than_end, and unwrap_error. Every other panic ID emits a call to builtin.default_panic with a message from panic_messages.
  • There are no panic IDs where ?usize is non-null.
  • There is a single call to builtin.default_panic throughout the standard library. The sole caller is debug.panicExtra.
  • There are six calls to debug.panicExtra throughout the standard library. Five are from the special handler functions. The final caller is debug.panic, which calls with both ?usize and ?*builtin.StackTrace as null.

Exclusion of stack trace pointer ?*builtin.StackTrace

The only possible non-null value for the stack trace parameter to builtin.default_panic originates from builtin.panicUnwrapError via debug.panicExtra.

Usage in detail ...

  • There are four calls to @errorReturnTrace() throughout the standard library; this is relevant because it reveals every potential origin of a non-null stack trace parameter in a call to builtin.default_panic. All instances are found in startup code (where errors thrown back to entry or thread-main are caught) where debug.dumpStackTrace is called with the unwrapped, dereferenced return value.
  • The only other call to debug.dumpStackTrace throughout the standard library is from debug.panicImpl.
  • There are four calls to debug.panicImpl throughout the standard library. Three are exclusive to the Windows fault handler (which does not compile) and each call with ?*builtin.StackTrace as null. The only remaining caller is builtin.default_panic.

All remaining callers call with null for both values

The special handler functions will be removed by this patch, meaning that neither parameter will ever be defined at the interface with the compiler, except by panic cause unwrapped_error where it (the stack trace pointer) is part of the panic data and not an independent parameter.

The excluded parameters only have the appearance of a purpose due to the standard library overusing builtin.default_panic. See commit 754ea11.

Comptime-known panic cause builtin.PanicCause

Resolves #18995

Two of the existing five special handler functions are generic, because they needed to be so that the feature would work. However for this interface there are significant costs if the interface is not generic.

If the panic cause is runtime-known:

  1. Certain types of context data can not and will never allow reporting, either because the data size exceeds the maximum permitted by any reasonable common type, or because the type ID(s) relevant to the panic condition has unlimited variety and there is no possible common type (such as with pointers).

  2. Multiple additional causes (where data type is the only variable, like sentinel_mismatch_usize: usize, sentinel_mismatch_isize: isize in the suggested interface) would be required to support some of the current variety of panic data, and the logic required to determine which (sub-)causes should be used for which type(s) is complex.

  3. Every writer for every panic cause must be compiled regardless of whether it is used. This would mean that programs compiling std.builtin.panicImpl would include Errol3 tables for the sole purpose of representing float sentinels. The ability to override builtin.panic does not excuse this requirement, because the user should not be forced to compile any (potentially unused) handler defined by their own implementation either.

  4. Some data types for some panic causes become substantially more expensive to report when a compromise type is used. The significance of this point is demonstrated by a later example.

A fixed interface is a downgrade for existing and future runtime safety features (1). It is more complex to maintain (2), comes at a greater upfront cost to the user (3), and is innately less efficient in the fail block for reliance on compromise types (4).

If the panic cause is compile-time-known, meaning the interface is generic:

  1. The user pays for precisely what they use, regardless of whether panic is their definition or the standard library's.

  2. Users are able to define compile errors and other compile-time behaviour for specific panic causes.

  3. Every panic data payload type for every panic cause is supported. This patch adds support for panic data for every panic cause with relevant data:
    unwrapped_error: Discarded error and the error return trace.
    accessed_out_of_bounds: Out-of-bounds index and the upper bound.
    accessed_out_of_order: Start and end operands.
    accessed_out_of_order_extra: Start and end operands and the upper bound of the pointer operand.
    accessed_inactive_field: Requested field and the active field.
    alias_of_noalias_parameter: Destination and source addresses and the length.
    mismatched_memcpy_arguments: Destination and source lengths.
    mismatched_for_loop_capture_lengths: Expected length and the length of the first mismatch.
    mismatched_null_terminator: Value of the element which should be null.
    mismatched_sentinel: Expected value and the actual value.
    mul_overflowed: LHS and RHS operands.
    add_overflowed: LHS and RHS operands.
    sub_overflowed: LHS and RHS operands.
    div_with_remainder: LHS and RHS operands.
    shl_overflow: Integer value and the shift amount.
    shr_overflow: Integer value and the shift amount.
    shift_amt_overflowed: Shift amount.
    cast_to_ptr_from_invalid: Address.
    cast_to_int_from_invalid: Float value.
    cast_truncated_data: Integer value.
    cast_to_unsigned_from_negative: Signed integer value.
    cast_to_enum_from_invalid: Integer value.
    cast_to_error_from_invalid: Integer value or error set element.

Notes:

  • Correction: The stack trace parameter of builtin.default_panic is non-null for errors unwrapped like switch (err) { error.A => @panic("message") }. This functionality could be implemented in the new interface with an additional panic cause unwrapped_error_extra, with an extra data field for the panic message. Whether this will be added to this patch is undecided. The current behaviour for this 'switch-prong-panic-unwrap' syntax is panic cause message like any other @panic.
  • An example of a 'common type' is usize being used as the payload type for all unsigned integers (like the suggested sentinel data type) because it can accommodate most of them, with the exceptions being so infrequent that it is no great loss.
  • An example of a 'compromise type' is using @tagName to convert all types of enumeration to strings and using struct { active: [:0]const u8, accessed: [:0]const u8 } to report inactive field accesses for tagged unions.

Panic examples from the compiler safety testsuite

Spoiler warning.

spoilers

Slice syntax

This patch includes changes to slice semantic analysis. For this and future sections, the implementation offered by this patch will be referred to as Sema.analyzeSlice2, though in reality it will remain in the sub-namespace (Sema.RuntimeSafety) while the pull request is in draft.

This section is only intended to address changes which directly or indirectly contribute to correctness.

Differences compared to status quo

These differences do not directly contribute to the correctness of Sema.analyzeSlice2.

1. Slice operation slice_start does not preserve sentinel value for Many pointer operands

This difference exists because Sema.analyzeSlice2 ignores the values of sentinels for pointer operand types without explicit lengths, because (they mean different things) these can not participate in the same proofs as sentinels for pointer operand types with explicit lengths.

The original behaviour can be restored if that is preferred. The cost of doing so is unknown.

2. Slicing *T uses the same core logic and error messages as every other pointer operand

Status quo allows slicing pointers-to-one like *T using the slice_end syntax. This variant has a separate set of compile errors which are arguably more instructive.

This implementation only provides special compile errors for slicing *T when start or end operands are runtime-known. This is done to adhere to the stricter standard (status quo), which requires that start and end operands are known at compile time. If runtime start and end operands were permitted the behaviour of slicing *T would be indistinguishable from the behaviour of slicing *[1]T.

Special compile errors similar to status quo may be added if that is necessary. The cost of adding these special error messages would be high in relation to how often they would be seen.

Changes to slice behaviour

These changes result in differences in behaviour significant to these categories of outcome:

  • Compile error
  • Runtime panic
  • Success

Added sentinel runtime safety check for compile-time-known pointer operands referencing runtime-known memory

Fixes #19792

Compile and run example program with zig-runtime_safety run omit_sentinel_safety_check.zig
omit_sentinel_safety_check.zig:

var src_mem: [3:0]u8 = .{ 'a', 'b', 'c' };
pub fn main() void {
    const src_ptr: [:0]const u8 = &src_mem;
    const slice: [:0]const u8 = src_ptr[0..1 :0];
    if (slice[slice.len] != 0) {
        unreachable;
    }
}

Output:

zig-runtime_safety run omit_sentinel_safety_check.zig
thread 87608 panic: mismatched null terminator: 98

omit_sentinel_safety_check.zig:4:40: 0x8001308 in main (omit_sentinel_safety_check.zig)
    const slice: [:0]const u8 = src_ptr[0..1 :0];
                                       ^
lib/std/start.zig:501:22: 0x800053e in posixCallMainAndExit (omit_sentinel_safety_check.zig)
            root.main();
                     ^
lib/std/start.zig:253:5: 0x800001a in _start (omit_sentinel_safety_check.zig)
    asm volatile (switch (native_arch) {
    ^

This difference exists because Sema.analyzeSlice incorrectly assumes that a compile-time-check will always perform the test for this combination of inputs. Instead, the pointer load fails (because the memory is runtime-known) and there is no mechanism in place to try again at runtime.

Corrected computation of upper bound used by runtime safety check

Fixes #19794

Compile and run example program with zig-runtime_safety run underestimate_upper_bound.zig
underestimate_upper_bound.zig:

var dest_end: usize = 3;
pub fn main() void {
    const src_mem: [3:0]usize = .{ 0, 0, 0 };
    const src_ptr: [:0]const usize = &src_mem;
    _ = src_ptr[0..dest_end :0];
    _ = src_ptr[0..][0..dest_end :0];
}

Output:

zig-runtime_safety run underestimate_upper_bound.zig

This will be explained in future comments or issues on slicing to consume sentinel.

Corrected computation of upper bound used by compile-time assertion

Fixes #19795

Compile example program with zig-runtime_safety build-obj overestimate_upper_bound.zig
overestimate_upper_bound.zig:

var src_mem: [3]u8 = .{ 0, 0, 0 };
pub fn main() void {
    const src_ptr: *[3]u8 = src_mem[0..3];
    _ = src_ptr[1..3 :1];
    _ = src_ptr[1..][0..2 :1];
}

Output:

zig-runtime_safety build-obj overestimate_upper_bound.zig
overestimate_upper_bound.zig:4:20: error: slice sentinel out of bounds: end 3(+1), length 3
    _ = src_ptr[1..3 :1];
                   ^
referenced by:
    callMain: lib/std/start.zig:501:17
    callMainWithArgs: lib/std/start.zig:469:12
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

This will be explained in future comments or issues on slicing to consume sentinel.

Substituted runtime crash with compile error for slice_start (with sentinel) for certain pointer operands

Fixes #19796

Compile example program with zig-runtime_safety build-obj slice_start_sentinel_always_out_of_bounds.zig
slice_start_sentinel_always_out_of_bounds.zig:

pub fn main() !void {
    var buf: *const [8]u8 = &([1]u8{0} ** 8);
    _ = buf[0.. :1];
}

Output:

zig-runtime_safety build-obj slice_start_sentinel_always_out_of_bounds.zig
slice_start_sentinel_always_out_of_bounds.zig:3:18: error: sentinel index always out of bounds
    _ = buf[0.. :1];
                 ^
referenced by:
    callMain: lib/std/start.zig:511:32
    callMainWithArgs: lib/std/start.zig:469:12
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

Added compile-time assertions for slices of reinterpreted memory for certain comptime pointer operands

Closes #19797

Compile example program with zig-runtime_safety build-obj demo_various_unusable_results.zig
demo_various_unusable_results.zig:

const T = extern struct {
    a: usize = 1,
    b: u32 = 0,
    c: [4]u16 = .{ 2, 3, 4, 5 },
};
const S = extern struct {
    a: usize = 1,
    b: T = .{},
    c: [4]u8 = .{ 2, 3, 4, 5 },
};
var mem1: [2]S = .{ .{}, .{} };
const mem2: [2]S = .{ .{}, .{} };
comptime {
    const ptr1: [*]usize = @ptrCast(&mem1);
    const len1: usize = (2 * @sizeOf(S)) / @sizeOf(usize);
    const slice1: []usize = ptr1[0..len1];

    _ = ptr1[slice1.len + 1 ..];
}
comptime {
    const ptr1: [*]const usize = @ptrCast(&mem2);
    const ptr2: [*]const u32 = @ptrCast(ptr1[2..]);
    const len2: usize = ((2 * @sizeOf(S)) - 2 * @sizeOf(usize)) / @sizeOf(u32);
    const slice2: []const u32 = ptr2[0..len2];

    _ = ptr2[slice2.len + 1 ..];
}
comptime {
    var mem3: [2]S = .{ .{}, .{} };
    const ptr1: [*]usize = @ptrCast(&mem3);
    const ptr2: [*]u32 = @ptrCast(ptr1[2..]);
    const ptr3: [*]u8 = @ptrCast(ptr2[1..]);
    const len3: usize = (((2 * @sizeOf(S)) - 2 * @sizeOf(usize)) - @sizeOf(u32)) / @sizeOf(u8);
    const slice3: []u8 = ptr3[0..len3];

    _ = ptr3[slice3.len + 1 ..];
}
comptime {
    const mem4: [2]S = .{ .{}, .{} };
    const ptr4: [*]const u16 = @ptrCast(&mem4[0].b.c[2]);
    const len4: usize = ((2 * @sizeOf(S)) - (@offsetOf(S, "b") + @offsetOf(T, "c") + (2 * @sizeOf(u16)))) / @sizeOf(u16);
    const slice4: []const u16 = ptr4[0..len4];

    _ = ptr4[slice4.len + 1 ..];
}
comptime {
    var mem5: comptime_int = 0;
    const ptr5: [*]comptime_int = @ptrCast(&mem5);
    const slice5: []comptime_int = ptr5[0..1];

    _ = ptr5[slice5.len + 1 ..];
}
comptime {
    var mem6: comptime_int = 0;
    const ptr6: [*]type = @ptrCast(&mem6);

    _ = ptr6[0..1];
}

Output:

zig-runtime_safety build-obj demo_various_unusable_results.zig
demo_various_unusable_results.zig:18:25: error: slice start out of bounds of reinterpreted memory: start 11, length 10
    _ = ptr1[slice1.len + 1 ..];
             ~~~~~~~~~~~^~~
demo_various_unusable_results.zig:26:25: error: slice start out of bounds of reinterpreted memory: start 17, length 16
    _ = ptr2[slice2.len + 1 ..];
             ~~~~~~~~~~~^~~
demo_various_unusable_results.zig:36:25: error: slice start out of bounds of reinterpreted memory: start 61, length 60
    _ = ptr3[slice3.len + 1 ..];
             ~~~~~~~~~~~^~~
demo_various_unusable_results.zig:44:25: error: slice start out of bounds of reinterpreted memory: start 29, length 28
    _ = ptr4[slice4.len + 1 ..];
             ~~~~~~~~~~~^~~
demo_various_unusable_results.zig:51:25: error: slice start out of bounds of reinterpreted memory: start 2, length 1
    _ = ptr5[slice5.len + 1 ..];
             ~~~~~~~~~~~^~~
demo_various_unusable_results.zig:57:13: error: type 'comptime_int' cannot be reinterpreted as type 'type'
    _ = ptr6[0..1];
        ~~~~^~~~~~

The functions used to compute these lengths are currently:

  • Sema.RuntimeSafety.abiSizeOfContainingDecl for element types with runtime bits.
  • Sema.RuntimeSafety.reinterpretLengthOfContainingDecl for element types without runtime bits.

Reduced variety of outcome when slicing compile-time-known undefined pointers, etc.

Fixes #19798

less_various_behaviour_of_slice_of_const_undefined.zig:

var end: usize = 1;
var val: u8 = 47;
pub fn main() void {
    // 1. OK
    _ = @as([*]u8, undefined)[0..0];
    _ = @as(*[0]u8, undefined)[0..0];
    _ = @as(*u8, undefined)[0..0];
    _ = @as([*c]u8, undefined)[0..0];
    _ = @as([]u8, undefined)[0..0];

    // 2. Compile error: non-zero length slice of undefined pointer
    _ = @as(*[0:1]u8, undefined)[1..];
    _ = @as([*]u8, undefined)[0..1];

    // 3. Compile error: slice of undefined pointer with runtime length causes undefined behaviour
    _ = @as([*]u8, undefined)[0..end];
    _ = @as([]u8, undefined)[0..end];
    _ = @as(*[0]u8, undefined)[0..end :0];

    // 4. Compile error: slice end out of bounds: end 1, length 0
    _ = @as([]u8, &.{})[0..1];

    // 5. Runtime panic: index out of bounds: index 1, length 0
    _ = @as([]u8, &.{})[0..end];
}

This new behaviour reflects the third option presented in the linked issue. This was done to avoid adding the complexity of breakages (potentially at runtime in the case of the second option) to what is likely to be a time-consuming merge.

Implementation progress

Sema

  • Applied feature restrictions to check (enable runtime safety) and extra (enable runtime safety and forward context data to panic handler) to match panic_fn and safety_check_formatted when testing is complete.
  • Remove optional usage of the old interface.
  • Remove unused functions, such as:
    • panicUnwrapError
    • panicIndexOutOfBounds
    • panicInactiveUnionField
    • panicSentinelMismatch
    • safetyCheckFormatted
    • safetyPanic
    • analyzeSlice
  • Added compile errors for non-scalar sentinels in functions: zirReify, zirArrayTypeSentinel, zirPtrType, analyzeSlice.
  • Updated functions with panic conditions to use new panic preparation functions:
    • checkAccessInactiveUnionField by unionFieldPtr
    • checkAccessInactiveUnionField by unionFieldVal
    • checkAccessNullValue by analyzeOptionalPayloadPtr
    • checkAccessNullValue by zirOptionalPayload
    • checkAccessNullValue, checkAccessOutOfBounds, checkAccessOutOfOrder, and checkMismatchedSentinel by analyzeSlice
    • checkAccessOutOfBounds by elemPtrArray
    • checkAccessOutOfBounds by elemPtrSlice
    • checkAccessOutOfBounds by elemValArray
    • checkAccessOutOfBounds by elemValSlice
    • checkAliasingMemcpyArguments, checkMismatchedMemcpyArgumentLengths, and analyzeSlice2 by zirMemcpy
    • checkArithmeticOverflow by addDivIntOverflowSafety
    • checkArithmeticOverflow by analyzeArithmetic
    • checkArithmeticOverflow by zirDivExact
    • checkCastToEnumFromInvalid and panicReachedUnreachable by analyzeSwitchRuntimeBlock
    • checkCastToEnumFromInvalid by zirEnumFromInt
    • checkCastToEnumFromInvalid by zirSwitchBlock
    • checkCastToEnumFromInvalid by zirTagName
    • checkCastToErrorFromInvalid by zirErrorCast
    • checkCastToErrorFromInvalid by zirErrorFromInt
    • checkCastToIntFromInvalid by zirIntFromFloat
    • checkCastToPointerFromInvalid by coerceCompatiblePtrs
    • checkCastToPointerFromInvalid by ptrCastFull
    • checkCastToPointerFromInvalid by zirPtrFromInt
    • checkCastTruncatedData and checkCastToUnsignedFromNegative by intCast
    • checkDivisionByZero by addDivByZeroSafety
    • checkMismatchedForLoopCaptureLengths by zirForLen
    • checkShiftAmountOverflow and checkArithmeticOverflow by zirShl
    • checkShiftAmountOverflow and checkArithmeticOverflow by zirShr
    • checkUnwrappedError by analyzeErrUnionPayloadPtr
    • checkUnwrappedError by analyzeErrUnionPayload
    • panicReachedUnreachable by Block.addUnreachable
    • panicReachedUnreachable by analyzeCall
    • panicUnwrappedError by maybeErrorUnwrap
    • panicWithMsg by zirPanic
    • analyzeSlice2 by zirSliceStart, zirSliceEnd, zirSliceSentinel, and zirSliceLength

Sema.RuntimeSafety

  • Remove namespace, relocate definitions to be more appropriate.
  • Added helper functions:
    • preparePanicCause
    • failBlock
    • preparePanicExtraType
    • callBuiltinHelper
    • wantPanicCause
    • resolveArithOverflowedPanicImpl
    • resolveArithOverflowedPanicCause
    • abiSizeOfContainingDecl
    • reinterpretLengthOfContainingDecl
  • Added panic preparation functions:
    • panicWithMsg
    • panicReachedUnreachable
    • panicCastToEnumFromInvalid
    • panicUnwrappedError
    • checkAccessNullValue
    • checkUnwrappedError
    • checkDivisionByZero
    • checkAccessOutOfBounds
    • checkAccessOutOfOrder
    • checkAccessOutOfOrderExtra
    • checkAliasingMemcpyArguments
    • checkMismatchedMemcpyArgumentLengths
    • checkMismatchedForLoopCaptureLengths
    • checkAccessInactiveUnionField
    • checkMismatchedSentinel
    • checkMismatchedNullTerminator
    • checkShiftAmountOverflow
    • checkArithmeticOverflow
    • checkCastToEnumFromInvalid
    • checkCastToErrorFromInvalid
    • checkCastToPointerFromInvalid
    • checkCastToIntFromInvalid
    • checkCastTruncatedData
    • checkCastToUnsignedFromNegative
  • Added primary functions:
    • zirSliceStart
    • zirSliceEnd
    • zirSliceSentinel
    • zirSliceLength
    • analyzeSlice2

Module

  • Module: Eliminate unused items from reference cache, including those from the old implementation.
  • Added cache of panic-related types and function references.

c

  • Rename to panic when the old interface is removed.
  • Added placeholder panicNew function to replace panic.

compiler_rt

  • Rename to panic when the old interface is removed.
  • Added placeholder panicNew function to replace panic.

main

  • Removed controls listed below, as these required changes which exceeded the (already large) scope of this patch.
  • Removed convenience/test flags -f[no-]runtime-safety and -f[no-]analyze-slice2 when testing is complete.
  • Updated command line parser to parse convenience/test flags -f[no-]runtime-safety and -f[no-]analyze-slice2.
  • Updated command line parser to parse controls for panic causes:
    • -funwrapped-error
    • -funwrapped-null
    • -freturned-noreturn
    • -freached-unreachable
    • -faccessed-out-of-bounds
    • -faccessed-out-of-order
    • -faccessed-inactive-field
    • -fdivided-by-zero
    • -fmemcpy-argument-aliasing
    • -fmismatched-memcpy-argument-lengths
    • -fmismatched-for-loop-capture-lengths
    • -fmismatched-sentinel
    • -fshift-amt-overflowed
    • -farith-overflowed
      (add_overflowed, sub_overflowed,
      inc_overflowed, dec_overflowed,
      div_overflowed, div_with_remainder)
    • -fcast-truncated-data
    • -fcast-to-enum-from-invalid
    • -fcast-to-error-from-invalid
    • -fcast-to-pointer-from-invalid
    • -fcast-to-int-from-invalid
    • -fcast-to-unsigned-from-negative

main.RuntimeSafety

  • Removed namespace and helper functions related to test flags.
  • Added helper functions:
    • setAll
    • parseSafety
    • parseAllSafety

std.builtin

  • Removed format from StackTrace.
  • Added the standard library implementation for panicNew.
  • Repurposed definition of default_panic.

std.debug

  • Substituted helper functions with any existing analogous standard library functions.
  • Added writer functions
  • Renamed panicImpl to panicImplDefault, and replaced panicImpl with definition of former builtin.default_panic.
  • Updated panicExtra to call panicImpl instead of builtin.panic.

std.math.nextafter

std.multi_array_list

  • Rewrote MultiArrayList.Slice.items to avoid slice of comptime-known undefined pointer with runtime-known end operand.

test

  • Add analyzeSlice2 runtime panic variants or restore former slice-related safety tests.
  • Updated existing slice compile error tests.
  • Added analyzeSlice2 compile error variants.
  • Added analyzeSlice2 success variants.
  • Restored ad hoc non-slice tests with new panic interface.
  • Removed old runtime safety (panic) tests.

test.tests

  • Rename to panic when the old interface is removed.
  • Added definition for panicNew to Compiler Explorer program.

type

  • Added function allowSentinel for determining whether an aggregate of type ty permits sentinels.

             Helper functions require standard library substitutes.
                      require the extreme slice edge-case: slice of
                      comptime-known pointer with runtime-known end.
             'convenience' declaration of `runtime_safety`.
* Added convenience declaration `runtime_safety`.
* This value modified directly from the command line in `main.zig`.
* This value is used to initialize `Builtin` options field `runtime_safety`.
* Updated command line parser to parse controls for each panic cause compile command:
     `-funwrapped-error`,
     `-funwrapped-null`,
     `-freturned-noreturn`,
     `-freached-unreachable`,
     `-faccessed-out-of-bounds`,
     `-faccessed-out-of-order`,
     `-faccessed-inactive-field`,
     `-fdivided-by-zero`,
     `-fmemcpy-argument-aliasing`,
     `-fmismatched-memcpy-argument-lengths`,
     `-fmismatched-for-loop-capture-lengths`,
     `-fmismatched-sentinel`,
     `-fshift-amt-overflowed`,
     `-farith-overflowed`,
     `-fcast-truncated-data`,
     `-fcast-to-enum-from-invalid`,
     `-fcast-to-error-from-invalid`,
     `-fcast-to-pointer-from-invalid`,
     `-fcast-to-int-from-invalid`,
     `-fcast-to-unsigned-from-negative`.
* Updated command line parser to parse convenience/test flags:
    `-fruntime-safety`,
    `-f[no-]analyze-slice2`.
…Safety`.

      When testing is complete this namespace will be removed and all
      analogous functions in the file scope will be replaced.

* Added helper functions:
    `preparePanicCause`,
    `failBlock`,
    `preparePanicExtraType`,
    `callBuiltinHelper`,
    `wantPanicCause`,
    `bestGenericIntType`,
    `resolveArithOverflowedPanicImpl`,
    `resolveArithOverflowedPanicCause`,
    `abiSizeOfContainingDecl`.

* Added panic preparation functions:
    `panicWithMsg`,
    `panicReachedUnreachable`,
    `panicCastToEnumFromInvalid`,
    `panicUnwrappedError`,
    `checkAccessNullValue`,
    `checkUnwrappedError`,
    `checkDivisionByZero`,
    `checkAccessOutOfBounds`,
    `checkAccessOutOfOrder`,
    `checkAccessOutOfOrderExtra`,
    `checkAliasingMemcpyArguments`,
    `checkMismatchedMemcpyArgumentLengths`,
    `checkMismatchedForLoopCaptureLengths`,
    `checkAccessInactiveUnionField`,
    `checkMismatchedSentinel`,
    `checkMismatchedNullTerminator`,
    `checkShiftAmountOverflow`,
    `checkArithmeticOverflow`,
    `checkCastToEnumFromInvalid`,
    `checkCastToEnumFromInvalidHelper`,
    `checkCastToErrorFromInvalid`,
    `checkCastToErrorFromInvalidHelper`,
    `checkCastToPointerFromInvalid`,
    `checkCastToIntFromInvalid`,
    `checkCastTruncatedData`,
    `checkCastToUnsignedFromNegative`.

* Added primary functions:
    `zirSliceStart`,
    `zirSliceEnd`,
    `zirSliceSentinel`,
    `zirSliceLength`,
    `analyzeSlice2`.
      `zirReify`, `zirArrayTypeSentinel`,
      `zirPtrType`, `analyzeSlice`.
      `zirSliceStart`, `zirSliceEnd`,
      `zirSliceSentinel`, and `zirSliceLength`.
      `checkCastToUnsignedFromNegative` by `intCast`.
      `zirSwitchBlock`. This is potentially the wrong panic cause, but
      aligns more closely with the backend requirement.
      `panicReachedUnreachable` by `analyzeSwitchRuntimeBlock`.
      `checkArithmeticOverflow` by `zirShl`.
      `checkArithmeticOverflow` by `zirShr`.
amp-59 added 30 commits May 10, 2024 03:55
      possible that the current implementation only calls this function
      after having already established backend support, however it might
      not be the case that backend support for `error_return_trace` and
      `unwrap_error` are the same. In this case case a lot of the `unwrap_error`
      panic data may be reported without the `builtin.StackTrace` pointer.
* Improved general usability of panic preparation functions by
  preventing usage by backends which do not support the feature.

  Note: The x86-64 backend actually does support all but three of the
  current panic causes. It does not support `checkCastToEnumFromInvalid`
  and `checkCastToErrorFromInvalid`, nor does it support the former
  `corrupt_switch` panic cause (except in one unconditional case)--but
  this might be unreachable for other reasons.

* Updated `reinterpretLength` to allow recursively reinterpreting
  arrays of arrays, arrays of vectors, and vectors of arrays of the
  child type.

* Relocated helper functions `reinterpretLength`,
  `reinterpretLengthOfContainingDecl`, and `abiSizeOfContainingDecl`, which may
  be suitable for general use.

* Removed extra definition of `Sema.analyzeSlice2` from the sub-namespace.

* Added experimental `addSubSafeOptimized`. This function has been
  tested to produce a small optimisation to arithmetic overflow checks,
  which are currently very expensive when generated by LLVM. The
  soundness of this function has not been totally verified, so it
  remains unused.
      a small effect overall, because the applicable variants are not
      very common (in empty main). However the effect will likely be
      much larger for functions in `std.crypto`, where the variant is
      frequently seen in inline while loops.
* Removed explicit field values from `PanicCause`. These will not
  be required until the `RuntimeSafety` type is used to manage
  the state of runtime safety features.
* Swapped field order of `ErrorStackTrace`.
         precisely. Note that mismatches have not been ruled out, but
         none are known.
* Removed unused cache elements.
* Removed more references to `runtime_safety`.
      `panicIndexOutOfBounds`, `panicInactiveUnionField`,
      `panicSentinelMismatch`, `safetyCheckFormatted`, and `safetyPanic`.
* Removed `export` from functions declared in slice success cases
  behaviour test.
* Added skip for `stage2_riscv64` backend for the same slice behaviour test.
         these are unreachable due to the preceding if-expression.
…ed`.

       This is more efficient for the standard library, because all of these
       are likely to be compiled before reaching `main`.
* Removed `addSafetyCheck`.
* `addSafetyCheck` will not insert a trap if the target fail block has
  no instructions.
* Removed `haveRuntimeSafetyPanic`.
* Added traps where required.

target: Temporarily disabled `riscv64` panics. This was recently added
        for the old interface but I know nothing about the requirements for the
        new interface.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment