1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 import jdk.incubator.code.CodeReflection;
 25 import java.util.function.Consumer;
 26 import java.util.function.Supplier;
 27 
 28 /*
 29  * @test
 30  * @summary Smoke test for code reflection with switch expressions.
 31  * @modules jdk.incubator.code
 32  * @enablePreview
 33  * @build SwitchExpressionTest
 34  * @build CodeReflectionTester
 35  * @run main CodeReflectionTester SwitchExpressionTest
 36  */
 37 
 38 public class SwitchExpressionTest {
 39 
 40     @CodeReflection
 41     @IR("""
 42             func @"constantCaseLabelRule" (%0 : java.lang.String)java.lang.Object -> {
 43                 %1 : Var<java.lang.String> = var %0 @"r";
 44                 %2 : java.lang.String = var.load %1;
 45                 %3 : java.lang.Object = java.switch.expression %2
 46                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
 47                         %5 : java.lang.String = constant @"FOO";
 48                         %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
 49                         yield %6;
 50                     }
 51                     ()java.lang.Object -> {
 52                         %7 : java.lang.String = constant @"FOO";
 53                         yield %7;
 54                     }
 55                     ^constantCaseLabel(%8 : java.lang.String)boolean -> {
 56                         %9 : java.lang.String = constant @"BAR";
 57                         %10 : boolean = invoke %8 %9 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
 58                         yield %10;
 59                     }
 60                     ()java.lang.Object -> {
 61                         %11 : java.lang.String = constant @"FOO";
 62                         yield %11;
 63                     }
 64                     ^constantCaseLabel(%12 : java.lang.String)boolean -> {
 65                         %13 : java.lang.String = constant @"BAZ";
 66                         %14 : boolean = invoke %12 %13 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
 67                         yield %14;
 68                     }
 69                     ()java.lang.Object -> {
 70                         %15 : java.lang.String = constant @"FOO";
 71                         yield %15;
 72                     }
 73                     ^defaultCaseLabel()boolean -> {
 74                         %17 : boolean = constant @"true";
 75                         yield %17;
 76                     }
 77                     ()java.lang.Object -> {
 78                         %16 : java.lang.String = constant @"";
 79                         yield %16;
 80                     };
 81                 return %3;
 82             };
 83             """)
 84     public static Object constantCaseLabelRule(String r) {
 85         return switch (r) {
 86             case "FOO" -> "FOO";
 87             case "BAR" -> "FOO";
 88             case "BAZ" -> "FOO";
 89             default -> "";
 90         };
 91     }
 92 
 93     @CodeReflection
 94     @IR("""
 95             func @"constantCaseLabelsRule" (%0 : java.lang.String)java.lang.Object -> {
 96                 %1 : Var<java.lang.String> = var %0 @"r";
 97                 %2 : java.lang.String = var.load %1;
 98                 %3 : java.lang.Object = java.switch.expression %2
 99                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
100                         %5 : boolean = java.cor
101                             ()boolean -> {
102                                 %6 : java.lang.String = constant @"FOO";
103                                 %7 : boolean = invoke %4 %6 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
104                                 yield %7;
105                             }
106                             ()boolean -> {
107                                 %8 : java.lang.String = constant @"BAR";
108                                 %9 : boolean = invoke %4 %8 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
109                                 yield %9;
110                             }
111                             ()boolean -> {
112                                 %10 : java.lang.String = constant @"BAZ";
113                                 %11 : boolean = invoke %4 %10 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
114                                 yield %11;
115                             };
116                         yield %5;
117                     }
118                     ()java.lang.Object -> {
119                         %12 : java.lang.String = constant @"FOO";
120                         yield %12;
121                     }
122                     ^defaultCaseLabel()boolean -> {
123                         %14 : boolean = constant @"true";
124                         yield %14;
125                     }
126                     ()java.lang.Object -> {
127                         %13 : java.lang.String = constant @"";
128                         java.yield %13;
129                     };
130                 return %3;
131             };
132             """)
133     public static Object constantCaseLabelsRule(String r) {
134         return switch (r) {
135             case "FOO", "BAR", "BAZ" -> "FOO";
136             default -> {
137                 yield "";
138             }
139         };
140     }
141 
142     @CodeReflection
143     @IR("""
144             func @"constantCaseLabelStatement" (%0 : java.lang.String)java.lang.Object -> {
145                 %1 : Var<java.lang.String> = var %0 @"r";
146                 %2 : java.lang.String = var.load %1;
147                 %3 : java.lang.Object = java.switch.expression %2
148                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
149                         %5 : java.lang.String = constant @"FOO";
150                         %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
151                         yield %6;
152                     }
153                     ()java.lang.Object -> {
154                         %7 : java.lang.String = constant @"FOO";
155                         java.yield %7;
156                     }
157                     ^constantCaseLabel(%8 : java.lang.String)boolean -> {
158                         %9 : java.lang.String = constant @"BAR";
159                         %10 : boolean = invoke %8 %9 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
160                         yield %10;
161                     }
162                     ()java.lang.Object -> {
163                         %11 : java.lang.String = constant @"FOO";
164                         java.yield %11;
165                     }
166                     ^constantCaseLabel(%12 : java.lang.String)boolean -> {
167                         %13 : java.lang.String = constant @"BAZ";
168                         %14 : boolean = invoke %12 %13 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
169                         yield %14;
170                     }
171                     ()java.lang.Object -> {
172                         %15 : java.lang.String = constant @"FOO";
173                         java.yield %15;
174                     }
175                     ^defaultCaseLabel()boolean -> {
176                         %17 : boolean = constant @"true";
177                         yield %17;
178                     }
179                     ()java.lang.Object -> {
180                         %16 : java.lang.String = constant @"";
181                         java.yield %16;
182                     };
183                 return %3;
184             };
185             """)
186     public static Object constantCaseLabelStatement(String r) {
187         return switch (r) {
188             case "FOO" : yield "FOO";
189             case "BAR" : yield "FOO";
190             case "BAZ" : yield "FOO";
191             default : yield "";
192         };
193     }
194 
195     @CodeReflection
196     @IR("""
197             func @"constantCaseLabelsStatement" (%0 : java.lang.String)java.lang.Object -> {
198                 %1 : Var<java.lang.String> = var %0 @"r";
199                 %2 : java.lang.String = var.load %1;
200                 %3 : java.lang.Object = java.switch.expression %2
201                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
202                         %5 : boolean = java.cor
203                             ()boolean -> {
204                                 %6 : java.lang.String = constant @"FOO";
205                                 %7 : boolean = invoke %4 %6 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
206                                 yield %7;
207                             }
208                             ()boolean -> {
209                                 %8 : java.lang.String = constant @"BAR";
210                                 %9 : boolean = invoke %4 %8 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
211                                 yield %9;
212                             }
213                             ()boolean -> {
214                                 %10 : java.lang.String = constant @"BAZ";
215                                 %11 : boolean = invoke %4 %10 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
216                                 yield %11;
217                             };
218                         yield %5;
219                     }
220                     ()java.lang.Object -> {
221                         %12 : java.lang.String = constant @"FOO";
222                         java.yield %12;
223                     }
224                     ^defaultCaseLabel()boolean -> {
225                         %17 : boolean = constant @"true";
226                         yield %17;
227                     }
228                     ()java.lang.Object -> {
229                         java.block ()void -> {
230                             %13 : java.lang.String = constant @"";
231                             java.yield %13;
232                         };
233                         unreachable;
234                     };
235                 return %3;
236             };
237             """)
238     public static Object constantCaseLabelsStatement(String r) {
239         return switch (r) {
240             case "FOO", "BAR", "BAZ" : yield "FOO";
241             default : { yield ""; }
242         };
243     }
244 
245     @CodeReflection
246     @IR("""
247             func @"constantCaseLabelStatements" (%0 : java.lang.String)java.lang.Object -> {
248                 %1 : Var<java.lang.String> = var %0 @"r";
249                 %2 : java.lang.String = var.load %1;
250                 %3 : java.lang.Object = java.switch.expression %2
251                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
252                         %5 : java.lang.String = constant @"FOO";
253                         %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
254                         yield %6;
255                     }
256                     ()java.lang.Object -> {
257                         java.block ()void -> {
258                             %7 : java.io.PrintStream = field.load @"java.lang.System::out()java.io.PrintStream";
259                             %8 : java.lang.String = constant @"FOO";
260                             invoke %7 %8 @"java.io.PrintStream::println(java.lang.String)void";
261                             yield;
262                         };
263                         java.block ()void -> {
264                             %9 : java.lang.String = constant @"FOO";
265                             java.yield %9;
266                         };
267                         unreachable;
268                     }
269                     ^defaultCaseLabel()boolean -> {
270                         %11 : boolean = constant @"true";
271                         yield %11;
272                     }
273                     ()java.lang.Object -> {
274                         %10 : java.lang.String = constant @"";
275                         java.yield %10;
276                     };
277                 return %3;
278             };
279             """)
280     public static Object constantCaseLabelStatements(String r) {
281         return switch (r) {
282             case "FOO" : {
283                 System.out.println("FOO");
284             }
285             {
286                 yield "FOO";
287             }
288             default : yield "";
289         };
290     }
291 
292     @CodeReflection
293     @IR("""
294             func @"constantCaseLabelFallthrough" (%0 : java.lang.String)java.lang.Object -> {
295                 %1 : Var<java.lang.String> = var %0 @"r";
296                 %2 : java.lang.String = var.load %1;
297                 %3 : java.lang.Object = java.switch.expression %2
298                     ^constantCaseLabel(%4 : java.lang.String)boolean -> {
299                         %5 : java.lang.String = constant @"FOO";
300                         %6 : boolean = invoke %4 %5 @"java.util.Objects::equals(java.lang.Object, java.lang.Object)boolean";
301                         yield %6;
302                     }
303                     ()java.lang.Object -> {
304                         java.block ()void -> {
305                             %7 : java.io.PrintStream = field.load @"java.lang.System::out()java.io.PrintStream";
306                             %8 : java.lang.String = constant @"FOO";
307                             invoke %7 %8 @"java.io.PrintStream::println(java.lang.String)void";
308                             yield;
309                         };
310                         java.switch.fallthrough;
311                     }
312                     ^defaultCaseLabel()boolean -> {
313                         %10 : boolean = constant @"true";
314                         yield %10;
315                     }
316                     ()java.lang.Object -> {
317                         %9 : java.lang.String = constant @"";
318                         java.yield %9;
319                     };
320                 return %3;
321             };
322             """)
323     public static Object constantCaseLabelFallthrough(String r) {
324         return switch (r) {
325             case "FOO" : {
326                 System.out.println("FOO");
327             }
328             default : yield "";
329         };
330     }
331 
332     record A(Number n) {
333     }
334 
335     @CodeReflection
336     @IR("""
337             func @"patternCaseLabel" (%0 : java.lang.Object)java.lang.Object -> {
338                 %1 : Var<java.lang.Object> = var %0 @"r";
339                 %2 : java.lang.Object = var.load %1;
340                 %3 : java.lang.Number = constant @null;
341                 %4 : Var<java.lang.Number> = var %3 @"n";
342                 %5 : java.lang.String = constant @null;
343                 %6 : Var<java.lang.String> = var %5 @"s";
344                 %7 : java.lang.Object = java.switch.expression %2
345                     ^patternCaseLabel(%8 : java.lang.Object)boolean -> {
346                         %9 : boolean = pattern.match %8
347                             ^pattern()jdk.incubator.code.op.ExtendedOp$Pattern$Record<SwitchExpressionTest$A> -> {
348                                 %10 : jdk.incubator.code.op.ExtendedOp$Pattern$Type<java.lang.Number> = pattern.type @"n";
349                                 %11 : jdk.incubator.code.op.ExtendedOp$Pattern$Record<SwitchExpressionTest$A> = pattern.record %10 @"(java.lang.Number n)SwitchExpressionTest$A";
350                                 yield %11;
351                             }
352                             ^match(%12 : java.lang.Number)void -> {
353                                 var.store %4 %12;
354                                 yield;
355                             };
356                         yield %9;
357                     }
358                     ()java.lang.Object -> {
359                         %13 : java.lang.Number = var.load %4;
360                         java.yield %13;
361                     }
362                     ^patternCaseLabel(%14 : java.lang.Object)boolean -> {
363                         %15 : boolean = pattern.match %14
364                             ^pattern()jdk.incubator.code.op.ExtendedOp$Pattern$Type<java.lang.String> -> {
365                                 %16 : jdk.incubator.code.op.ExtendedOp$Pattern$Type<java.lang.String> = pattern.type @"s";
366                                 yield %16;
367                             }
368                             ^match(%17 : java.lang.String)void -> {
369                                 var.store %6 %17;
370                                 yield;
371                             };
372                         yield %15;
373                     }
374                     ()java.lang.Object -> {
375                         %18 : java.lang.String = var.load %6;
376                         java.yield %18;
377                     }
378                     ^defaultCaseLabel()boolean -> {
379                         %10 : boolean = constant @"true";
380                         yield %10;
381                     }
382                     ()java.lang.Object -> {
383                         %19 : java.lang.String = constant @"";
384                         java.yield %19;
385                     };
386                 return %7;
387             };
388             """)
389     public static Object patternCaseLabel(Object r) {
390         return switch (r) {
391             case A(Number n) -> {
392                 yield n;
393             }
394             case String s -> {
395                 yield s;
396             }
397             default -> {
398                 yield "";
399             }
400         };
401     }
402 
403     @CodeReflection
404     @IR("""
405             func @"patternCaseLabelGuard" (%0 : java.lang.Object)java.lang.Object -> {
406                 %1 : Var<java.lang.Object> = var %0 @"r";
407                 %2 : java.lang.Object = var.load %1;
408                 %3 : java.lang.Number = constant @null;
409                 %4 : Var<java.lang.Number> = var %3 @"n";
410                 %5 : java.lang.String = constant @null;
411                 %6 : Var<java.lang.String> = var %5 @"s";
412                 %7 : java.lang.String = constant @null;
413                 %8 : Var<java.lang.String> = var %7 @"s";
414                 %9 : java.lang.Object = java.switch.expression %2
415                     ^patternCaseLabel(%10 : java.lang.Object)boolean -> {
416                         %11 : boolean = pattern.match %10
417                             ^pattern()jdk.incubator.code.op.ExtendedOp$Pattern$Record<SwitchExpressionTest$A> -> {
418                                 %12 : jdk.incubator.code.op.ExtendedOp$Pattern$Type<java.lang.Number> = pattern.type @"n";
419                                 %13 : jdk.incubator.code.op.ExtendedOp$Pattern$Record<SwitchExpressionTest$A> = pattern.record %12 @"(java.lang.Number n)SwitchExpressionTest$A";
420                                 yield %13;
421                             }
422                             ^match(%14 : java.lang.Number)void -> {
423                                 var.store %4 %14;
424                                 yield;
425                             };
426                         yield %11;
427                     }
428                     ()java.lang.Object -> {
429                         %15 : java.lang.Number = var.load %4;
430                         java.yield %15;
431                     }
432                     ^patternCaseLabel(%16 : java.lang.Object)boolean -> {
433                         %17 : boolean = java.cand
434                             ()boolean -> {
435                                 %18 : boolean = pattern.match %16
436                                     ^pattern()jdk.incubator.code.op.ExtendedOp$Pattern$Type<java.lang.String> -> {
437                                         %19 : jdk.incubator.code.op.ExtendedOp$Pattern$Type<java.lang.String> = pattern.type @"s";
438                                         yield %19;
439                                     }
440                                     ^match(%20 : java.lang.String)void -> {
441                                         var.store %6 %20;
442                                         yield;
443                                     };
444                                 yield %18;
445                             }
446                             ()boolean -> {
447                                 %21 : java.lang.String = var.load %6;
448                                 %22 : int = invoke %21 @"java.lang.String::length()int";
449                                 %23 : int = constant @"5";
450                                 %24 : boolean = lt %22 %23;
451                                 yield %24;
452                             };
453                         yield %17;
454                     }
455                     ()java.lang.Object -> {
456                         %25 : java.lang.String = var.load %6;
457                         java.yield %25;
458                     }
459                     ^patternCaseLabel(%26 : java.lang.Object)boolean -> {
460                         %27 : boolean = java.cand
461                             ()boolean -> {
462                                 %28 : boolean = pattern.match %26
463                                     ^pattern()jdk.incubator.code.op.ExtendedOp$Pattern$Type<java.lang.String> -> {
464                                         %29 : jdk.incubator.code.op.ExtendedOp$Pattern$Type<java.lang.String> = pattern.type @"s";
465                                         yield %29;
466                                     }
467                                     ^match(%30 : java.lang.String)void -> {
468                                         var.store %8 %30;
469                                         yield;
470                                     };
471                                 yield %28;
472                             }
473                             ()boolean -> {
474                                 %31 : java.lang.String = var.load %8;
475                                 %32 : int = invoke %31 @"java.lang.String::length()int";
476                                 %33 : int = constant @"10";
477                                 %34 : boolean = lt %32 %33;
478                                 yield %34;
479                             };
480                         yield %27;
481                     }
482                     ()java.lang.Object -> {
483                         %35 : java.lang.String = var.load %8;
484                         java.yield %35;
485                     }
486                     ^defaultCaseLabel()boolean -> {
487                         %37 : boolean = constant @"true";
488                         yield %37;
489                     }
490                     ()java.lang.Object -> {
491                         %36 : java.lang.String = constant @"";
492                         java.yield %36;
493                     };
494                 return %9;
495             };
496             """)
497     public static Object patternCaseLabelGuard(Object r) {
498         return switch (r) {
499             case A(Number n) -> {
500                 yield n;
501             }
502             case String s when s.length() < 5 -> {
503                 yield s;
504             }
505             case String s when s.length() < 10 -> {
506                 yield s;
507             }
508             default -> {
509                 yield "";
510             }
511         };
512     }
513 }