detached 3c86956

all: drop support for Go 1.18 through 1.24 (#469)

Set the go.mod language version to 1.25.0 and trim the CI matrix to the
two most recent Go releases. Exercising every release back to 1.18 made
test runs long, and that cost grows as more targets are added (e.g. a
future windows/arm64). Narrowing the supported range also lowers the
maintenance burden of carrying version-gated workarounds.

Remove the compatibility shims that only existed for older toolchains:

- internal/xreflect, which polyfilled reflect.TypeAssert; callers now
  use the standard library function directly.
- the objc clone helper, replaced by slices.Clone.

Modernize the remaining code to the new baseline: integer range loops
and reflect.TypeFor, and an iter.Seq-based field iterator in the tests.

Updates #468

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
commit
3c869562eaac4f1afa41ceaa6d00721d6527c244
author
Hajime Hoshi · · 2026-06-23 03:13 UTC
committer
GitHub · · 2026-06-23 03:13 UTC
parent
576826c
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
15 files · +56 -117
M .github/workflows/test.yml +2 -7
@@ -10,7 +10,7 @@ jobs:
1010 strategy:
1111 matrix:
1212 os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
13- go: ['1.18.x', '1.19.x', '1.20.x', '1.21.x', '1.22.x', '1.23.x', '1.24.x', '1.25.x', '1.26.x']
13+ go: ['1.25.x', '1.26.x']
1414 name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
1515 runs-on: ${{ matrix.os }}
1616 defaults:
@@ -174,21 +174,18 @@ jobs:
174174 go env -u CXX
175175
176176 - name: go test race (no Cgo)
177- if: ${{ runner.os == 'macOS' && !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }}
177+ if: runner.os == 'macOS'
178178 run: |
179179 # -race usually requires Cgo, but macOS is an exception. See https://go.dev/doc/articles/race_detector#Requirements
180180 env CGO_ENABLED=0 go test -race -shuffle=on -v -count=10 ./...
181181
182182 - name: go test race (Cgo)
183- if: ${{ !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }}
184183 run: |
185184 env CGO_ENABLED=1 go test -race -shuffle=on -v -count=10 ./...
186185
187186 minor-arches:
188187 strategy:
189188 matrix:
190- # Test only the latest two Go versions to save CI time.
191- # See https://go.dev/doc/devel/release#policy
192189 go: ['1.25.x', '1.26.x']
193190 name: Test with Go ${{ matrix.go }} on Linux minor architectures
194191 runs-on: ubuntu-latest
@@ -285,8 +282,6 @@ jobs:
285282 strategy:
286283 matrix:
287284 os: ['FreeBSD', 'NetBSD']
288- # Test only the latest two Go versions to save CI time.
289- # See https://go.dev/doc/devel/release#policy
290285 go: ['1.25.8', '1.26.1']
291286 name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
292287 runs-on: ubuntu-22.04
@@ -10,7 +10,7 @@ jobs:
10 strategy:10 strategy:
11 matrix:11 matrix:
12 os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]12 os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
13- go: ['1.18.x', '1.19.x', '1.20.x', '1.21.x', '1.22.x', '1.23.x', '1.24.x', '1.25.x', '1.26.x']13+ go: ['1.25.x', '1.26.x']
14 name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}14 name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
15 runs-on: ${{ matrix.os }}15 runs-on: ${{ matrix.os }}
16 defaults:16 defaults:
@@ -174,21 +174,18 @@ jobs:
174 go env -u CXX174 go env -u CXX
175 175
176 - name: go test race (no Cgo)176 - name: go test race (no Cgo)
177- if: ${{ runner.os == 'macOS' && !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }}177+ if: runner.os == 'macOS'
178 run: |178 run: |
179 # -race usually requires Cgo, but macOS is an exception. See https://go.dev/doc/articles/race_detector#Requirements179 # -race usually requires Cgo, but macOS is an exception. See https://go.dev/doc/articles/race_detector#Requirements
180 env CGO_ENABLED=0 go test -race -shuffle=on -v -count=10 ./...180 env CGO_ENABLED=0 go test -race -shuffle=on -v -count=10 ./...
181 181
182 - name: go test race (Cgo)182 - name: go test race (Cgo)
183- if: ${{ !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }}
184 run: |183 run: |
185 env CGO_ENABLED=1 go test -race -shuffle=on -v -count=10 ./...184 env CGO_ENABLED=1 go test -race -shuffle=on -v -count=10 ./...
186 185
187 minor-arches:186 minor-arches:
188 strategy:187 strategy:
189 matrix:188 matrix:
190- # Test only the latest two Go versions to save CI time.
191- # See https://go.dev/doc/devel/release#policy
192 go: ['1.25.x', '1.26.x']189 go: ['1.25.x', '1.26.x']
193 name: Test with Go ${{ matrix.go }} on Linux minor architectures190 name: Test with Go ${{ matrix.go }} on Linux minor architectures
194 runs-on: ubuntu-latest191 runs-on: ubuntu-latest
@@ -285,8 +282,6 @@ jobs:
285 strategy:282 strategy:
286 matrix:283 matrix:
287 os: ['FreeBSD', 'NetBSD']284 os: ['FreeBSD', 'NetBSD']
288- # Test only the latest two Go versions to save CI time.
289- # See https://go.dev/doc/devel/release#policy
290 go: ['1.25.8', '1.26.1']285 go: ['1.25.8', '1.26.1']
291 name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}286 name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
292 runs-on: ubuntu-22.04287 runs-on: ubuntu-22.04
M callback_test.go +1 -1
@@ -43,7 +43,7 @@ func TestCallGoFromSharedLib(t *testing.T) {
4343
4444 const want = 10101
4545 cb := purego.NewCallback(goFunc)
46- for i := 0; i < 10; i++ {
46+ for i := range 10 {
4747 got := callCallback(cb, "a test string")
4848 if got != want {
4949 t.Fatalf("%d: callCallback() got %v want %v", i, got, want)
@@ -43,7 +43,7 @@ func TestCallGoFromSharedLib(t *testing.T) {
43 43
44 const want = 1010144 const want = 10101
45 cb := purego.NewCallback(goFunc)45 cb := purego.NewCallback(goFunc)
46- for i := 0; i < 10; i++ {46+ for i := range 10 {
47 got := callCallback(cb, "a test string")47 got := callCallback(cb, "a test string")
48 if got != want {48 if got != want {
49 t.Fatalf("%d: callCallback() got %v want %v", i, got, want)49 t.Fatalf("%d: callCallback() got %v want %v", i, got, want)
M examples/objc/main_darwin.go +1 -1
@@ -36,7 +36,7 @@ func main() {
3636 []objc.FieldDef{
3737 {
3838 Name: "bar",
39- Type: reflect.TypeOf(int(0)),
39+ Type: reflect.TypeFor[int](),
4040 Attribute: objc.ReadWrite,
4141 },
4242 },
@@ -36,7 +36,7 @@ func main() {
36 []objc.FieldDef{36 []objc.FieldDef{
37 {37 {
38 Name: "bar",38 Name: "bar",
39- Type: reflect.TypeOf(int(0)),39+ Type: reflect.TypeFor[int](),
40 Attribute: objc.ReadWrite,40 Attribute: objc.ReadWrite,
41 },41 },
42 },42 },
M func.go +2 -3
@@ -14,7 +14,6 @@ import (
1414 "unsafe"
1515
1616 "github.com/ebitengine/purego/internal/strings"
17- "github.com/ebitengine/purego/internal/xreflect"
1817 )
1918
2019 const (
@@ -155,7 +154,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
155154 // created in NewCallback.
156155 for j := 0; j < arg.NumIn(); j++ {
157156 in := arg.In(j)
158- if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
157+ if !in.AssignableTo(reflect.TypeFor[CDecl]()) {
159158 continue
160159 }
161160 if j != 0 {
@@ -303,7 +302,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
303302 }
304303 }
305304 for i, v := range args {
306- if variadic, ok := xreflect.TypeAssert[[]any](args[i]); ok {
305+ if variadic, ok := reflect.TypeAssert[[]any](args[i]); ok {
307306 if i != len(args)-1 {
308307 panic("purego: can only expand last parameter")
309308 }
@@ -14,7 +14,6 @@ import (
14 "unsafe"14 "unsafe"
15 15
16 "github.com/ebitengine/purego/internal/strings"16 "github.com/ebitengine/purego/internal/strings"
17- "github.com/ebitengine/purego/internal/xreflect"
18 )17 )
19 18
20 const (19 const (
@@ -155,7 +154,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
155 // created in NewCallback.154 // created in NewCallback.
156 for j := 0; j < arg.NumIn(); j++ {155 for j := 0; j < arg.NumIn(); j++ {
157 in := arg.In(j)156 in := arg.In(j)
158- if !in.AssignableTo(reflect.TypeOf(CDecl{})) {157+ if !in.AssignableTo(reflect.TypeFor[CDecl]()) {
159 continue158 continue
160 }159 }
161 if j != 0 {160 if j != 0 {
@@ -303,7 +302,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
303 }302 }
304 }303 }
305 for i, v := range args {304 for i, v := range args {
306- if variadic, ok := xreflect.TypeAssert[[]any](args[i]); ok {305+ if variadic, ok := reflect.TypeAssert[[]any](args[i]); ok {
307 if i != len(args)-1 {306 if i != len(args)-1 {
308 panic("purego: can only expand last parameter")307 panic("purego: can only expand last parameter")
309 }308 }
M func_test.go +1 -1
@@ -60,7 +60,7 @@ func TestRegisterFunc_ConcurrentPointerReturn(t *testing.T) {
6060 wg.Add(1)
6161 go func(id int) {
6262 defer wg.Done()
63- for j := 0; j < 400_000; j++ {
63+ for range 400_000 {
6464 ptr := alloc(5)
6565 if ptr == nil {
6666 continue
@@ -60,7 +60,7 @@ func TestRegisterFunc_ConcurrentPointerReturn(t *testing.T) {
60 wg.Add(1)60 wg.Add(1)
61 go func(id int) {61 go func(id int) {
62 defer wg.Done()62 defer wg.Done()
63- for j := 0; j < 400_000; j++ {63+ for range 400_000 {
64 ptr := alloc(5)64 ptr := alloc(5)
65 if ptr == nil {65 if ptr == nil {
66 continue66 continue
M go.mod +1 -1
@@ -1,3 +1,3 @@
11 module github.com/ebitengine/purego
22
3-go 1.18
3+go 1.25.0
@@ -1,3 +1,3 @@
1 module github.com/ebitengine/purego1 module github.com/ebitengine/purego
2 2
3-go 1.183+go 1.25.0
D internal/xreflect/reflect_go124.go +0 -15
@@ -1,15 +0,0 @@
1-// SPDX-License-Identifier: Apache-2.0
2-// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
3-
4-//go:build !go1.25
5-
6-package xreflect
7-
8-import "reflect"
9-
10-// TODO: remove this and use Go 1.25's reflect.TypeAssert when minimum go.mod version is 1.25
11-
12-func TypeAssert[T any](v reflect.Value) (T, bool) {
13- v2, ok := v.Interface().(T)
14- return v2, ok
15-}
@@ -1,15 +0,0 @@
1-// SPDX-License-Identifier: Apache-2.0
2-// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
3-
4-//go:build !go1.25
5-
6-package xreflect
7-
8-import "reflect"
9-
10-// TODO: remove this and use Go 1.25's reflect.TypeAssert when minimum go.mod version is 1.25
11-
12-func TypeAssert[T any](v reflect.Value) (T, bool) {
13- v2, ok := v.Interface().(T)
14- return v2, ok
15-}
D internal/xreflect/reflect_go125.go +0 -12
@@ -1,12 +0,0 @@
1-// SPDX-License-Identifier: Apache-2.0
2-// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
3-
4-//go:build go1.25
5-
6-package xreflect
7-
8-import "reflect"
9-
10-func TypeAssert[T any](v reflect.Value) (T, bool) {
11- return reflect.TypeAssert[T](v)
12-}
@@ -1,12 +0,0 @@
1-// SPDX-License-Identifier: Apache-2.0
2-// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
3-
4-//go:build go1.25
5-
6-package xreflect
7-
8-import "reflect"
9-
10-func TypeAssert[T any](v reflect.Value) (T, bool) {
11- return reflect.TypeAssert[T](v)
12-}
M objc/objc_block_darwin.go +3 -4
@@ -10,7 +10,6 @@ import (
1010 "unsafe"
1111
1212 "github.com/ebitengine/purego"
13- "github.com/ebitengine/purego/internal/xreflect"
1413 )
1514
1615 const (
@@ -125,7 +124,7 @@ func (*blockCache) encode(typ reflect.Type) *uint8 {
125124 encoding = returnType
126125 }
127126
128- if typ.NumIn() == 0 || typ.In(0) != reflect.TypeOf(Block(0)) {
127+ if typ.NumIn() == 0 || typ.In(0) != reflect.TypeFor[Block]() {
129128 panic(fmt.Sprintf("objc: A Block implementation must take a Block as its first argument; got %v", typ.String()))
130129 }
131130
@@ -168,7 +167,7 @@ func (b *blockCache) getLayout(typ reflect.Type) blockLayout {
168167 reflect.MakeFunc(
169168 typ,
170169 func(args []reflect.Value) (results []reflect.Value) {
171- block, ok := xreflect.TypeAssert[Block](args[0])
170+ block, ok := reflect.TypeAssert[Block](args[0])
172171 if !ok {
173172 panic(fmt.Sprintf("objc: block argument is not a block but %s", args[0].Type().String()))
174173 }
@@ -263,7 +262,7 @@ func InvokeBlock[T any](block Block, args ...any) (result T, err error) {
263262 callResult := fn.Call(reflectedArgs)
264263
265264 var ok bool
266- result, ok = xreflect.TypeAssert[T](callResult[0])
265+ result, ok = reflect.TypeAssert[T](callResult[0])
267266 if !ok {
268267 return result, fmt.Errorf("objc: the returned value type %s was not %T", callResult[0].Type().String(), result)
269268 }
@@ -10,7 +10,6 @@ import (
10 "unsafe"10 "unsafe"
11 11
12 "github.com/ebitengine/purego"12 "github.com/ebitengine/purego"
13- "github.com/ebitengine/purego/internal/xreflect"
14 )13 )
15 14
16 const (15 const (
@@ -125,7 +124,7 @@ func (*blockCache) encode(typ reflect.Type) *uint8 {
125 encoding = returnType124 encoding = returnType
126 }125 }
127 126
128- if typ.NumIn() == 0 || typ.In(0) != reflect.TypeOf(Block(0)) {127+ if typ.NumIn() == 0 || typ.In(0) != reflect.TypeFor[Block]() {
129 panic(fmt.Sprintf("objc: A Block implementation must take a Block as its first argument; got %v", typ.String()))128 panic(fmt.Sprintf("objc: A Block implementation must take a Block as its first argument; got %v", typ.String()))
130 }129 }
131 130
@@ -168,7 +167,7 @@ func (b *blockCache) getLayout(typ reflect.Type) blockLayout {
168 reflect.MakeFunc(167 reflect.MakeFunc(
169 typ,168 typ,
170 func(args []reflect.Value) (results []reflect.Value) {169 func(args []reflect.Value) (results []reflect.Value) {
171- block, ok := xreflect.TypeAssert[Block](args[0])170+ block, ok := reflect.TypeAssert[Block](args[0])
172 if !ok {171 if !ok {
173 panic(fmt.Sprintf("objc: block argument is not a block but %s", args[0].Type().String()))172 panic(fmt.Sprintf("objc: block argument is not a block but %s", args[0].Type().String()))
174 }173 }
@@ -263,7 +262,7 @@ func InvokeBlock[T any](block Block, args ...any) (result T, err error) {
263 callResult := fn.Call(reflectedArgs)262 callResult := fn.Call(reflectedArgs)
264 263
265 var ok bool264 var ok bool
266- result, ok = xreflect.TypeAssert[T](callResult[0])265+ result, ok = reflect.TypeAssert[T](callResult[0])
267 if !ok {266 if !ok {
268 return result, fmt.Errorf("objc: the returned value type %s was not %T", callResult[0].Type().String(), result)267 return result, fmt.Errorf("objc: the returned value type %s was not %T", callResult[0].Type().String(), result)
269 }268 }
M objc/objc_runtime_darwin.go +13 -24
@@ -12,13 +12,13 @@ import (
1212 "reflect"
1313 "regexp"
1414 "runtime"
15+ "slices"
1516 stdstrings "strings"
1617 "unicode"
1718 "unsafe"
1819
1920 "github.com/ebitengine/purego"
2021 "github.com/ebitengine/purego/internal/strings"
21- "github.com/ebitengine/purego/internal/xreflect"
2222 )
2323
2424 // TODO: support try/catch?
@@ -322,7 +322,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
322322 case ReadWrite:
323323 ty := reflect.FuncOf(
324324 []reflect.Type{
325- reflect.TypeOf(ID(0)), reflect.TypeOf(SEL(0)), ivar.Type,
325+ reflect.TypeFor[ID](), reflect.TypeFor[SEL](), ivar.Type,
326326 },
327327 nil, false,
328328 )
@@ -343,7 +343,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
343343 // })(unsafe.Pointer(args[0].Interface().(ID)))).v = 123
344344 //
345345 // However, since the type of the variable is unknown reflection is used to actually assign the value
346- id, ok := xreflect.TypeAssert[ID](args[0])
346+ id, ok := reflect.TypeAssert[ID](args[0])
347347 if !ok {
348348 panic(fmt.Sprintf("objc: id argument is not a ID but %s", args[0].Type().String()))
349349 }
@@ -358,7 +358,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
358358 case ReadOnly:
359359 ty := reflect.FuncOf(
360360 []reflect.Type{
361- reflect.TypeOf(ID(0)), reflect.TypeOf(SEL(0)),
361+ reflect.TypeFor[ID](), reflect.TypeFor[SEL](),
362362 },
363363 []reflect.Type{ivar.Type}, false,
364364 )
@@ -371,7 +371,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
371371 if len(args) != 2 {
372372 panic(fmt.Sprintf("objc: incorrect number of args. expected 2 got %d", len(args)))
373373 }
374- id, ok := xreflect.TypeAssert[ID](args[0])
374+ id, ok := reflect.TypeAssert[ID](args[0])
375375 if !ok {
376376 panic(fmt.Sprintf("objc: id argument is not a ID but %s", args[0].Type().String()))
377377 }
@@ -419,11 +419,11 @@ const (
419419 // Source: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
420420 func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
421421 switch typ {
422- case reflect.TypeOf(Class(0)):
422+ case reflect.TypeFor[Class]():
423423 return encClass, nil
424- case reflect.TypeOf(ID(0)), reflect.TypeOf(Block(0)):
424+ case reflect.TypeFor[ID](), reflect.TypeFor[Block]():
425425 return encId, nil
426- case reflect.TypeOf(SEL(0)):
426+ case reflect.TypeFor[SEL]():
427427 return encSelector, nil
428428 }
429429
@@ -626,7 +626,7 @@ func (p *Protocol) Register() {
626626 func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod bool) []MethodDescription {
627627 count := uint32(0)
628628 desc := protocol_copyMethodDescriptionList(p, isRequiredMethod, isInstanceMethod, &count)
629- methods := clone(unsafe.Slice(desc, count))
629+ methods := slices.Clone(unsafe.Slice(desc, count))
630630 free(unsafe.Pointer(desc))
631631 return methods
632632 }
@@ -635,7 +635,7 @@ func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod
635635 func (p *Protocol) CopyProtocolList() []*Protocol {
636636 count := uint32(0)
637637 desc := protocol_copyProtocolList(p, &count)
638- protocols := clone(unsafe.Slice(desc, count))
638+ protocols := slices.Clone(unsafe.Slice(desc, count))
639639 free(unsafe.Pointer(desc))
640640 return protocols
641641 }
@@ -644,7 +644,7 @@ func (p *Protocol) CopyProtocolList() []*Protocol {
644644 func (p *Protocol) CopyPropertyList(isRequiredProperty, isInstanceProperty bool) []Property {
645645 count := uint32(0)
646646 desc := protocol_copyPropertyList2(p, &count, isRequiredProperty, isInstanceProperty)
647- protocols := clone(unsafe.Slice(desc, count))
647+ protocols := slices.Clone(unsafe.Slice(desc, count))
648648 free(unsafe.Pointer(desc))
649649 return protocols
650650 }
@@ -691,21 +691,10 @@ func NewIMP(fn any) IMP {
691691 switch {
692692 case ty.NumIn() < 2:
693693 fallthrough
694- case ty.In(0) != reflect.TypeOf(ID(0)):
694+ case ty.In(0) != reflect.TypeFor[ID]():
695695 fallthrough
696- case ty.In(1) != reflect.TypeOf(SEL(0)):
696+ case ty.In(1) != reflect.TypeFor[SEL]():
697697 panic("objc: NewIMP must take a (id, SEL) as its first two arguments; got " + ty.String())
698698 }
699699 return IMP(purego.NewCallback(fn))
700700 }
701-
702-// TODO: remove and use slices.Clone when minimum version for purego is 1.21
703-func clone[S ~[]E, E any](s S) S {
704- // Preserve nilness in case it matters.
705- if s == nil {
706- return nil
707- }
708- // Avoid s[:0:0] as it leads to unwanted liveness when cloning a
709- // zero-length slice of a large array; see https://go.dev/issue/68488.
710- return append(S{}, s...)
711-}
@@ -12,13 +12,13 @@ import (
12 "reflect"12 "reflect"
13 "regexp"13 "regexp"
14 "runtime"14 "runtime"
15+ "slices"
15 stdstrings "strings"16 stdstrings "strings"
16 "unicode"17 "unicode"
17 "unsafe"18 "unsafe"
18 19
19 "github.com/ebitengine/purego"20 "github.com/ebitengine/purego"
20 "github.com/ebitengine/purego/internal/strings"21 "github.com/ebitengine/purego/internal/strings"
21- "github.com/ebitengine/purego/internal/xreflect"
22 )22 )
23 23
24 // TODO: support try/catch?24 // TODO: support try/catch?
@@ -322,7 +322,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
322 case ReadWrite:322 case ReadWrite:
323 ty := reflect.FuncOf(323 ty := reflect.FuncOf(
324 []reflect.Type{324 []reflect.Type{
325- reflect.TypeOf(ID(0)), reflect.TypeOf(SEL(0)), ivar.Type,325+ reflect.TypeFor[ID](), reflect.TypeFor[SEL](), ivar.Type,
326 },326 },
327 nil, false,327 nil, false,
328 )328 )
@@ -343,7 +343,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
343 // })(unsafe.Pointer(args[0].Interface().(ID)))).v = 123343 // })(unsafe.Pointer(args[0].Interface().(ID)))).v = 123
344 //344 //
345 // However, since the type of the variable is unknown reflection is used to actually assign the value345 // However, since the type of the variable is unknown reflection is used to actually assign the value
346- id, ok := xreflect.TypeAssert[ID](args[0])346+ id, ok := reflect.TypeAssert[ID](args[0])
347 if !ok {347 if !ok {
348 panic(fmt.Sprintf("objc: id argument is not a ID but %s", args[0].Type().String()))348 panic(fmt.Sprintf("objc: id argument is not a ID but %s", args[0].Type().String()))
349 }349 }
@@ -358,7 +358,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
358 case ReadOnly:358 case ReadOnly:
359 ty := reflect.FuncOf(359 ty := reflect.FuncOf(
360 []reflect.Type{360 []reflect.Type{
361- reflect.TypeOf(ID(0)), reflect.TypeOf(SEL(0)),361+ reflect.TypeFor[ID](), reflect.TypeFor[SEL](),
362 },362 },
363 []reflect.Type{ivar.Type}, false,363 []reflect.Type{ivar.Type}, false,
364 )364 )
@@ -371,7 +371,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
371 if len(args) != 2 {371 if len(args) != 2 {
372 panic(fmt.Sprintf("objc: incorrect number of args. expected 2 got %d", len(args)))372 panic(fmt.Sprintf("objc: incorrect number of args. expected 2 got %d", len(args)))
373 }373 }
374- id, ok := xreflect.TypeAssert[ID](args[0])374+ id, ok := reflect.TypeAssert[ID](args[0])
375 if !ok {375 if !ok {
376 panic(fmt.Sprintf("objc: id argument is not a ID but %s", args[0].Type().String()))376 panic(fmt.Sprintf("objc: id argument is not a ID but %s", args[0].Type().String()))
377 }377 }
@@ -419,11 +419,11 @@ const (
419 // Source: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100419 // Source: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
420 func encodeType(typ reflect.Type, insidePtr bool) (string, error) {420 func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
421 switch typ {421 switch typ {
422- case reflect.TypeOf(Class(0)):422+ case reflect.TypeFor[Class]():
423 return encClass, nil423 return encClass, nil
424- case reflect.TypeOf(ID(0)), reflect.TypeOf(Block(0)):424+ case reflect.TypeFor[ID](), reflect.TypeFor[Block]():
425 return encId, nil425 return encId, nil
426- case reflect.TypeOf(SEL(0)):426+ case reflect.TypeFor[SEL]():
427 return encSelector, nil427 return encSelector, nil
428 }428 }
429 429
@@ -626,7 +626,7 @@ func (p *Protocol) Register() {
626 func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod bool) []MethodDescription {626 func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod bool) []MethodDescription {
627 count := uint32(0)627 count := uint32(0)
628 desc := protocol_copyMethodDescriptionList(p, isRequiredMethod, isInstanceMethod, &count)628 desc := protocol_copyMethodDescriptionList(p, isRequiredMethod, isInstanceMethod, &count)
629- methods := clone(unsafe.Slice(desc, count))629+ methods := slices.Clone(unsafe.Slice(desc, count))
630 free(unsafe.Pointer(desc))630 free(unsafe.Pointer(desc))
631 return methods631 return methods
632 }632 }
@@ -635,7 +635,7 @@ func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod
635 func (p *Protocol) CopyProtocolList() []*Protocol {635 func (p *Protocol) CopyProtocolList() []*Protocol {
636 count := uint32(0)636 count := uint32(0)
637 desc := protocol_copyProtocolList(p, &count)637 desc := protocol_copyProtocolList(p, &count)
638- protocols := clone(unsafe.Slice(desc, count))638+ protocols := slices.Clone(unsafe.Slice(desc, count))
639 free(unsafe.Pointer(desc))639 free(unsafe.Pointer(desc))
640 return protocols640 return protocols
641 }641 }
@@ -644,7 +644,7 @@ func (p *Protocol) CopyProtocolList() []*Protocol {
644 func (p *Protocol) CopyPropertyList(isRequiredProperty, isInstanceProperty bool) []Property {644 func (p *Protocol) CopyPropertyList(isRequiredProperty, isInstanceProperty bool) []Property {
645 count := uint32(0)645 count := uint32(0)
646 desc := protocol_copyPropertyList2(p, &count, isRequiredProperty, isInstanceProperty)646 desc := protocol_copyPropertyList2(p, &count, isRequiredProperty, isInstanceProperty)
647- protocols := clone(unsafe.Slice(desc, count))647+ protocols := slices.Clone(unsafe.Slice(desc, count))
648 free(unsafe.Pointer(desc))648 free(unsafe.Pointer(desc))
649 return protocols649 return protocols
650 }650 }
@@ -691,21 +691,10 @@ func NewIMP(fn any) IMP {
691 switch {691 switch {
692 case ty.NumIn() < 2:692 case ty.NumIn() < 2:
693 fallthrough693 fallthrough
694- case ty.In(0) != reflect.TypeOf(ID(0)):694+ case ty.In(0) != reflect.TypeFor[ID]():
695 fallthrough695 fallthrough
696- case ty.In(1) != reflect.TypeOf(SEL(0)):696+ case ty.In(1) != reflect.TypeFor[SEL]():
697 panic("objc: NewIMP must take a (id, SEL) as its first two arguments; got " + ty.String())697 panic("objc: NewIMP must take a (id, SEL) as its first two arguments; got " + ty.String())
698 }698 }
699 return IMP(purego.NewCallback(fn))699 return IMP(purego.NewCallback(fn))
700 }700 }
701-
702-// TODO: remove and use slices.Clone when minimum version for purego is 1.21
703-func clone[S ~[]E, E any](s S) S {
704- // Preserve nilness in case it matters.
705- if s == nil {
706- return nil
707- }
708- // Avoid s[:0:0] as it leads to unwanted liveness when cloning a
709- // zero-length slice of a large array; see https://go.dev/issue/68488.
710- return append(S{}, s...)
711-}
M objc/objc_runtime_darwin_test.go +2 -2
@@ -58,12 +58,12 @@ func ExampleRegisterClass() {
5858 []objc.FieldDef{
5959 {
6060 Name: "bar",
61- Type: reflect.TypeOf(int(0)),
61+ Type: reflect.TypeFor[int](),
6262 Attribute: objc.ReadWrite,
6363 },
6464 {
6565 Name: "foo",
66- Type: reflect.TypeOf(false),
66+ Type: reflect.TypeFor[bool](),
6767 Attribute: objc.ReadWrite,
6868 },
6969 },
@@ -58,12 +58,12 @@ func ExampleRegisterClass() {
58 []objc.FieldDef{58 []objc.FieldDef{
59 {59 {
60 Name: "bar",60 Name: "bar",
61- Type: reflect.TypeOf(int(0)),61+ Type: reflect.TypeFor[int](),
62 Attribute: objc.ReadWrite,62 Attribute: objc.ReadWrite,
63 },63 },
64 {64 {
65 Name: "foo",65 Name: "foo",
66- Type: reflect.TypeOf(false),66+ Type: reflect.TypeFor[bool](),
67 Attribute: objc.ReadWrite,67 Attribute: objc.ReadWrite,
68 },68 },
69 },69 },
M struct_arm64.go +3 -3
@@ -311,7 +311,7 @@ func copyStruct8ByteChunks(ptr unsafe.Pointer, size uintptr, addChunk func(uintp
311311 chunk = *(*uintptr)(unsafe.Add(ptr, offset))
312312 } else {
313313 // Read byte-by-byte to avoid reading beyond allocation
314- for i := uintptr(0); i < remaining; i++ {
314+ for i := range remaining {
315315 b := *(*byte)(unsafe.Add(ptr, offset+i))
316316 chunk |= uintptr(b) << (i * 8)
317317 }
@@ -514,7 +514,7 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
514514 paddingNeeded := uintptr(valAlign) - (currentOffset % uintptr(valAlign))
515515 fields = append(fields, reflect.StructField{
516516 Name: paddingFieldPrefix + strconv.Itoa(fieldIndex),
517- Type: reflect.ArrayOf(int(paddingNeeded), reflect.TypeOf(byte(0))),
517+ Type: reflect.ArrayOf(int(paddingNeeded), reflect.TypeFor[byte]()),
518518 })
519519 currentOffset += paddingNeeded
520520 fieldIndex++
@@ -610,7 +610,7 @@ func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int,
610610
611611 // Pointer on stack (rare: all integer registers exhausted).
612612 if isDarwin {
613- ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeOf(uintptr(0)))
613+ ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeFor[uintptr]())
614614 ptr := uintptr(ptrVal.Uint())
615615 return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()
616616 }
@@ -311,7 +311,7 @@ func copyStruct8ByteChunks(ptr unsafe.Pointer, size uintptr, addChunk func(uintp
311 chunk = *(*uintptr)(unsafe.Add(ptr, offset))311 chunk = *(*uintptr)(unsafe.Add(ptr, offset))
312 } else {312 } else {
313 // Read byte-by-byte to avoid reading beyond allocation313 // Read byte-by-byte to avoid reading beyond allocation
314- for i := uintptr(0); i < remaining; i++ {314+ for i := range remaining {
315 b := *(*byte)(unsafe.Add(ptr, offset+i))315 b := *(*byte)(unsafe.Add(ptr, offset+i))
316 chunk |= uintptr(b) << (i * 8)316 chunk |= uintptr(b) << (i * 8)
317 }317 }
@@ -514,7 +514,7 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
514 paddingNeeded := uintptr(valAlign) - (currentOffset % uintptr(valAlign))514 paddingNeeded := uintptr(valAlign) - (currentOffset % uintptr(valAlign))
515 fields = append(fields, reflect.StructField{515 fields = append(fields, reflect.StructField{
516 Name: paddingFieldPrefix + strconv.Itoa(fieldIndex),516 Name: paddingFieldPrefix + strconv.Itoa(fieldIndex),
517- Type: reflect.ArrayOf(int(paddingNeeded), reflect.TypeOf(byte(0))),517+ Type: reflect.ArrayOf(int(paddingNeeded), reflect.TypeFor[byte]()),
518 })518 })
519 currentOffset += paddingNeeded519 currentOffset += paddingNeeded
520 fieldIndex++520 fieldIndex++
@@ -610,7 +610,7 @@ func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int,
610 610
611 // Pointer on stack (rare: all integer registers exhausted).611 // Pointer on stack (rare: all integer registers exhausted).
612 if isDarwin {612 if isDarwin {
613- ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeOf(uintptr(0)))613+ ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeFor[uintptr]())
614 ptr := uintptr(ptrVal.Uint())614 ptr := uintptr(ptrVal.Uint())
615 return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()615 return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()
616 }616 }
M struct_test.go +17 -33
@@ -6,11 +6,13 @@
66 package purego_test
77
88 import (
9+ "iter"
910 "math"
1011 "os"
1112 "path/filepath"
1213 "reflect"
1314 "runtime"
15+ "slices"
1416 "testing"
1517 "unsafe"
1618
@@ -76,7 +78,6 @@ func TestRegisterFunc_structArgs(t *testing.T) {
7678 },
7779 }
7880 for _, imp := range implementations {
79- imp := imp
8081 if imp.usesCallbacks && runtime.GOOS == "windows" {
8182 // Callbacks on Windows use the stdlib syscall.NewCallback, which does
8283 // not support struct arguments or returns.
@@ -834,38 +835,23 @@ func TestRegisterFunc_structArgs(t *testing.T) {
834835 }
835836 }
836837
837-// TODO: this could use the iter.Seq interface when purego supports Go 1.23
838-func nextFieldFn(v reflect.Value) func() (reflect.Value, bool) {
839- var fieldIndex int
840- var tracker func() (reflect.Value, bool)
841- return func() (reflect.Value, bool) {
842- if v.NumField() == 0 {
843- return reflect.Value{}, false
844- }
845- if tracker != nil {
846- if field, ok := tracker(); ok {
847- return field, ok
848- }
849- tracker = nil
850- }
851- for fieldIndex < v.NumField() {
852- if v.Type().Field(fieldIndex).Name == "_" {
853- fieldIndex++
838+func fields(v reflect.Value) iter.Seq[reflect.Value] {
839+ return func(yield func(reflect.Value) bool) {
840+ for i := range v.NumField() {
841+ if v.Type().Field(i).Name == "_" {
854842 continue
855843 }
856- field := v.Field(fieldIndex)
857- fieldIndex++
844+ field := v.Field(i)
858845 if field.Kind() == reflect.Struct {
859- tracker = nextFieldFn(field)
860- if inner, ok := tracker(); ok {
861- return inner, ok
846+ for inner := range fields(field) {
847+ if !yield(inner) {
848+ return
849+ }
862850 }
863- tracker = nil
864- } else {
865- return field, true
851+ } else if !yield(field) {
852+ return
866853 }
867854 }
868- return reflect.Value{}, false
869855 }
870856 }
871857
@@ -903,13 +889,12 @@ func TestRegisterFunc_structReturns(t *testing.T) {
903889 fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value {
904890 retType := reflect.TypeOf(fptr).Elem().Out(0)
905891 ret := reflect.New(retType).Elem()
906- next := nextFieldFn(ret)
907- for _, a := range args {
908- field, ok := next()
909- if !ok {
892+ leaves := slices.Collect(fields(ret))
893+ for i, a := range args {
894+ if i >= len(leaves) {
910895 panic("purego: no more fields")
911896 }
912- field.Set(a)
897+ leaves[i].Set(a)
913898 }
914899 return []reflect.Value{ret}
915900 })
@@ -918,7 +903,6 @@ func TestRegisterFunc_structReturns(t *testing.T) {
918903 },
919904 }
920905 for _, imp := range implementations {
921- imp := imp
922906 if imp.usesCallbacks && runtime.GOOS == "windows" {
923907 // Callbacks on Windows use the stdlib syscall.NewCallback, which does
924908 // not support struct arguments or returns.
@@ -6,11 +6,13 @@
6 package purego_test6 package purego_test
7 7
8 import (8 import (
9+ "iter"
9 "math"10 "math"
10 "os"11 "os"
11 "path/filepath"12 "path/filepath"
12 "reflect"13 "reflect"
13 "runtime"14 "runtime"
15+ "slices"
14 "testing"16 "testing"
15 "unsafe"17 "unsafe"
16 18
@@ -76,7 +78,6 @@ func TestRegisterFunc_structArgs(t *testing.T) {
76 },78 },
77 }79 }
78 for _, imp := range implementations {80 for _, imp := range implementations {
79- imp := imp
80 if imp.usesCallbacks && runtime.GOOS == "windows" {81 if imp.usesCallbacks && runtime.GOOS == "windows" {
81 // Callbacks on Windows use the stdlib syscall.NewCallback, which does82 // Callbacks on Windows use the stdlib syscall.NewCallback, which does
82 // not support struct arguments or returns.83 // not support struct arguments or returns.
@@ -834,38 +835,23 @@ func TestRegisterFunc_structArgs(t *testing.T) {
834 }835 }
835 }836 }
836 837
837-// TODO: this could use the iter.Seq interface when purego supports Go 1.23838+func fields(v reflect.Value) iter.Seq[reflect.Value] {
838-func nextFieldFn(v reflect.Value) func() (reflect.Value, bool) {839+ return func(yield func(reflect.Value) bool) {
839- var fieldIndex int840+ for i := range v.NumField() {
840- var tracker func() (reflect.Value, bool)841+ if v.Type().Field(i).Name == "_" {
841- return func() (reflect.Value, bool) {
842- if v.NumField() == 0 {
843- return reflect.Value{}, false
844- }
845- if tracker != nil {
846- if field, ok := tracker(); ok {
847- return field, ok
848- }
849- tracker = nil
850- }
851- for fieldIndex < v.NumField() {
852- if v.Type().Field(fieldIndex).Name == "_" {
853- fieldIndex++
854 continue842 continue
855 }843 }
856- field := v.Field(fieldIndex)844+ field := v.Field(i)
857- fieldIndex++
858 if field.Kind() == reflect.Struct {845 if field.Kind() == reflect.Struct {
859- tracker = nextFieldFn(field)846+ for inner := range fields(field) {
860- if inner, ok := tracker(); ok {847+ if !yield(inner) {
861- return inner, ok848+ return
849+ }
862 }850 }
863- tracker = nil851+ } else if !yield(field) {
864- } else {852+ return
865- return field, true
866 }853 }
867 }854 }
868- return reflect.Value{}, false
869 }855 }
870 }856 }
871 857
@@ -903,13 +889,12 @@ func TestRegisterFunc_structReturns(t *testing.T) {
903 fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value {889 fn := reflect.MakeFunc(reflect.TypeOf(fptr).Elem(), func(args []reflect.Value) []reflect.Value {
904 retType := reflect.TypeOf(fptr).Elem().Out(0)890 retType := reflect.TypeOf(fptr).Elem().Out(0)
905 ret := reflect.New(retType).Elem()891 ret := reflect.New(retType).Elem()
906- next := nextFieldFn(ret)892+ leaves := slices.Collect(fields(ret))
907- for _, a := range args {893+ for i, a := range args {
908- field, ok := next()894+ if i >= len(leaves) {
909- if !ok {
910 panic("purego: no more fields")895 panic("purego: no more fields")
911 }896 }
912- field.Set(a)897+ leaves[i].Set(a)
913 }898 }
914 return []reflect.Value{ret}899 return []reflect.Value{ret}
915 })900 })
@@ -918,7 +903,6 @@ func TestRegisterFunc_structReturns(t *testing.T) {
918 },903 },
919 }904 }
920 for _, imp := range implementations {905 for _, imp := range implementations {
921- imp := imp
922 if imp.usesCallbacks && runtime.GOOS == "windows" {906 if imp.usesCallbacks && runtime.GOOS == "windows" {
923 // Callbacks on Windows use the stdlib syscall.NewCallback, which does907 // Callbacks on Windows use the stdlib syscall.NewCallback, which does
924 // not support struct arguments or returns.908 // not support struct arguments or returns.
M syscall_bench_test.go +7 -7
@@ -205,40 +205,40 @@ func callRegisterFunc(registerFn any, n int, args []int64, iterations int) int64
205205 switch n {
206206 case 1:
207207 f := registerFn.(*func(int64) int64)
208- for i := 0; i < iterations; i++ {
208+ for range iterations {
209209 result = (*f)(args[0])
210210 }
211211 case 2:
212212 f := registerFn.(*func(int64, int64) int64)
213- for i := 0; i < iterations; i++ {
213+ for range iterations {
214214 result = (*f)(args[0], args[1])
215215 }
216216 case 3:
217217 f := registerFn.(*func(int64, int64, int64) int64)
218- for i := 0; i < iterations; i++ {
218+ for range iterations {
219219 result = (*f)(args[0], args[1], args[2])
220220 }
221221 case 5:
222222 f := registerFn.(*func(int64, int64, int64, int64, int64) int64)
223- for i := 0; i < iterations; i++ {
223+ for range iterations {
224224 result = (*f)(args[0], args[1], args[2], args[3], args[4])
225225 }
226226 case 10:
227227 f := registerFn.(*func(int64, int64, int64, int64, int64, int64, int64, int64, int64, int64) int64)
228- for i := 0; i < iterations; i++ {
228+ for range iterations {
229229 result = (*f)(args[0], args[1], args[2], args[3], args[4],
230230 args[5], args[6], args[7], args[8], args[9])
231231 }
232232 case 14:
233233 f := registerFn.(*func(int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64) int64)
234- for i := 0; i < iterations; i++ {
234+ for range iterations {
235235 result = (*f)(args[0], args[1], args[2], args[3], args[4],
236236 args[5], args[6], args[7], args[8], args[9],
237237 args[10], args[11], args[12], args[13])
238238 }
239239 case 15:
240240 f := registerFn.(*func(int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64) int64)
241- for i := 0; i < iterations; i++ {
241+ for range iterations {
242242 result = (*f)(args[0], args[1], args[2], args[3], args[4],
243243 args[5], args[6], args[7], args[8], args[9],
244244 args[10], args[11], args[12], args[13], args[14])
@@ -205,40 +205,40 @@ func callRegisterFunc(registerFn any, n int, args []int64, iterations int) int64
205 switch n {205 switch n {
206 case 1:206 case 1:
207 f := registerFn.(*func(int64) int64)207 f := registerFn.(*func(int64) int64)
208- for i := 0; i < iterations; i++ {208+ for range iterations {
209 result = (*f)(args[0])209 result = (*f)(args[0])
210 }210 }
211 case 2:211 case 2:
212 f := registerFn.(*func(int64, int64) int64)212 f := registerFn.(*func(int64, int64) int64)
213- for i := 0; i < iterations; i++ {213+ for range iterations {
214 result = (*f)(args[0], args[1])214 result = (*f)(args[0], args[1])
215 }215 }
216 case 3:216 case 3:
217 f := registerFn.(*func(int64, int64, int64) int64)217 f := registerFn.(*func(int64, int64, int64) int64)
218- for i := 0; i < iterations; i++ {218+ for range iterations {
219 result = (*f)(args[0], args[1], args[2])219 result = (*f)(args[0], args[1], args[2])
220 }220 }
221 case 5:221 case 5:
222 f := registerFn.(*func(int64, int64, int64, int64, int64) int64)222 f := registerFn.(*func(int64, int64, int64, int64, int64) int64)
223- for i := 0; i < iterations; i++ {223+ for range iterations {
224 result = (*f)(args[0], args[1], args[2], args[3], args[4])224 result = (*f)(args[0], args[1], args[2], args[3], args[4])
225 }225 }
226 case 10:226 case 10:
227 f := registerFn.(*func(int64, int64, int64, int64, int64, int64, int64, int64, int64, int64) int64)227 f := registerFn.(*func(int64, int64, int64, int64, int64, int64, int64, int64, int64, int64) int64)
228- for i := 0; i < iterations; i++ {228+ for range iterations {
229 result = (*f)(args[0], args[1], args[2], args[3], args[4],229 result = (*f)(args[0], args[1], args[2], args[3], args[4],
230 args[5], args[6], args[7], args[8], args[9])230 args[5], args[6], args[7], args[8], args[9])
231 }231 }
232 case 14:232 case 14:
233 f := registerFn.(*func(int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64) int64)233 f := registerFn.(*func(int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64) int64)
234- for i := 0; i < iterations; i++ {234+ for range iterations {
235 result = (*f)(args[0], args[1], args[2], args[3], args[4],235 result = (*f)(args[0], args[1], args[2], args[3], args[4],
236 args[5], args[6], args[7], args[8], args[9],236 args[5], args[6], args[7], args[8], args[9],
237 args[10], args[11], args[12], args[13])237 args[10], args[11], args[12], args[13])
238 }238 }
239 case 15:239 case 15:
240 f := registerFn.(*func(int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64) int64)240 f := registerFn.(*func(int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64, int64) int64)
241- for i := 0; i < iterations; i++ {241+ for range iterations {
242 result = (*f)(args[0], args[1], args[2], args[3], args[4],242 result = (*f)(args[0], args[1], args[2], args[3], args[4],
243 args[5], args[6], args[7], args[8], args[9],243 args[5], args[6], args[7], args[8], args[9],
244 args[10], args[11], args[12], args[13], args[14])244 args[10], args[11], args[12], args[13], args[14])
M syscall_unix.go +3 -3
@@ -29,7 +29,7 @@ func NewCallback(fn any) uintptr {
2929 ty := reflect.TypeOf(fn)
3030 for i := 0; i < ty.NumIn(); i++ {
3131 in := ty.In(i)
32- if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
32+ if !in.AssignableTo(reflect.TypeFor[CDecl]()) {
3333 continue
3434 }
3535 if i != 0 {
@@ -62,7 +62,7 @@ func compileCallback(fn any) uintptr {
6262 in := ty.In(i)
6363 switch in.Kind() {
6464 case reflect.Struct:
65- if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) {
65+ if i == 0 && in.AssignableTo(reflect.TypeFor[CDecl]()) {
6666 continue
6767 }
6868 ensureStructSupported()
@@ -190,7 +190,7 @@ func callbackWrap(a *callbackArgs) {
190190 }
191191 floatsN += slots
192192 case reflect.Struct:
193- if i == 0 && inType.AssignableTo(reflect.TypeOf(CDecl{})) {
193+ if i == 0 && inType.AssignableTo(reflect.TypeFor[CDecl]()) {
194194 args[i] = reflect.Zero(inType)
195195 continue
196196 }
@@ -29,7 +29,7 @@ func NewCallback(fn any) uintptr {
29 ty := reflect.TypeOf(fn)29 ty := reflect.TypeOf(fn)
30 for i := 0; i < ty.NumIn(); i++ {30 for i := 0; i < ty.NumIn(); i++ {
31 in := ty.In(i)31 in := ty.In(i)
32- if !in.AssignableTo(reflect.TypeOf(CDecl{})) {32+ if !in.AssignableTo(reflect.TypeFor[CDecl]()) {
33 continue33 continue
34 }34 }
35 if i != 0 {35 if i != 0 {
@@ -62,7 +62,7 @@ func compileCallback(fn any) uintptr {
62 in := ty.In(i)62 in := ty.In(i)
63 switch in.Kind() {63 switch in.Kind() {
64 case reflect.Struct:64 case reflect.Struct:
65- if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) {65+ if i == 0 && in.AssignableTo(reflect.TypeFor[CDecl]()) {
66 continue66 continue
67 }67 }
68 ensureStructSupported()68 ensureStructSupported()
@@ -190,7 +190,7 @@ func callbackWrap(a *callbackArgs) {
190 }190 }
191 floatsN += slots191 floatsN += slots
192 case reflect.Struct:192 case reflect.Struct:
193- if i == 0 && inType.AssignableTo(reflect.TypeOf(CDecl{})) {193+ if i == 0 && inType.AssignableTo(reflect.TypeFor[CDecl]()) {
194 args[i] = reflect.Zero(inType)194 args[i] = reflect.Zero(inType)
195 continue195 continue
196 }196 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close