Skip to content

Commit 0a69663

Browse files
committed
Merge branch '3.1.x' into 3.2.x
2 parents 62ba6b5 + 44e0951 commit 0a69663

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

CHANGES.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ Other changes
515515
(Github issue :issue:`6423`)
516516

517517

518-
3.1.7 (2025-11-??)
518+
3.1.7 (2025-11-11)
519519
==================
520520

521521
Bugs fixed
@@ -526,6 +526,9 @@ Bugs fixed
526526
Also, lone surrogates failed to format in this way.
527527
(Github issue :issue:`7298`)
528528

529+
* Assigning nested structs from a list of structs (item by item) could crash Cython.
530+
(Github issue :issue:`7308`)
531+
529532
* Cython incorrectly called ``PyList_GetItemRef()`` in PyPy and GraalPython before Py3.13.
530533
(Github issue :issue:`7269`)
531534

Cython/Compiler/ExprNodes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9170,9 +9170,12 @@ def coerce_to(self, dst_type, env):
91709170
else:
91719171
if len(self.args) < len(dst_type.scope.var_entries):
91729172
warning(self.pos, "Too few members for '%s'" % dst_type, 1)
9173-
for i, (arg, member) in enumerate(zip(self.original_args, dst_type.scope.var_entries)):
9173+
for i, (arg, coerced_arg, member) in enumerate(zip(self.original_args, self.args, dst_type.scope.var_entries)):
91749174
if isinstance(arg, CoerceToPyTypeNode):
91759175
arg = arg.arg
9176+
elif member.type.is_struct_or_union and coerced_arg.is_dict_literal:
9177+
# For struct assignments, use the coerced dict directly, not a struct type call etc.
9178+
arg = coerced_arg
91769179
self.args[i] = arg.coerce_to(member.type, env)
91779180
self.type = dst_type
91789181
elif dst_type.is_ctuple:

tests/run/cstruct.pyx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,53 @@ def reserved_names():
105105
s2.new = s1.new
106106
s2.case, s2.do = s1.case, s1.do
107107
return (s2.new, s2.case, s2.do)
108+
109+
110+
cdef struct Point:
111+
double x,y
112+
int colour
113+
114+
cdef union int_or_float:
115+
int i
116+
float f
117+
118+
cdef struct NestedStruct:
119+
Point p
120+
int_or_float x
121+
122+
123+
def struct_union_assignments():
124+
"""
125+
>>> struct_union_assignments()
126+
"""
127+
cdef NestedStruct ns
128+
129+
ns = [Point(1.0, 2.0, 42), int_or_float(i=1)]
130+
assert ns.p.colour == 42, ns.p.colour
131+
assert ns.p.x == 1.0, ns.p.x
132+
assert ns.p.y == 2.0, ns.p.y
133+
assert ns.x.i == 1, ns.x.i
134+
135+
ns = NestedStruct(Point(x=2.0, y=3.0, colour=43), int_or_float(i=2))
136+
assert ns.p.colour == 43, ns.p.colour
137+
assert ns.p.x == 2.0, ns.p.x
138+
assert ns.p.y == 3.0, ns.p.y
139+
assert ns.x.i == 2, ns.x.i
140+
141+
ns = NestedStruct(x=int_or_float(i=3), p=Point(colour=44, y=4.0, x=3.0))
142+
assert ns.p.colour == 44, ns.p.colour
143+
assert ns.p.x == 3.0, ns.p.x
144+
assert ns.p.y == 4.0, ns.p.y
145+
assert ns.x.i == 3, ns.x.i
146+
147+
ns = NestedStruct([4.0, 5.0, 45], x=int_or_float(i=4))
148+
assert ns.p.colour == 45, ns.p.colour
149+
assert ns.p.x == 4.0, ns.p.x
150+
assert ns.p.y == 5.0, ns.p.y
151+
assert ns.x.i == 4, ns.x.i
152+
153+
ns = [[4.0, 5.0, 46], int_or_float(i=5)]
154+
assert ns.p.colour == 46, ns.p.colour
155+
assert ns.p.x == 4.0, ns.p.x
156+
assert ns.p.y == 5.0, ns.p.y
157+
assert ns.x.i == 5, ns.x.i

0 commit comments

Comments
 (0)