@@ -3686,6 +3686,99 @@ TEST_F(LLVMCodeBuilderTest, ProcedureThreadStop_NonWarp)
36863686 ASSERT_TRUE (code->isFinished (ctx.get ()));
36873687}
36883688
3689+ TEST_F (LLVMCodeBuilderTest, ProcedureThreadStop_NonWarp_AfterYield)
3690+ {
3691+ Sprite sprite;
3692+
3693+ // Inner procedure (proc2): yields via a repeat loop, then stops the thread
3694+ // This exercises the coroutine resume path where the sentinel must propagate
3695+ BlockPrototype prototype2;
3696+ prototype2.setProcCode (" proc2" );
3697+ prototype2.setWarp (false );
3698+
3699+ LLVMCodeBuilder *builder = m_utils.createBuilder (&sprite, &prototype2);
3700+ CompilerValue *v = builder->addConstValue (" inner_before" );
3701+ builder->addFunctionCall (" test_print_string" , Compiler::StaticType::Void, { Compiler::StaticType::String }, { v });
3702+
3703+ // This repeat loop causes the coroutine to suspend (yield) on each iteration
3704+ v = builder->addConstValue (2 );
3705+ builder->beginRepeatLoop (v);
3706+ {
3707+ v = builder->addConstValue (" inner_loop" );
3708+ builder->addFunctionCall (" test_print_string" , Compiler::StaticType::Void, { Compiler::StaticType::String }, { v });
3709+ }
3710+ builder->endLoop ();
3711+
3712+ // After the loop completes, stop the thread
3713+ builder->createThreadStop ();
3714+
3715+ // This should NOT execute
3716+ v = builder->addConstValue (" inner_after" );
3717+ builder->addFunctionCall (" test_print_string" , Compiler::StaticType::Void, { Compiler::StaticType::String }, { v });
3718+
3719+ auto proc2Code = builder->build ();
3720+
3721+ // Outer procedure (proc1): calls proc2
3722+ BlockPrototype prototype1;
3723+ prototype1.setProcCode (" proc1" );
3724+ prototype1.setWarp (false );
3725+
3726+ builder = m_utils.createBuilder (&sprite, &prototype1);
3727+
3728+ v = builder->addConstValue (" outer_before" );
3729+ builder->addFunctionCall (" test_print_string" , Compiler::StaticType::Void, { Compiler::StaticType::String }, { v });
3730+ builder->createProcedureCall (&prototype2, {});
3731+
3732+ // This should NOT execute (thread was stopped by proc2)
3733+ v = builder->addConstValue (" outer_after" );
3734+ builder->addFunctionCall (" test_print_string" , Compiler::StaticType::Void, { Compiler::StaticType::String }, { v });
3735+ auto proc1Code = builder->build ();
3736+
3737+ // Root script: calls proc1
3738+ builder = m_utils.createBuilder (&sprite, false );
3739+ v = builder->addConstValue (" script_before" );
3740+ builder->addFunctionCall (" test_print_string" , Compiler::StaticType::Void, { Compiler::StaticType::String }, { v });
3741+ builder->createProcedureCall (&prototype1, {});
3742+
3743+ // This should NOT execute (thread was stopped by proc2 via proc1)
3744+ v = builder->addConstValue (" script_after" );
3745+ builder->addFunctionCall (" test_print_string" , Compiler::StaticType::Void, { Compiler::StaticType::String }, { v });
3746+
3747+ auto code = builder->build ();
3748+ Script script (&sprite, nullptr , nullptr );
3749+ script.setCode (code);
3750+
3751+ Thread thread (&sprite, nullptr , &script);
3752+ auto ctx = code->createExecutionContext (&thread);
3753+
3754+ // First run: enters the repeat loop in proc2, yields after first iteration
3755+ std::string expected1 =
3756+ " script_before\n "
3757+ " outer_before\n "
3758+ " inner_before\n "
3759+ " inner_loop\n " ;
3760+
3761+ testing::internal::CaptureStdout ();
3762+ code->run (ctx.get ());
3763+ ASSERT_EQ (testing::internal::GetCapturedStdout (), expected1);
3764+ ASSERT_FALSE (code->isFinished (ctx.get ()));
3765+
3766+ // Second run: second iteration of the repeat loop, yields again
3767+ std::string expected2 = " inner_loop\n " ;
3768+ testing::internal::CaptureStdout ();
3769+ code->run (ctx.get ());
3770+ ASSERT_EQ (testing::internal::GetCapturedStdout (), expected2);
3771+ ASSERT_FALSE (code->isFinished (ctx.get ()));
3772+
3773+ // Third run: loop is done, createThreadStop() fires, sentinel must propagate
3774+ // through the coroutine resume path back to the caller
3775+ // Neither "inner_after", "outer_after", nor "script_after" should print
3776+ testing::internal::CaptureStdout ();
3777+ code->run (ctx.get ());
3778+ ASSERT_EQ (testing::internal::GetCapturedStdout (), " " );
3779+ ASSERT_TRUE (code->isFinished (ctx.get ()));
3780+ }
3781+
36893782TEST_F (LLVMCodeBuilderTest, HatPredicates)
36903783{
36913784 Sprite sprite;
0 commit comments