Go version

go version go1.25rc2 darwin/arm64

Output of go env in your module/workspace:

AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE='on'
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN='***'
GOCACHE='***'
GOCACHEPROG=''
GODEBUG=''
GOENV='***'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/6c/_j2w7w9j23db85y594fl2xmc0000gn/T/go-build3349238838=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='***'
GOMODCACHE='***'
GONOPROXY='***'
GONOSUMDB='***'
GOOS='darwin'
GOPATH='***'
GOPRIVATE='***'
GOPROXY='***'
GOROOT='***'
GOSUMDB='sum.golang.google.cn'
GOTELEMETRY='local'
GOTELEMETRYDIR='***'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='***'
GOVCS=''
GOVERSION='go1.25rc2'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

package main

import (
    "reflect"
    "unsafe"
)

var stringTypePointer = reflect.ValueOf(reflect.TypeFor[string]()).Pointer()

func main() {
    t := reflect.TypeOf(0)
    (*[2]uintptr)(unsafe.Pointer(&t))[1] = stringTypePointer
    println(t.String())
}

What did you see happen?

Without optimization:

>> go run -gcflags='all=-l -N' main.go
string

Default:

>> go run main.go
int

What did you expect to see?

string is correct.

Comment From: mateusz834

Not sure how to judge the use of unsafe here, but:

func main() {
    stringTypePointer := reflect.ValueOf(reflect.TypeFor[string]()).Pointer()
    t := reflect.TypeOf(0)
    (*[2]uintptr)(unsafe.Pointer(&t))[1] = stringTypePointer
    call(t.String()) // int
    call(t.String()) // string
}

//go:noinline
func call(s string) {
    fmt.Println(s)
}
    # live at call to Value.Pointer:
v146    00044 (10) CALL reflect.Value.Pointer(SB)
v152    00045 (?) NOP
v119    00046 (+11) LEAQ go:itab.*reflect.rtype,reflect.Type(SB), DX
v222    00047 (11) MOVQ DX, main.t-16(SP)
v195    00048 (+12) MOVQ AX, main.t-8(SP) # string type
v199    00049 (+13) TESTB AX, (DX)
v202    00050 (13) MOVQ go:itab.*reflect.rtype,reflect.Type+296(SB), DX
v109    00051 (13) LEAQ type:int(SB), AX # WRONG!
v203    00052 (+13) PCDATA $1, $1
    # live at indirect call: t
v203    00053 (+13) CALL DX
    # live at call to call: t
v206    00054 (13) CALL main.call(SB)

I believe that the compiler should not generate the v109 LEAQ.

It seems to happen in late opt:

-v88 (179) = Load <*abi.Type> v95 v155 (~r0.data[*uint8])
-v192 (11) = Phi <*uint8> v131 v88 (~r0.data[*uint8], ~r0.data[*uint8])
+v149 (?) = Addr <*uint8> {type:int} v3 (i.type[uintptr])
+v192 (11) = Phi <*uint8> v131 v149 (~r0.data[*uint8], ~r0.data[*uint8])
v203 (+13) = InterCall <*uint8,int,mem> {AuxCall{nil}} [8] v202 v192 v195

Comment From: cuonglm

git bisect points to https://go-review.googlesource.com/c/go/+/632176.

cc @randall77 @andreybokhanko

Comment From: randall77

I'm afraid this code is type-punning uintptr and unsafe.Pointer in ways that are not allowed. In particular, the write in (*[2]uintptr)(unsafe.Pointer(&t))[1] = stringTypePointer is writing a uintptr to a slot that is typed as an unsafe.Pointer. That is not allowed. Not the cause of this issue, but doing it this way means a required write barrier is missing, which could cause all sorts of GC corruption later on. For this issue, the compiler assumes that a uintptr and an unsafe.Pointer can never have the same address. That allows us to reorder this write with a subsequent read.

This code works:

package main

import (
    "reflect"
    "unsafe"
)

var stringTypePointer = reflect.ValueOf(reflect.TypeFor[string]()).UnsafePointer()

func main() {
    t := reflect.TypeOf(0)
    (*[2]unsafe.Pointer)(unsafe.Pointer(&t))[1] = stringTypePointer
    println(t.String())
}

Not that I would recommend that either. You are reaching into the internals of interfaces. That could break at any time.

Comment From: AlphaLxy

Although StringHeader has been deprecated, another type of error can be constructed for the reasons described above.

package main

import (
    "reflect"
    "unsafe"
)

var A = "A"
var dataToB = unsafe.Pointer(unsafe.StringData("B"))

func main() {
    header := *(*reflect.StringHeader)(unsafe.Pointer(&A))
    (*[2]unsafe.Pointer)(unsafe.Pointer(&header))[0] = dataToB
    printString(header)
}

func printString(header reflect.StringHeader) {
    println(*(*string)(unsafe.Pointer(&header)))
}

Comment From: ianlancetaylor

As the docs say, reflect.StringHeader cannot be used safely. This program is an example of why that is true.

Comment From: AlphaLxy

In the first example, the uintptr values point to memory regions that are guaranteed not to be garbage collected.

Although this usage is incorrect, it has been working fine in previous versions (<= 1.24). It seems to be a breaking change.