@@ -87,6 +87,51 @@ public void dispatchNarrowingUsesStaticReceiverTypeInJass() throws IOException {
8787 "Call site should not use full A dispatch for static receiver type C." );
8888 }
8989
90+ @ Test
91+ public void dispatchNarrowingOnInterfaceSubtypeKeepsSemantics () throws IOException {
92+ testAssertOkLines (true ,
93+ "package test" ,
94+ " native testSuccess()" ,
95+ "" ,
96+ " interface A" ,
97+ " function f1() returns int" ,
98+ "" ,
99+ " interface B extends A" ,
100+ " function f2() returns int" ,
101+ "" ,
102+ " class C implements B" ,
103+ " override function f1() returns int" ,
104+ " return 3" ,
105+ " override function f2() returns int" ,
106+ " return 4" ,
107+ "" ,
108+ " function useB(B b) returns int" ,
109+ " return b.f1()" ,
110+ "" ,
111+ " function useA(A a) returns int" ,
112+ " return a.f1()" ,
113+ "" ,
114+ " init" ,
115+ " B b = new C()" ,
116+ " A a = b" ,
117+ " if useB(b) == 3 and useA(a) == 3" ,
118+ " testSuccess()" ,
119+ "endpackage"
120+ );
121+
122+ File output = new File (TEST_OUTPUT_PATH + "ClassesTests_dispatchNarrowingOnInterfaceSubtypeKeepsSemantics_no_opts.j" );
123+ String jass = Files .readString (output .toPath (), StandardCharsets .UTF_8 );
124+
125+ assertTrue (jass .contains ("function useB takes integer b returns integer" ),
126+ "Expected useB helper in generated jass." );
127+ assertTrue (jass .contains ("return dispatch_narrow_B_A_A_f1(b)" ),
128+ "Expected B-typed call site to use narrowed dispatch." );
129+ assertTrue (jass .contains ("function useA takes integer a returns integer" ),
130+ "Expected useA helper in generated jass." );
131+ assertTrue (jass .contains ("return dispatch_A_A_f1(a)" ),
132+ "Expected A-typed call site to use regular A dispatch." );
133+ }
134+
90135 @ Test
91136 public void repeatedCallsOnSameReceiverAreNotDispatchCachedYet () throws IOException {
92137 testAssertOkLines (true ,
@@ -170,6 +215,34 @@ public void dispatchGuardNotDedupedAcrossRhsSideEffects() throws IOException {
170215 "Expected inlopt output to keep separate guards across mutating RHS call, found " + inlOptGuardCount );
171216 }
172217
218+ @ Test (expectedExceptions = DebugPrintError .class )
219+ public void dispatchGuardAcrossRhsSideEffectsStillThrowsAtRuntime () {
220+ test ().executeProg ()
221+ .executeProgOnlyAfterTransforms ()
222+ .lines (
223+ "package test" ,
224+ " class A" ,
225+ " function bar() returns int" ,
226+ " return 1" ,
227+ "" ,
228+ " function mutatingRhs(A a) returns int" ,
229+ " destroy a" ,
230+ " return 0" ,
231+ "" ,
232+ " function useA(A a) returns int" ,
233+ " int r = 0" ,
234+ " r += a.bar()" ,
235+ " r = mutatingRhs(a)" ,
236+ " r += a.bar()" ,
237+ " return r" ,
238+ "" ,
239+ " init" ,
240+ " let a = new A" ,
241+ " useA(a)" ,
242+ "endpackage"
243+ );
244+ }
245+
173246 @ Test
174247 public void dispatchGuardNotDedupedAcrossNestedMutatingExprStatement () throws IOException {
175248 testAssertOkLines (false ,
@@ -204,6 +277,34 @@ public void dispatchGuardNotDedupedAcrossNestedMutatingExprStatement() throws IO
204277 "Expected inlopt output to keep separate guards across nested mutating expr statement, found " + inlOptGuardCount );
205278 }
206279
280+ @ Test (expectedExceptions = DebugPrintError .class )
281+ public void dispatchGuardAcrossNestedMutatingExprStatementStillThrowsAtRuntime () {
282+ test ().executeProg ()
283+ .executeProgOnlyAfterTransforms ()
284+ .lines (
285+ "package test" ,
286+ " class A" ,
287+ " function bar() returns int" ,
288+ " return 1" ,
289+ "" ,
290+ " function mutatingRhs(A a) returns int" ,
291+ " destroy a" ,
292+ " return 0" ,
293+ "" ,
294+ " function useA(A a) returns int" ,
295+ " int r = 0" ,
296+ " r += a.bar()" ,
297+ " int unused = mutatingRhs(a) + 0" ,
298+ " r += a.bar()" ,
299+ " return r" ,
300+ "" ,
301+ " init" ,
302+ " let a = new A" ,
303+ " useA(a)" ,
304+ "endpackage"
305+ );
306+ }
307+
207308 private static int countOccurrences (String text , String needle ) {
208309 int c = 0 ;
209310 int i = 0 ;
0 commit comments