#Why can't I take a slice of a single item pointer?

1 messages · Page 1 of 1 (latest)

south silo
#

Trying to do so produces error: slice of single-item pointer
The best work around appears to be doing something like @as(*[1]T, ptr), which is not very obvious and a common cause of friction. It seems like single item pointers should coerce, is there a good reason why this is not the case?

neat flume
#

because single item pointers aren't slices?

#

if you're working with pointers to multiple items, you should be using slices in the first place

#

that's the point of having multiple pointer types

south silo
#

The real world case is where one is calling some library API that takes a slice but the specific usage only ever passes a single thing. Multi-item pointers coerce, why not single-item pointers?

neat flume
#

what do you mean multi-item pointers coerce? Do you mean array pointers?

south silo
#

yes

mortal stag
#

Multipointers don't coerce to slices AFAIK, because they don't know what length to slice at - so you have to do mptr[0..n] to do that.

neat flume
#

*[n]T coerces to []T because it is an obvious and often-encountered conversion. slice.len = array_ptr.len, slice.ptr = array_ptr

#

single item pointers don't coerce because they are not something that is considered to point to multiple items

south silo
#

yeah that's what i'm saying the ptr[0..n] syntax doesn't work for single item pointer, but why is single_ptr[0..1] not legal?

neat flume
#

because it doesn't make sense to slice a single item

#

that's just the design of the type system

south silo
#

but its quite common when calling api that work with slices

neat flume
#

the way you solve this is by using @as(*[1]T, single_ptr)

#

that's it

south silo
#

yes, which is non-obvious and friction. is that friction necessary?

neat flume
#

yes, because single item pointers are not usually meant to be treated as pointers to many items. A pointer to one item isn't supposed to point to many items, and it doesn't make sense to treat it as if it were able to, only to solve that one use case, which is already solved by typing a few extra characters

#

I would also argue it's about as non-obvious as any other zig-specific feature, but makes sense within the context of zig as it is

#

that you could slice a single item pointer makes no sense within the context of zig

south silo
#

it makes sense in the context of writing code. this is something that people bump into and @as(*[1]T, single_ptr) is only obvious once you've seen it. there are other, less nice and footgunnable workarounds too. at the very least, the compiler should offer the correct workaround as advice when giving the single-item pointer error. but i'm really struggling to see what would be so bad about accepting the coercion when the slice length is compile-time known

neat flume
#

sure, error messages are always improvable.
but it would be bad, because it creates an ambiguity in the type system. Currently, *[n]T coerces to []T. But if *T can coerce to []T directly, then the logic/reasoning behind the coercion between all of these becomes confusing. Does this mean that *[n]T should also be coercible to [][n]T? What about *T to [*]T, since *[1]T can also coerce to that? That certainly doesn't sound good.
It's just more simple as it is, there is virtually no room for emergent behaviour, whereas there is with the suggested coercion.

#

I don't see any value in allowing it, and can only imagine emergent degradation of the type system as a result of it

south silo
gritty quest
#

@neat flume thanks for the answer. I need to think about it a bit. trying to figure out where &myvalue[0..1] could be ambiguous.

neat flume
# gritty quest <@161569749984083968> thanks for the answer. I need to think about it a bit. try...

one edge case example of where it may be problematic is actually userland "type inference" code. Right now, it's pretty simple for a function to take slice: anytype, and infer that if the function passes *[n]T, then it should be treated like []T. If the suggested coercion were allowed, then it would no longer be clear if what the code should do is the aforementioned conversion, or instead treat it like [][n]T; as it is, you have to explicitly opt in to treating a pointer to your single value as a pointer to potentially many values

#

this could be worked around of course

#

but that's worse than status quo

gritty quest
#

hmmm... a bit busy now but will think more about this later, it feels like it ought to work unambiguously! 😄

south silo
#

"as it is, you have to explicitly opt in to treating a pointer to your single value as a pointer to potentially many values"
is using the [0..] syntax not sufficient to explicitly opt in?

south silo
#

bump Q:
is using the [0..] syntax not sufficient to explicitly opt in?

neat flume
south silo
#

why is it weird? we're just saying that we want to treat this as a collection of one right?

neat flume
#

That isn't really what "slicing" means

#

Generally slicing is an operation which returns a pointer to a subsection of a span of memory

#

And usually, it's a subsection containing 0, 1, or more items

#

A single item pointer doesn't have a "section" as it were, it just points to one object

#

That's the point of having a type system with multiple pointer types

#

Otherwise we could just get rid of single item pointers and allow doing .* on many item pointers

south silo
#

im not saying we should change the type system or define the meaning of things differently, just that is seems like there is a non-destructive way to reduce friction in this one specific case that is within the bounds of the type system as-it-is

#

what is a "section"? everything is just spans of memory really

neat flume
#

well, everything is just bytes, but I'm not talking about representation, I mean in the context of the type system

neat flume
#

I should be able to read code, and know that if I see x[0..], that x is a pointer to 0, 1, or more items, and not have to guess whether it's that, or a single item pointer

south silo
#

gotcha. it seems odd that this is controversial to me.

south silo
#

i too like to be able to read code and know what is going on. but this just makes no sense to me in this case unfortunately

south silo
#

and, with respect, the goal posts keep moving:
first it was "why are you even doing that"
then it was "oh ok, yes but there would be ambiguity"
and then "oh ok that is explicit, but i don't like it"

#

empahasis on the "with respect" bit, sincerly not trying to be an arsehole

neat flume
#

With respect in return, I'm not moving any goal posts, I'm responding to different points.
First it was that you wanted to treat a single item pointer as a slice, which isn't the general case.
Then you brought up a specific use case for implicit coercion, and I said it would be ambiguous.
And then you proposed to use a different syntax to remove the ambiguity, and I said I don't think it makes sense/I don't like it.

neat flume
south silo
#

i dunno man, it's been thing_ptr[0..] since the beginning

#

but thanks for taking the time to answer

#

do appreciate it

neat flume
south silo
#

again, sincerly, hard to not sound sarcastic in text form

neat flume
#

textual communication makes it difficult to parse tone, so don't worry

#

you've done nothing to indicate to me that you are a sarcastic or malintentioned actor, so I give you the benefit of the doubt by default

#

I enjoyed the discussion

#

it's good to argue about these things

#

it made me think about my position on the topic, and I feel more confident in it now after having had to defend it

south silo
#

👍 just pushing on things to see where we can reduce friction for user without compromising type-safety and readabilty

wheat remnant
#

Neither have been accepted or rejected yet

mortal stag
#

It's actually interesting because the idea that "everything is just a bunch of bits" isn't necessarily antithetical to "have a strong type system".
I tend to like the latter because it helps guard against making mistakes, but it's also not enough to prevent the majority of mistakes you actually make: logic bugs.
While having less of a type system obviously won't help with that, the idea that everything is ultimately just a bunch of bits that you are manipulating doesn't necessarily mean that.
It does interest me as a topic, for a similar reason that DoD does; them seem somewhat related to me.

As such, I fancy there may be some interesting ideas that could be made manifest there.

south silo
#

can anyone help me with the above PR?

this is what i'm doing

const ptr = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
if (sema.typeOf(ptr).ptrSize() == .One) {
    const msg = msg: {
        const msg = try sema.errMsg(block, src, "slice of single-item pointer", .{});
        errdefer msg.destroy(sema.gpa);
        try sema.errNote(
            block,
            src,
            msg,
            "perhaps you want to coerce using '@as(*[1]{}, ptr)'?",
            .{ptr_ptr_child_ty.childType().fmt(mod)},
        );
        break :msg msg;
    };
    return sema.failWithOwnedErrorMsg(msg);
} else {
    return sema.fail(block, src, "slice of single-item pointer", .{});
}

but the condition is incorrect. How do I extract the "requested slice length"?

bleak meadow
neat flume
#

not really

#

a single item pointer is a pointer to one item

bleak meadow
#

whats the difference?

neat flume
#

you treat a pointer to one item as a single value that you can operate on. A slice of items is a pointer to many items that can each be operated on, wherein a slice of 1 items just happens to have the same representation as a single item pointer

bleak meadow
#

the difference lies in how you operate with them?

neat flume
#

yeah, that's the difference between literally all types

#

everything is just bytes

#

types are just an abstraction over them

bleak meadow
#

pointers have properties that integers dont, types have different alignment/access-modifiers/etc from others

neat flume
#

what in-memory properties do pointers have that integers don't?

bleak meadow
#

provenance - constness, what range of bytes its allowed to access, if its being accessed in the case of TSAN, hidden hardware verification in the case of CHERI

neat flume
#

those boil down to how you encode the values

#

at the end of the day, you can move one thing into a register, and ask the CPU to operate on it however you want, and then suffer the consequences

bleak meadow
#

in that case, the difference is how the hardware operates on them vs the source

#

but at the source level:

  • there are different types that you operate on similarly: u32 vs f64 for example
  • there are also different types that the compiler operates on differently in which the hardware operates on the same way (volatile/atomics)
#

but going back to ptr vs slice, their main diff being how u operate on them makes sense. What are u thoughts about implicit coercion then? (currently *[N]T to []T)

neat flume
#

sure, but my argument is that it's worth distinguishing single item pointers from many item pointers, because they represent different purposes, vs numerical operations and instruction observability/atomicity.
The coercion from *[n]T to []T makes sense because they are both pointing to multiple items, and it makes pragmatic sense to allow it given the fact that if it were not present, you couldn't assign the result of a slicing operation with comptime indices back to a slice.
The coercion from *T to *[1]T makes sense as a convenience for the use cases specified in this thread.
The proposed implicit coercion from *T to []T doesn't make sense, because the former doesn't point to many items, and can't point to 0 items, it only ever point to one item.

south silo
#

but the proposal is actually an explict coercion from *T to *[1]T that implicitly coerces to []T

bleak meadow
#

if it were not present, you couldn't assign the result of a slicing operation with comptime indices back to a slice.
slicing with comptime indices still gives a slice no?

neat flume
neat flume
#

especially when the coercion already exists

south silo
#

right, the coecion already exists, so we're really only talking about allowing slice syntax to represent that coersion instead of the nasty @as thing

neat flume
#

yeah, essentially

#

disagree that @as is nasty

south silo
bleak meadow
#

makes sense given x[a..b].* gives an array back

neat flume
#

ye

#

that's how it's type-ified

south silo
bleak meadow
#

prob #compiler-devel

south silo
#

thanks

south silo