detached f0510f1

purego: fix bool field placement at non-zero offsets (#351)

Bool fields were incorrectly placed at bit 0 regardless of their offset
within the struct. This caused incorrect marshalling when bool fields
appeared after other fields.

The fix properly shifts the bool value by the current bit offset,
ensuring it's placed at the correct position in the packed struct data.

Adds regression test with FloatAndBool struct to verify the fix.

Closes #350
commit
f0510f17e9ec8629aac1751cea7f51ee0d1e557f
author
Travis Cline · · 2025-10-27 03:33 UTC
committer
Hajime Hoshi · · 2025-10-27 03:35 UTC
parent
291e8af
signature
unsigned
5 files · +26 -3
M struct_amd64.go +1 -1
@@ -165,7 +165,7 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
165165 place(f)
166166 case reflect.Bool:
167167 if f.Bool() {
168- val |= 1
168+ val |= 1 << shift
169169 }
170170 shift += 8
171171 class |= _INTEGER
@@ -165,7 +165,7 @@ func tryPlaceRegister(v reflect.Value, addFloat func(uintptr), addInt func(uintp
165 place(f)165 place(f)
166 case reflect.Bool:166 case reflect.Bool:
167 if f.Bool() {167 if f.Bool() {
168- val |= 1168+ val |= 1 << shift
169 }169 }
170 shift += 8170 shift += 8
171 class |= _INTEGER171 class |= _INTEGER
M struct_arm64.go +1 -1
@@ -123,7 +123,7 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
123123 place(f)
124124 case reflect.Bool:
125125 if f.Bool() {
126- val |= 1
126+ val |= 1 << shift
127127 }
128128 shift += 8
129129 class |= _INT
@@ -123,7 +123,7 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
123 place(f)123 place(f)
124 case reflect.Bool:124 case reflect.Bool:
125 if f.Bool() {125 if f.Bool() {
126- val |= 1126+ val |= 1 << shift
127 }127 }
128 shift += 8128 shift += 8
129 class |= _INT129 class |= _INT
M struct_loong64.go +1 -1
@@ -104,7 +104,7 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
104104 place(f)
105105 case reflect.Bool:
106106 if f.Bool() {
107- val |= 1
107+ val |= 1 << shift
108108 }
109109 shift += 8
110110 class |= _INT
@@ -104,7 +104,7 @@ func placeRegisters(v reflect.Value, addFloat func(uintptr), addInt func(uintptr
104 place(f)104 place(f)
105 case reflect.Bool:105 case reflect.Bool:
106 if f.Bool() {106 if f.Bool() {
107- val |= 1107+ val |= 1 << shift
108 }108 }
109 shift += 8109 shift += 8
110 class |= _INT110 class |= _INT
M struct_test.go +14 -0
@@ -472,6 +472,20 @@ func TestRegisterFunc_structArgs(t *testing.T) {
472472 t.Fatalf("TakeGoUintAndReturn returned %+v wanted %+v", ret, expected)
473473 }
474474 }
475+ {
476+ type FloatAndBool struct {
477+ x float32
478+ y bool
479+ }
480+ var FloatAndBoolFn func(FloatAndBool) int32
481+ purego.RegisterLibFunc(&FloatAndBoolFn, lib, "FloatAndBool")
482+ if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: true}); ret != 1 {
483+ t.Fatalf("FloatAndBool(y: true) = %d, want 1", ret)
484+ }
485+ if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: false}); ret != 0 {
486+ t.Fatalf("FloatAndBool(y: false) = %d, want 0", ret)
487+ }
488+ }
475489 }
476490
477491 func TestRegisterFunc_structReturns(t *testing.T) {
@@ -472,6 +472,20 @@ func TestRegisterFunc_structArgs(t *testing.T) {
472 t.Fatalf("TakeGoUintAndReturn returned %+v wanted %+v", ret, expected)472 t.Fatalf("TakeGoUintAndReturn returned %+v wanted %+v", ret, expected)
473 }473 }
474 }474 }
475+ {
476+ type FloatAndBool struct {
477+ x float32
478+ y bool
479+ }
480+ var FloatAndBoolFn func(FloatAndBool) int32
481+ purego.RegisterLibFunc(&FloatAndBoolFn, lib, "FloatAndBool")
482+ if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: true}); ret != 1 {
483+ t.Fatalf("FloatAndBool(y: true) = %d, want 1", ret)
484+ }
485+ if ret := FloatAndBoolFn(FloatAndBool{x: 12345.0, y: false}); ret != 0 {
486+ t.Fatalf("FloatAndBool(y: false) = %d, want 0", ret)
487+ }
488+ }
475 }489 }
476 490
477 func TestRegisterFunc_structReturns(t *testing.T) {491 func TestRegisterFunc_structReturns(t *testing.T) {
M testdata/structtest/struct_test.c +9 -0
@@ -352,3 +352,12 @@ GoUint GoUint4(struct GoUint4 g) {
352352 GoUint TakeGoUintAndReturn(GoUint a) {
353353 return a;
354354 }
355+
356+struct FloatAndBool {
357+ float value;
358+ _Bool has_value;
359+};
360+
361+int FloatAndBool(struct FloatAndBool f) {
362+ return f.has_value;
363+}
@@ -352,3 +352,12 @@ GoUint GoUint4(struct GoUint4 g) {
352 GoUint TakeGoUintAndReturn(GoUint a) {352 GoUint TakeGoUintAndReturn(GoUint a) {
353 return a;353 return a;
354 }354 }
355+
356+struct FloatAndBool {
357+ float value;
358+ _Bool has_value;
359+};
360+
361+int FloatAndBool(struct FloatAndBool f) {
362+ return f.has_value;
363+}

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