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

Slice operation with compile-time-known undefined pointer operand has various, inconsistent behaviour, etc. #19798

Open
amp-59 opened this issue Apr 28, 2024 · 0 comments · May be fixed by #19764

Comments

@amp-59
Copy link
Contributor

amp-59 commented Apr 28, 2024

Zig Version

0.13.0-dev.46+3648d7df1

Behaviour

Below is a selection of slice operations showing the variety of outcome when slicing pointers which are known to be undefined at compile time.

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

    // 2. OK
    _ = @as(*[0]u8, undefined)[0..0];
    _ = @as(*u8, undefined)[0..0];

    var x: u8 = 47;
    // 3. OK
    _ = @as([*]u8, undefined)[0..end];
    _ = @as([*]u8, undefined)[0..end :1];
    _ = @as([*]u8, undefined)[0..@intFromPtr(&x) :47];

    // 4. Runtime panic: index out of bounds: index 2, len 0
    _ = @as(*[0]u8, undefined)[0..end :0];

    // 5. Compile error: start index 1 is larger than end index 0
    _ = @as(*[0:1]u8, undefined)[1..];

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

    // 7. Compile error: slice of undefined
    _ = @as([]u8, undefined)[0..0];

    // 8. Compile error: use of undefined value here causes undefined behavior
    _ = @as([]u8, undefined)[0..end];
    _ = @as([*c]u8, undefined)[0..0];

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

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

There are at least two behaviours for slicing compile-time-known undefined pointers. Slice pointer operands outright prevent it, while Many and One pointer operands require that the result length is equal to 0.

For other inputs the control flow reaches errors in other functions (case 8) before Sema.analyzeSlice can determine the outcome, or the compiler can not discern that the input is effectively undefined (cases 9 and 10).

The inconsistency is not a bug, but it is also not good. Every slice operation where the pointer operand is known to be undefined at compile time should produce the same outcome. However this might currently be impossible for cases 9 and 10.

Case 3 is the bug, because the same syntax (with the same pointer operand type) produces a compile error if the length of the result is known to be non-zero at compile time. This requirement has no runtime equivalent.

The addition of a sentinel to this (case 3, expression 1) variant compiles an unavoidable runtime memory error, as the sentinel safety check will attempt to dereference the compile-time-known undefined pointer, offset by the runtime-known end operand (sentinel index).

How is tmp undefined?

Compile and run with zig run undefined_slice_ptr_tuple_lit.zig
undefined_slice_ptr_tuple_lit.zig:

const std = @import("std");
pub fn main() void {
    // @TypeOf(tuple) => struct{comptime [*]u8 = &.{}}
    const tuple = .{@as([]u8, &.{}).ptr + 1};
    std.debug.print("{x}\n", .{@intFromPtr(tuple[0])});
}

Result:

zig run undefined_slice_ptr_tuple_lit.zig
aaaaaaaaaaaaaaab

Something is going on here, because the integer value of tmp.ptr at runtime shows the sum of undefined and 1, an operation that completed at compile time and produced a compile-time-known pointer (as shown by the tuple type). Maybe another issue?

Expected Behaviour

Cases 1 to 8 inclusive should have the same behaviour. Here are some options:

  • Always a compile error: an undefined pointer is 'garbage in', and the slice syntax should not allow 'garbage out'.
  • Length must be zero; checked at both compile time and runtime.
  • Length must be zero; checked at compile time, or a compile error if a runtime length makes checking impossible.
@amp-59 amp-59 linked a pull request Apr 28, 2024 that will close this issue
40 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant