Open Jorropo opened 7 months ago
This happen because the compiler use a non saving copy to handle the copy:
TEXT command-line-arguments.Copy(SB), ABIInternal
MOVQ AX, DI
MOVQ BX, SI
MOVL $512, CX
REP
MOVSQ
RET
I would guess duffcopy
has the same issue.
It seems the compiler assumes structs are either not aliased at all or completely aliased (a == b
then copy is a no-op).
It's unclear to me why unsafe
's doc would disallow this.
(1) Conversion of a T1 to Pointer to T2. Provided that T2 is no larger than T1 and that the two share an equivalent memory layout, this conversion allows reinterpreting data of one type as data of another type.
(*S0)(unsafe.Pointer(&arr[1]))
is legal because &arr[1]
point to 2 S1
which is just as large and has the same memory layout as S0
.
Is also legal for the same reasons (*S0)(unsafe.Pointer(&arr[0]))
(it now points to 3 S1
which is larger than 1 S0).
I don't know if this could be a bug in unsafe's docs, or in the compiler.
I don't know if it possible to manifest this bug without using unsafe
.
I found this, so it looks like a sentence is needed in unsafe
:
https://github.com/golang/go/blob/6076edc55c548878c261316f3e3294f1f73125a3/src/cmd/compile/internal/ssagen/ssa.go#L1382-L1399
This conversion already violates unsafe rules:
(*S0)(unsafe.Pointer(&arr[1]))
This violates rule 1, casting from a smaller to a larger pointed-to type.
I did thought about that.
The correct version would be (*S0)(unsafe.Add(unsafe.Pointer(&arr), unsafe.Sizeof(S1{}))
it's more verbose and I was lazy.
Reinterpreting a [2]T
to a struct{a, b T}
is definitely stretching what is allowed by the unsafe rules. But I don't see any rule explicitly disallowing it.
In triage, we agree that the kind of conversion mentioned in this issue is a bit of a stretch. You could argue that converting [2]T to a struct{a, b T} is incorrectly assuming equivalent memory layouts, especially since the spec (technically) allows for things like reordering struct fields.
We should perhaps consider clarifying this case in unsafe
, in which case this is really more an unsafe
package issue as it was originally, but I retitled it to more directly described what @Jorropo discovered.
What about this code that treats any as [2]uintptr?(Currently any is equivalent to a structure of two Pointers, thus converted no problem or rely on existing compiler behavior?) https://gitee.com/qiulaidongfeng/arena/blob/master/buf.go#L150
@qiulaidongfeng That would almost certainly fall under the same non-guarantee. Layouts of multi-word builtin types (interfaces, slices, strings, complex) should not be guaranteed to be interchangeable by unsafe recasting.
In triage, we agree that the kind of conversion mentioned in this issue is a bit of a stretch. You could argue that converting [2]T to a struct{a, b T} is incorrectly assuming equivalent memory layouts, especially since the spec (technically) allows for things like reordering struct fields.
This might need to be reevaluated given https://github.com/golang/go/issues/66408
Go version
go version devel go1.23-6076edc55c Mon Feb 5 20:59:15 2024 +0000 linux/amd64
Output of
go env
in your module/workspace:What did you do?
While investing #65495 I noticed that the compiler makes assumptions about aliasing of structs which are not documented in
unsafe
.See this piece of code:
What did you see happen?
What did you expect to see?