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

Cast from slices to array pointers #863

Closed
Hejsil opened this issue Mar 27, 2018 · 8 comments
Closed

Cast from slices to array pointers #863

Hejsil opened this issue Mar 27, 2018 · 8 comments
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@Hejsil
Copy link
Contributor

Hejsil commented Mar 27, 2018

Array pointers are slices, but with a length known at compile time. This is powerful because it allows many of Zig's compile time constructs to work on them such as comptime bound check for constants and inline for (inline for can't unroll runtime arrays. We can use inline while to do it though).

I propose making it safer and easier to convert from slices to array pointers so that it encourages people to utilize the static guarantees that array pointers provide.

const s : []const u8 = "ABCD";

// Currently, we can do:
comptime assert(@typeOf(@ptrCast(&const [1]u8, s[1..2].ptr)) == &const [1]u8);

// Option 1: Slicing with constant values should return an array pointer
comptime assert(@typeOf(s[1..2]) == &const [1]u8);

// Option 2: Explicit cast from slice to array pointer. The cast will assert that the slices length is >= the array
comptime assert(@typeOf((&const [1]u8)(s[1..])) == &const [1]u8);

The current method is no good. ptrCast is a scary builtin, and leaving out the 2 in s[1..2] will bypass the safety check that ensures that our slice is at least the size of the array we are casting too.

Option 1 has some upsides. It will seamlessly flow around array pointers, giving you all their benefits without you even trying. Plus, array pointers should implicitly cast to slices (currently, only &const [1]u8 -> []const u8 works, not &[1]u8 -> []u8), so this shouldn't break much code. This does have the downside that changing a const declaration to var might break code that relies on these implicit array pointers.

Option 2 favors the programmer being more explicit about when they want the static guaranty of the array pointer. This allows a more clearly expressed intent, and code your code will only break in predictable ways when consts are changed to var (where the cast takes place).

Note, that indexing array pointers right now is a pain, as [] works on both pointers and arrays. #770 Fixes this.

@Hejsil Hejsil added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Mar 27, 2018
@Hejsil Hejsil changed the title Cast form slices to array pointers Cast from slices to array pointers Mar 28, 2018
@andrewrk andrewrk added this to the 0.3.0 milestone Mar 29, 2018
@andrewrk andrewrk modified the milestones: 0.3.0, 0.4.0 Jul 18, 2018
@andrewrk
Copy link
Member

I like Option 1. Let's implement that and then see if we need to introduce anything else to make slicing seamless.

@andrewrk andrewrk added the accepted This proposal is planned. label Nov 21, 2018
andrewrk added a commit that referenced this issue Dec 13, 2018
 * add `@bswap` builtin function. See #767
 * comptime evaluation facilities are improved to be able to
   handle a `@ptrCast` with a backing array.
 * `@truncate` allows "truncating" a u0 value to any integer
   type, and the result is always comptime known to be `0`.
 * when specifying pointer alignment in a type expression,
   the alignment value of pointers which do not have addresses
   at runtime is ignored, and always has the default/ABI alignment
 * threw in a fix to freebsd/x86_64.zig to update syntax from
   language changes
 * some improvements are pending #863

closes #638
closes #1733

std lib API changes
 * io.InStream().readIntNe renamed to readIntNative
 * io.InStream().readIntLe renamed to readIntLittle
 * io.InStream().readIntBe renamed to readIntBig
 * introduced io.InStream().readIntForeign
 * io.InStream().readInt has parameter order changed
 * io.InStream().readVarInt has parameter order changed
 * io.InStream().writeIntNe renamed to writeIntNative
 * introduced io.InStream().writeIntForeign
 * io.InStream().writeIntLe renamed to writeIntLittle
 * io.InStream().writeIntBe renamed to writeIntBig
 * io.InStream().writeInt has parameter order changed
 * mem.readInt has different parameters and semantics
 * introduced mem.readIntNative
 * introduced mem.readIntForeign
 * mem.readIntBE renamed to mem.readIntBig and different API
 * mem.readIntLE renamed to mem.readIntLittle and different API
 * introduced mem.readIntSliceNative
 * introduced mem.readIntSliceForeign
 * introduced mem.readIntSliceLittle
 * introduced mem.readIntSliceBig
 * introduced mem.readIntSlice
 * mem.writeInt has different parameters and semantics
 * introduced mem.writeIntNative
 * introduced mem.writeIntForeign
 * mem.writeIntBE renamed to mem.readIntBig and different semantics
 * mem.writeIntLE renamed to mem.readIntLittle and different semantics
 * introduced mem.writeIntSliceForeign
 * introduced mem.writeIntSliceNative
 * introduced mem.writeIntSliceBig
 * introduced mem.writeIntSliceLittle
 * introduced mem.writeIntSlice
 * removed mem.endianSwapIfLe
 * removed mem.endianSwapIfBe
 * removed mem.endianSwapIf
 * added mem.littleToNative
 * added mem.bigToNative
 * added mem.toNative
 * added mem.nativeTo
 * added mem.nativeToLittle
 * added mem.nativeToBig
@andrewrk
Copy link
Member

Note after implementing this, audit all the calls to readIntSlice* and writeIntSlice* and make them call the array variants instead.

@Hejsil
Copy link
Contributor Author

Hejsil commented Apr 15, 2019

Here is a fun thing this proposals will allow

a[1..4].* = b[2..5].*;

@Hejsil Hejsil removed their assignment Jun 11, 2019
@andrewrk andrewrk modified the milestones: 0.5.0, 0.6.0 Sep 20, 2019
@thejoshwolfe
Copy link
Contributor

not sure if this was said already, but you can get a comptime len slice at a runtime offset with a[index..][0..4].

@daurnimator
Copy link
Contributor

daurnimator commented Nov 21, 2019

Here is a fun thing this proposals will allow

a[1..4].* = b[2..5].*;

Would a[1..4].* = undefined also work to set a subset of an array to undefined at once? This would be good for removing some awkward @memsets around the place.

@emekoi
Copy link
Contributor

emekoi commented Nov 21, 2019

would this break zig's "no hidden control flow" or do memsets not count as hidden control flow?

@thejoshwolfe
Copy link
Contributor

"no hidden control flow"

There are a few functions implemented by the compiler that don't count as hidden control flow. memcpy and 128bit arithmetic are some that come to mind. The reason these don't count is that they're defined by the language, not user configurable. It's also not clear in each case if the optimizer will emit a function call or inline the assembly. The fact that there's a jump at all is kind of an implementation detail, not significant to the semantics.

@andrewrk
Copy link
Member

Landed in dc04e97.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

5 participants