@@ -282,6 +282,95 @@ public void tupleFieldReadsOnlyLoadTheSelectedStorageComponent() throws IOExcept
282282 || memberRead .contains ("__wurst_tuple_discard_" ));
283283 }
284284
285+ @ Test
286+ public void tupleFieldAssignmentsOnlyTouchTheSelectedStorageComponent () throws IOException {
287+ String [] source = {
288+ "package Test" ,
289+ "native testSuccess()" ,
290+ "tuple vec3(real x, real y, real z)" ,
291+ "tuple segment(vec3 start, vec3 finish)" ,
292+ "vec3 array points" ,
293+ "segment array segments" ,
294+ "int indexCalls" ,
295+ "int trace" ,
296+ "@noinline function nextIndex() returns int" ,
297+ " indexCalls++" ,
298+ " trace = trace * 10 + 1" ,
299+ " return 2" ,
300+ "@noinline function nextValue() returns real" ,
301+ " trace = trace * 10 + 2" ,
302+ " return 21." ,
303+ "@noinline function setAt(int index, real value)" ,
304+ " points[index].z = value" ,
305+ "@noinline function setAtNext()" ,
306+ " points[nextIndex()].y = nextValue()" ,
307+ "@noinline function addAt(int index, real value)" ,
308+ " points[index].x += value" ,
309+ "@noinline function setNested(int index, real value)" ,
310+ " segments[index].finish.y = value" ,
311+ "@noinline function replaceAt(int index, vec3 value)" ,
312+ " points[index] = value" ,
313+ "class Entity" ,
314+ " vec3 pos" ,
315+ "@noinline function setPos(Entity entity, real value)" ,
316+ " entity.pos.z = value" ,
317+ "init" ,
318+ " points[1] = vec3(1., 2., 3.)" ,
319+ " points[2] = vec3(4., 5., 6.)" ,
320+ " segments[1] = segment(vec3(7., 8., 9.), vec3(10., 11., 12.))" ,
321+ " let entity = new Entity()" ,
322+ " entity.pos = vec3(13., 14., 15.)" ,
323+ " setAt(1, 20.)" ,
324+ " setAtNext()" ,
325+ " addAt(1, 22.)" ,
326+ " setNested(1, 23.)" ,
327+ " setPos(entity, 24.)" ,
328+ " replaceAt(2, vec3(25., 26., 27.))" ,
329+ " if points[1] == vec3(23., 2., 20.) and points[2] == vec3(25., 26., 27.)" ,
330+ " and segments[1] == segment(vec3(7., 8., 9.), vec3(10., 23., 12.))" ,
331+ " and entity.pos == vec3(13., 14., 24.) and indexCalls == 1 and trace == 12" ,
332+ " testSuccess()"
333+ };
334+ test ().testLua (true ).executeProg ().lines (source );
335+
336+ String compiled = compileOptimizedLua (
337+ "tupleFieldAssignmentsOnlyTouchTheSelectedStorageComponentOptimized" , source );
338+ String plainWrite = luaFunctionBody (compiled , "setAt" );
339+ String effectfulWrite = luaFunctionBody (compiled , "setAtNext" );
340+ String readModifyWrite = luaFunctionBody (compiled , "addAt" );
341+ String nestedWrite = luaFunctionBody (compiled , "setNested" );
342+ String memberWrite = luaFunctionBody (compiled , "setPos" );
343+ String wholeTupleWrite = luaFunctionBody (compiled , "replaceAt" );
344+
345+ assertTrue (plainWrite .contains ("points_z[" ));
346+ assertFalse (plainWrite .contains ("points_x[" ));
347+ assertFalse (plainWrite .contains ("points_y[" ));
348+ assertTrue (effectfulWrite .contains ("points_y[" ));
349+ assertFalse (effectfulWrite .contains ("points_x[" ));
350+ assertFalse (effectfulWrite .contains ("points_z[" ));
351+ assertEquals ("a selected tuple-array field must evaluate its write index exactly once" ,
352+ 1 , effectfulWrite .split ("nextIndex\\ (" , -1 ).length - 1 );
353+ assertTrue (readModifyWrite .contains ("points_x[" ));
354+ assertFalse (readModifyWrite .contains ("points_y[" ));
355+ assertFalse (readModifyWrite .contains ("points_z[" ));
356+ assertTrue (nestedWrite .contains ("segments_finish_y[" ));
357+ assertFalse (nestedWrite .contains ("segments_start_" ));
358+ assertFalse (nestedWrite .contains ("segments_finish_x[" ));
359+ assertFalse (nestedWrite .contains ("segments_finish_z[" ));
360+ assertTrue (memberWrite .contains ("Entity_pos_z_storage[" ));
361+ assertFalse (memberWrite .contains ("Entity_pos_x_storage[" ));
362+ assertFalse (memberWrite .contains ("Entity_pos_y_storage[" ));
363+ assertTrue ("whole-tuple assignment must still write x" , wholeTupleWrite .contains ("points_x[" ));
364+ assertTrue ("whole-tuple assignment must still write y" , wholeTupleWrite .contains ("points_y[" ));
365+ assertTrue ("whole-tuple assignment must still write z" , wholeTupleWrite .contains ("points_z[" ));
366+ assertFalse ("selected tuple storage writes must not need discard helpers" ,
367+ plainWrite .contains ("__wurst_tuple_discard_" )
368+ || effectfulWrite .contains ("__wurst_tuple_discard_" )
369+ || readModifyWrite .contains ("__wurst_tuple_discard_" )
370+ || nestedWrite .contains ("__wurst_tuple_discard_" )
371+ || memberWrite .contains ("__wurst_tuple_discard_" ));
372+ }
373+
285374 @ Test
286375 public void tupleMemberArrayAssignmentCapturesIndex () throws IOException {
287376 test ().testLua (true ).luaOnly (false ).executeProg ().lines (
0 commit comments