1 /*
   2  * Copyright (c) 2019, 2023, 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 /**
  25  * RecordCompilationTests
  26  *
  27  * @test
  28  * @bug 8250629 8252307 8247352 8241151 8246774 8259025 8288130 8282714 8289647 8294020
  29  * @summary Negative compilation tests, and positive compilation (smoke) tests for records
  30  * @library /lib/combo /tools/lib /tools/javac/lib
  31  * @modules
  32  *      jdk.compiler/com.sun.tools.javac.api
  33  *      jdk.compiler/com.sun.tools.javac.code
  34  *      jdk.compiler/com.sun.tools.javac.util
  35  *      java.base/jdk.internal.classfile
  36  *      java.base/jdk.internal.classfile.attribute
  37  *      java.base/jdk.internal.classfile.constantpool
  38  *      java.base/jdk.internal.classfile.instruction
  39  *      java.base/jdk.internal.classfile.components
  40  *      java.base/jdk.internal.classfile.impl
  41  * @build JavacTestingAbstractProcessor
  42  * @run junit/othervm -DuseAP=false RecordCompilationTests
  43  * @run junit/othervm -DuseAP=true RecordCompilationTests
  44  */
  45 
  46 import java.io.File;
  47 
  48 import java.lang.annotation.ElementType;
  49 import java.util.*;
  50 import java.util.stream.Collectors;
  51 import java.util.stream.Stream;
  52 
  53 
  54 import com.sun.tools.javac.util.Assert;
  55 
  56 import javax.annotation.processing.AbstractProcessor;
  57 import javax.annotation.processing.RoundEnvironment;
  58 import javax.annotation.processing.SupportedAnnotationTypes;
  59 
  60 import javax.lang.model.element.AnnotationMirror;
  61 import javax.lang.model.element.AnnotationValue;
  62 import javax.lang.model.element.Element;
  63 import javax.lang.model.element.ElementKind;
  64 import javax.lang.model.element.ExecutableElement;
  65 import javax.lang.model.element.RecordComponentElement;
  66 import javax.lang.model.element.TypeElement;
  67 import javax.lang.model.element.VariableElement;
  68 
  69 import javax.lang.model.type.ArrayType;
  70 import javax.lang.model.type.TypeMirror;
  71 
  72 import jdk.internal.classfile.*;
  73 import jdk.internal.classfile.attribute.*;
  74 import jdk.internal.classfile.Opcode;
  75 import jdk.internal.classfile.constantpool.*;
  76 import jdk.internal.classfile.instruction.FieldInstruction;
  77 
  78 import com.sun.tools.javac.api.ClientCodeWrapper.DiagnosticSourceUnwrapper;
  79 import com.sun.tools.javac.code.Attribute.TypeCompound;
  80 import com.sun.tools.javac.code.Symbol;
  81 import com.sun.tools.javac.code.Symbol.VarSymbol;
  82 import com.sun.tools.javac.util.JCDiagnostic;
  83 
  84 import tools.javac.combo.CompilationTestCase;
  85 import org.junit.jupiter.api.Test;
  86 
  87 import static java.lang.annotation.ElementType.*;
  88 
  89 /** Records are the first feature which sports automatic injection of (declarative and type) annotations : from a
  90  *  given record component to one or more record members, if applicable.
  91  *  This implies that the record's implementation can be stressed with the presence of annotation processors. Which is
  92  *  something the implementator could easily skip. For this reason this test is executed twice, once without the
  93  *  presence of any annotation processor and one with a simple annotation processor (which does not annotation processing
  94  *  at all) just to force at least a round of annotation processing.
  95  *
  96  *  Tests needing special compilation options need to store current options, set its customs options by invoking method
  97  *  `setCompileOptions` and then reset the previous compilation options for other tests. To see an example of this check
  98  *  method: testAnnos()
  99  */
 100 
 101 class RecordCompilationTests extends CompilationTestCase {
 102     private static String[] OPTIONS_WITH_AP = {"-processor", SimplestAP.class.getName()};
 103 
 104     private static final List<String> BAD_COMPONENT_NAMES = List.of(
 105             "clone", "finalize", "getClass", "hashCode",
 106             "notify", "notifyAll", "toString", "wait");
 107 
 108     /* simplest annotation processor just to force a round of annotation processing for all tests
 109      */
 110     @SupportedAnnotationTypes("*")
 111     public static class SimplestAP extends AbstractProcessor {
 112         @Override
 113         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 114             return true;
 115         }
 116     }
 117 
 118     boolean useAP;
 119 
 120     public RecordCompilationTests() {
 121         useAP = System.getProperty("useAP", "false").equals("true");
 122         setDefaultFilename("R.java");
 123         if (useAP) {
 124             setCompileOptions(OPTIONS_WITH_AP);
 125         }
 126         System.out.println(useAP ? "running all tests using an annotation processor" : "running all tests without annotation processor");
 127     }
 128 
 129     @Test
 130     void testMalformedDeclarations() {
 131         assertFail("compiler.err.premature.eof", "record R()");
 132         assertFail("compiler.err.expected", "record R();");
 133         assertFail("compiler.err.illegal.start.of.type", "record R(,) { }");
 134         assertFail("compiler.err.illegal.start.of.type", "record R((int x)) { }");
 135         assertFail("compiler.err.expected", "record R { }");
 136         assertFail("compiler.err.expected", "record R(foo) { }");
 137         assertFail("compiler.err.expected", "record R(int int) { }");
 138         assertFail("compiler.err.mod.not.allowed.here", "abstract record R(String foo) { }");
 139         //assertFail("compiler.err.illegal.combination.of.modifiers", "non-sealed record R(String foo) { }");
 140         assertFail("compiler.err.repeated.modifier", "public public record R(String foo) { }");
 141         assertFail("compiler.err.repeated.modifier", "private private record R(String foo) { }");
 142         assertFail("compiler.err.already.defined", "record R(int x, int x) {}");
 143         for (String s : List.of("var", "record"))
 144             assertFail("compiler.err.restricted.type.not.allowed.here", "record R(# x) { }", s);
 145         for (String s : List.of("public", "protected", "private", "static", "final", "transient", "volatile",
 146                 "abstract", "synchronized", "native", "strictfp")) // missing: sealed and non-sealed
 147             assertFail("compiler.err.record.cant.declare.field.modifiers", "record R(# String foo) { }", s);
 148         assertFail("compiler.err.varargs.must.be.last", "record R(int... x, int... y) {}");
 149         assertFail("compiler.err.instance.initializer.not.allowed.in.records", "record R(int i) { {} }");
 150     }
 151 
 152     @Test
 153     void testGoodDeclarations() {
 154         assertOK("public record R() { }");
 155         assertOK("record R() { }");
 156         assertOK("record R() implements java.io.Serializable, Runnable { public void run() { } }");
 157         assertOK("record R(int x) { }");
 158         assertOK("record R(int x, int y) { }");
 159         assertOK("record R(int... xs) { }");
 160         assertOK("record R(String... ss) { }");
 161         assertOK("@Deprecated record R(int x, int y) { }");
 162         assertOK("record R(@Deprecated int x, int y) { }");
 163         assertOK("record R<T>(T x, T y) { }");
 164         assertOK(
 165                 """
 166                 record R<T>(T x) {
 167                     public T x() {
 168                         return this.x;
 169                     }
 170                 }
 171                 """);
 172         assertOK(
 173                 """
 174                 import java.util.List;
 175                 record R<T>(List<T> x) {
 176                     public List<T> x() {
 177                         return this.x;
 178                     }
 179                 }
 180                 """);
 181     }
 182 
 183     @Test
 184     void testGoodMemberDeclarations() {
 185         String template = "public record R(int x) {\n"
 186                 + "    public R(int x) { this.x = x; }\n"
 187                 + "    public int x() { return x; }\n"
 188                 + "    public boolean equals(Object o) { return true; }\n"
 189                 + "    public int hashCode() { return 0; }\n"
 190                 + "    public String toString() { return null; }\n"
 191                 + "}";
 192         assertOK(template);
 193     }
 194 
 195     @Test
 196     void testBadComponentNames() {
 197         for (String s : BAD_COMPONENT_NAMES)
 198             assertFail("compiler.err.illegal.record.component.name", "record R(int #) { } ", s);
 199     }
 200 
 201     @Test
 202     void testRestrictedIdentifiers() {
 203         for (String s : List.of("interface record { void m(); }",
 204                 "@interface record { }",
 205                 "class record { }",
 206                 "record record(int x) { }",
 207                 "enum record { A, B }",
 208                 "class R<record> { }")) {
 209             assertFail(
 210                     "compiler.err.restricted.type.not.allowed",
 211                     diagWrapper -> {
 212                         JCDiagnostic diagnostic = ((DiagnosticSourceUnwrapper)diagWrapper).d;
 213                         Object[] args = diagnostic.getArgs();
 214                         Assert.check(args.length == 2);
 215                         Assert.check(args[1].toString().equals("JDK14"));
 216                     },
 217                     s);
 218         }
 219     }
 220 
 221     @Test
 222     void testValidMembers() {
 223         for (String s : List.of("record X(int j) { }",
 224                 "interface I { }",
 225                 "static { }",
 226                 "enum E { A, B }",
 227                 "class C { }"
 228         )) {
 229             assertOK("record R(int i) { # }", s);
 230         }
 231     }
 232 
 233     @Test
 234     void testCyclic() {
 235         // Cyclic records are OK, but cyclic inline records would not be
 236         assertOK("record R(R r) { }");
 237     }
 238 
 239     @Test
 240     void testBadExtends() {
 241         assertFail("compiler.err.expected", "record R(int x) extends Object { }");
 242         assertFail("compiler.err.expected", "record R(int x) {}\n"
 243                 + "record R2(int x) extends R { }");
 244         assertFail("compiler.err.cant.inherit.from.final", "record R(int x) {}\n"
 245                 + "class C extends R { }");
 246     }
 247 
 248     @Test
 249     void testNoExtendRecord() {
 250         assertFail("compiler.err.invalid.supertype.record",
 251                    """
 252                    class R extends Record {
 253                        public String toString() { return null; }
 254                        public int hashCode() { return 0; }
 255                        public boolean equals(Object o) { return false; }
 256                    }
 257                    """
 258         );
 259     }
 260 
 261     @Test
 262     void testFieldDeclarations() {
 263         // static fields are OK
 264         assertOK("public record R(int x) {\n" +
 265                 "    static int I = 1;\n" +
 266                 "    static final String S = \"Hello World!\";\n" +
 267                 "    static private Object O = null;\n" +
 268                 "    static protected Object O2 = null;\n" +
 269                 "}");
 270 
 271         // instance fields are not
 272         assertFail("compiler.err.record.cannot.declare.instance.fields",
 273                 "public record R(int x) {\n" +
 274                         "    private final int y = 0;" +
 275                         "}");
 276 
 277         // mutable instance fields definitely not
 278         assertFail("compiler.err.record.cannot.declare.instance.fields",
 279                 "public record R(int x) {\n" +
 280                         "    private int y = 0;" +
 281                         "}");
 282 
 283         // redeclaring components also not
 284         assertFail("compiler.err.record.cannot.declare.instance.fields",
 285                 "public record R(int x) {\n" +
 286                         "    private final int x;" +
 287                         "}");
 288     }
 289 
 290     @Test
 291     void testAccessorRedeclaration() {
 292         assertOK("public record R(int x) {\n" +
 293                 "    public int x() { return x; };" +
 294                 "}");
 295 
 296         assertOK("public record R(int... x) {\n" +
 297                 "    public int[] x() { return x; };" +
 298                 "}");
 299 
 300         assertOK("public record R(int x) {\n" +
 301                 "    public final int x() { return 0; };" +
 302                 "}");
 303 
 304         assertOK("public record R(int x) {\n" +
 305                 "    public final int x() { return 0; };" +
 306                 "}");
 307 
 308         assertFail("compiler.err.invalid.accessor.method.in.record",
 309                 "public record R(int x) {\n" +
 310                         "    final int x() { return 0; };" +
 311                         "}");
 312 
 313         assertFail("compiler.err.invalid.accessor.method.in.record",
 314                 "public record R(int x) {\n" +
 315                         "    int x() { return 0; };" +
 316                         "}");
 317 
 318         assertFail("compiler.err.invalid.accessor.method.in.record",
 319                 "public record R(int x) {\n" +
 320                         "    private int x() { return 0; };" +
 321                         "}");
 322 
 323         assertFail("compiler.err.invalid.accessor.method.in.record",
 324                    "public record R(int x) {\n" +
 325                    "    public int x() throws Exception { return 0; };" +
 326                    "}");
 327 
 328         for (String s : List.of("List", "List<?>", "Object", "ArrayList<String>", "int"))
 329             assertFail("compiler.err.invalid.accessor.method.in.record",
 330                     "import java.util.*;\n" +
 331                             "public record R(List<String> x) {\n" +
 332                             "    public # x() { return null; };" +
 333                             "}", s);
 334 
 335         assertFail("compiler.err.invalid.accessor.method.in.record",
 336                 "public record R(int x) {\n" +
 337                         "    public <T> int x() { return x; };" +
 338                         "}");
 339 
 340         assertFail("compiler.err.invalid.accessor.method.in.record",
 341                 "public record R(int x) {\n" +
 342                         "    static private final j = 0;" +
 343                         "    static public int x() { return j; };" +
 344                         "}");
 345     }
 346 
 347     @Test
 348     void testConstructorRedeclaration() {
 349         for (String goodCtor : List.of(
 350                 "public R(int x) { this(x, 0); }",
 351                 "public R(int x, int y) { this.x = x; this.y = y; }",
 352                 "public R { }"))
 353             assertOK("record R(int x, int y) { # }", goodCtor);
 354 
 355         assertOK("import java.util.*; record R(String x, String y) {  public R { Objects.requireNonNull(x); Objects.requireNonNull(y); } }");
 356 
 357         // The lambda expressions in the constructor should be compiled successfully.
 358         assertOK("""
 359                 import static java.util.Objects.*;
 360                 record R(String v) {
 361                     R {
 362                         requireNonNull(v, () -> "v must be provided");
 363                         requireNonNullElseGet(v, () -> "w");
 364                     }
 365                 }""");
 366 
 367         // Not OK to redeclare canonical without DA
 368         assertFail("compiler.err.var.might.not.have.been.initialized", "record R(int x, int y) { # }",
 369                    "public R(int x, int y) { this.x = x; }");
 370 
 371         // Not OK to rearrange or change names
 372         for (String s : List.of("public R(int y, int x) { this.x = x; this.y = y; }",
 373                                 "public R(int _x, int _y) { this.x = _x; this.y = _y; }"))
 374             assertFail("compiler.err.invalid.canonical.constructor.in.record", "record R(int x, int y) { # }", s);
 375 
 376         // ctor args must match types
 377         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 378                 "import java.util.*;\n" +
 379                         "record R(List<String> list) { # }",
 380                 "R(List list) { this.list = list; }");
 381 
 382         // canonical ctor should not throw checked exceptions
 383         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 384                    "record R() { # }",
 385                    "public R() throws Exception { }");
 386 
 387         // same for compact
 388         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 389                 "record R() { # }",
 390                 "public R throws Exception { }");
 391 
 392         // not even unchecked exceptions
 393         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 394                 "record R() { # }",
 395                  "public R() throws IllegalArgumentException { }");
 396 
 397         // ditto
 398         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 399                 "record R() { # }",
 400                 "public R throws IllegalArgumentException { }");
 401 
 402         // If types match, names must match
 403         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 404                    "record R(int x, int y) { public R(int y, int x) { this.x = this.y = 0; }}");
 405 
 406         // first invocation should be one to the canonical
 407         assertFail("compiler.err.first.statement.must.be.call.to.another.constructor",
 408                 "record R(int x, int y) { public R(int y, int x, int z) { this.x = this.y = 0; } }");
 409 
 410         assertFail("compiler.err.first.statement.must.be.call.to.another.constructor",
 411                 "record R(int x, int y) { public R(int y, int x, int z) { super(); this.x = this.y = 0; } }");
 412 
 413         assertOK("record R(int x, int y) { " +
 414                  "    public R(int x, int y, int z) { this(x, y); } " +
 415                  "}");
 416 
 417         assertOK("record R(int x) { " +
 418                 "    public R(int x, int y) { this(x, y, 0); } " +
 419                 "    public R(int x, int y, int z) { this(x); } " +
 420                 "}");
 421 
 422         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 423                 "record R<T>(T a) { # }",
 424                 "public <T> R {}");
 425 
 426         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 427                 "record R(int i) { # }",
 428                 "public <T> R(int i) { this.i = i; }");
 429 
 430         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 431                 "record R<T>(T a) { # }",
 432                 "public <T> R(T a) { this.a = a; }");
 433 
 434         assertFail("compiler.err.invalid.canonical.constructor.in.record",
 435                 "record R(int a) { # }",
 436                 "public R(int a) { super(); this.a = a; }");
 437     }
 438 
 439     @Test
 440     void testAnnotationCriteria() {
 441         String imports = "import java.lang.annotation.*;\n";
 442         String template = "@Target({ # }) @interface A {}\n";
 443         EnumMap<ElementType, String> annotations = new EnumMap<>(ElementType.class);
 444         for (ElementType e : values())
 445             annotations.put(e, template.replace("#", "ElementType." + e.name()));
 446         EnumSet<ElementType> goodSet = EnumSet.of(RECORD_COMPONENT, FIELD, METHOD, PARAMETER, TYPE_USE);
 447         EnumSet<ElementType> badSet = EnumSet.of(CONSTRUCTOR, PACKAGE, TYPE, LOCAL_VARIABLE, ANNOTATION_TYPE, TYPE_PARAMETER, MODULE);
 448 
 449         Assert.check(goodSet.size() + badSet.size() == values().length);
 450         String A_GOOD = template.replace("#",
 451                                          goodSet.stream().map(ElementType::name).map(s -> "ElementType." + s).collect(Collectors.joining(",")));
 452         String A_BAD = template.replace("#",
 453                                         badSet.stream().map(ElementType::name).map(s -> "ElementType." + s).collect(Collectors.joining(",")));
 454         String A_ALL = template.replace("#",
 455                                         Stream.of(ElementType.values()).map(ElementType::name).map(s -> "ElementType." + s).collect(Collectors.joining(",")));
 456         String A_NONE = "@interface A {}";
 457 
 458         for (ElementType e : goodSet)
 459             assertOK(imports + annotations.get(e) + "record R(@A int x) { }");
 460         assertOK(imports + A_GOOD + "record R(@A int x) { }");
 461         assertOK(imports + A_ALL + "record R(@A int x) { }");
 462         assertOK(imports + A_NONE);
 463 
 464         for (ElementType e : badSet) {
 465             assertFail("compiler.err.annotation.type.not.applicable", imports + annotations.get(e) + "record R(@A int x) { }");
 466         }
 467 
 468         assertFail("compiler.err.annotation.type.not.applicable", imports + A_BAD + "record R(@A int x) { }");
 469 
 470         // TODO: OK to redeclare with or without same annos
 471     }
 472 
 473     @Test
 474     void testNestedRecords() {
 475         String template = "class R { \n" +
 476                           "    # record RR(int a) { }\n" +
 477                           "}";
 478 
 479         for (String s : List.of("", "static", "final",
 480                                 "private", "public", "protected",
 481                                 "private static", "public static", "private static final"))
 482             assertOK(template, s);
 483 
 484         for (String s : List.of("class C { }",
 485                                 "static class C { }",
 486                                 "enum X { A; }",
 487                                 "interface I { }",
 488                                 "record RR(int y) { }"))
 489             assertOK("record R(int x) { # }", s);
 490     }
 491 
 492     @Test
 493     void testDuplicatedMember() {
 494         String template
 495                 = "    record R(int i) {\n" +
 496                   "        public int i() { return i; }\n" +
 497                   "        public int i() { return i; }\n" +
 498                   "    }";
 499         assertFail("compiler.err.already.defined", template);
 500     }
 501 
 502     @Test
 503     void testStaticLocals() {
 504         // static locals can't capture local variables, instance fields or type variables
 505         for (String s : List.of(
 506                 "record RR(int x) { public int x() { return y; }};",
 507                 "record RR(int x) { public int x() { return z; }};",
 508                 "record RR(int x) { public int x() { return instance; }};",
 509                 "record RR(T t) {};",
 510                 "record RR(U u) {};",
 511 
 512                 "interface I { default int x() { return y; }};",
 513                 "interface I { default int x() { return z; }};",
 514                 "interface I { default int x() { return instance; }};",
 515                 "interface I { default int x(T t) { return 0; }};",
 516                 "interface I { default int x(U u) { return 0; }};",
 517 
 518                 "enum E { A; int x() { return y; }};",
 519                 "enum E { A; int x() { return z; }};",
 520                 "enum E { A; int x() { return instance; }};",
 521                 "enum E { A; int x(T t) { return 0; }};",
 522                 "enum E { A; int x(U u) { return 0; }};"
 523         )) {
 524             assertFail("compiler.err.non-static.cant.be.ref",
 525                 """
 526                 class R<T> {
 527                     int instance = 0;
 528                     <U> U m(int y) {
 529                         int z;
 530                         #S
 531                         return null;
 532                     }
 533                 }
 534                 """.replaceFirst("#S", s));
 535         }
 536 
 537         // a similar example but a bit more complex
 538         for (String s : List.of(
 539                 "record R() { void test1() { class X { void test2() { System.err.println(localVar); } } } }",
 540                 "record R() { void test1() { class X { void test2() { System.err.println(param); } } } }",
 541                 "record R() {void test1() { class X { void test2() { System.err.println(instanceField); } } } }",
 542                 "record R() { void test1() { class X { T t; } } }",
 543                 "record R() { void test1() { class X { U u; } } }",
 544 
 545                 "interface I { default void test1() { class X { void test2() { System.err.println(localVar); } } } }",
 546                 "interface I() { default void test1() { class X { void test2() {System.err.println(param);} } } }",
 547                 "interface I { default void test1() { class X { void test2() { System.err.println(instanceField); } } } }",
 548                 "interface I { default void test1() { class X { T t; } } }",
 549                 "interface I() { default void test1() { class X {U u;} } }",
 550 
 551                 "enum E { A; void test1() { class X { void test2() { System.err.println(localVar); } } } }",
 552                 "enum E { A; void test1() { class X { void test2() {System.err.println(param);} } } }",
 553                 "enum E { A; void test1() { class X { void test2() { System.err.println(instanceField); } } } }",
 554                 "enum E { A; void test1() { class X { T t; } } }",
 555                 "enum E { A; void test1() { class X {U u;} } }"
 556         )) {
 557             assertFail("compiler.err.non-static.cant.be.ref",
 558                     """
 559                     class C<T> {
 560                         String instanceField = "instance";
 561                         static <U> U m(String param) {
 562                             String localVar = "local";
 563                             #S
 564                             return null;
 565                     }
 566                 }
 567                 """.replaceFirst("#S", s));
 568         }
 569 
 570         // can't self-shadow
 571         for (String s : List.of("record R() {}", "interface R {}", "enum R { A }")) {
 572             assertFail("compiler.err.already.defined", "class R { void m() { #S } }".replaceFirst("#S", s));
 573         }
 574 
 575         // can't be explicitly static
 576         for (String s : List.of("static record RR() { }", "static interface I {}", "static enum E { A }")) {
 577             assertFail("compiler.err.illegal.start.of.expr", "class R { void m() { #S } }".replaceFirst("#S", s));
 578         }
 579 
 580         // but static fields can be accessed
 581         for (String s : List.of(
 582                 "record RR() { public int x() { return z; } };",
 583                 "interface I { default int x() { return z; } }",
 584                 "enum E { A; int x() { return z; } }"
 585         )) {
 586             assertOK("class R { static int z = 0; void m() { #S } }".replaceFirst("#S", s));
 587         }
 588 
 589         // local records can also be final
 590         assertOK("class R { void m() { final record RR(int x) { }; } }");
 591     }
 592 
 593     @Test
 594     void testStaticDefinitionsInInnerClasses() {
 595         // static defs in inner classes can't capture instance fields or type variables
 596         for (String s : List.of(
 597                 """
 598                 record R() {
 599                     void test() { System.err.println(field); }
 600                 }
 601                 """,
 602                 """
 603                 record R() {
 604                     void test(T t) {}
 605                 }
 606                 """,
 607                 """
 608                 record R() {
 609                     void test1() {
 610                         class X {
 611                             void test2() { System.err.println(field); }
 612                         }
 613                     }
 614                 }
 615                 """,
 616                 """
 617                 record R() {
 618                     void test1() {
 619                         class X { void test2(T t) {} }
 620                     }
 621                 }
 622                 """,
 623 
 624                 """
 625                 interface I {
 626                     default void test() { System.err.println(field); }
 627                 }
 628                 """,
 629                 """
 630                 interface I {
 631                     default void test(T t) {}
 632                 }
 633                 """,
 634                 """
 635                 interface I {
 636                     default void test1() {
 637                         class X {
 638                             void test2() { System.err.println(field); }
 639                         }
 640                     }
 641                 }
 642                 """,
 643                 """
 644                 interface I {
 645                     default void test1() {
 646                         class X { void test2(T t) {} }
 647                     }
 648                 }
 649                 """,
 650 
 651                 """
 652                 enum E {
 653                     A;
 654                     void test() { System.err.println(field); }
 655                 }
 656                 """,
 657                 """
 658                 enum E {
 659                     A;
 660                     void test(T t) {}
 661                 }
 662                 """,
 663                 """
 664                 enum E {
 665                     A;
 666                     void test1() {
 667                         class X {
 668                             void test2() { System.err.println(field); }
 669                         }
 670                     }
 671                 }
 672                 """,
 673                 """
 674                 enum E {
 675                     A;
 676                     void test1() {
 677                         class X { void test2(T t) {} }
 678                     }
 679                 }
 680                 """,
 681 
 682                 """
 683                 static class SC {
 684                     void test() { System.err.println(field); }
 685                 }
 686                 """,
 687                 """
 688                 static class SC {
 689                     void test(T t) {}
 690                 }
 691                 """,
 692                 """
 693                 static class SC {
 694                     void test1() {
 695                         class X {
 696                             void test2() { System.err.println(field); }
 697                         }
 698                     }
 699                 }
 700                 """,
 701                 """
 702                 static class SC {
 703                     void test1() {
 704                         class X { void test2(T t) {} }
 705                     }
 706                 }
 707                 """
 708         )) {
 709             assertFail("compiler.err.non-static.cant.be.ref",
 710                     """
 711                     class C<T> {
 712                         String field = "field";
 713                         class Inner {
 714                             #S
 715                         }
 716                     }
 717                     """.replaceFirst("#S", s));
 718         }
 719 
 720         // another, more complex, example
 721         // static defs in inner classes can't capture instance locals, fields or type variables
 722         for (String s : List.of(
 723                 """
 724                 record R() {
 725                     void test() { System.err.println(field); }
 726                 }
 727                 """,
 728                 """
 729                 record R() {
 730                     void test1() {
 731                         class X { void test2() { System.err.println(field); } }
 732                     }
 733                 }
 734                 """,
 735                 """
 736                 record R() {
 737                     void test() { System.err.println(param); }
 738                 }
 739                 """,
 740                 """
 741                 record R() {
 742                     void test1() {
 743                         class X { void test2() { System.err.println(param); } }
 744                     }
 745                 }
 746                 """,
 747                 """
 748                 record R() {
 749                     void test() { System.err.println(local); }
 750                 }
 751                 """,
 752                 """
 753                 record R() {
 754                     void test1() {
 755                         class X { void test2() { System.err.println(local); } }
 756                     }
 757                 }
 758                 """,
 759                 """
 760                 record R() {
 761                     void test(T t) {}
 762                 }
 763                 """,
 764                 """
 765                 record R() {
 766                     void test(U u) {}
 767                 }
 768                 """,
 769                 """
 770                 record R() {
 771                     void test1() {
 772                         class X { void test2(T t) {} }
 773                     }
 774                 }
 775                 """,
 776                 """
 777                 record R() {
 778                     void test1() {
 779                         class X { void test2(U u) {} }
 780                     }
 781                 }
 782                 """,
 783 
 784                 """
 785                 interface I {
 786                     default void test() { System.err.println(field); }
 787                 }
 788                 """,
 789                 """
 790                 interface I {
 791                     default void test1() {
 792                         class X {
 793                             void test2() { System.err.println(field); }
 794                         }
 795                     }
 796                 }
 797                 """,
 798                 """
 799                 interface I {
 800                     default void test() { System.err.println(param); }
 801                 }
 802                 """,
 803                 """
 804                 interface I {
 805                     default void test1() {
 806                         class X {
 807                             void test2() { System.err.println(param); }
 808                         }
 809                     }
 810                 }
 811                 """,
 812                 """
 813                 interface I {
 814                     default void test() { System.err.println(local); }
 815                 }
 816                 """,
 817                 """
 818                 interface I {
 819                     default void test1() {
 820                         class X {
 821                             void test2() { System.err.println(local); }
 822                         }
 823                     }
 824                 }
 825                 """,
 826                 """
 827                 interface I {
 828                     default void test(T t) {}
 829                 }
 830                 """,
 831                 """
 832                 interface I {
 833                     default void test(U u) {}
 834                 }
 835                 """,
 836                 """
 837                 interface I {
 838                     default void test1() {
 839                         class X { void test2(T t) {} }
 840                     }
 841                 }
 842                 """,
 843                 """
 844                 interface I {
 845                     default void test1() {
 846                         class X { void test2(U u) {} }
 847                     }
 848                 }
 849                 """,
 850 
 851                 """
 852                 enum E {
 853                     A;
 854                     void test() { System.err.println(field); }
 855                 }
 856                 """,
 857                 """
 858                 enum E {
 859                     A;
 860                     void test1() {
 861                         class X {
 862                             void test2() { System.err.println(field); }
 863                         }
 864                     }
 865                 }
 866                 """,
 867                 """
 868                 enum E {
 869                     A;
 870                     void test() { System.err.println(param); }
 871                 }
 872                 """,
 873                 """
 874                 enum E {
 875                     A;
 876                     void test1() {
 877                         class X {
 878                             void test2() { System.err.println(param); }
 879                         }
 880                     }
 881                 }
 882                 """,
 883                 """
 884                 enum E {
 885                     A;
 886                     void test() { System.err.println(local); }
 887                 }
 888                 """,
 889                 """
 890                 enum E {
 891                     A;
 892                     void test1() {
 893                         class X {
 894                             void test2() { System.err.println(local); }
 895                         }
 896                     }
 897                 }
 898                 """,
 899                 """
 900                 enum E {
 901                     A;
 902                     void test(T t) {}
 903                 }
 904                 """,
 905                 """
 906                 enum E {
 907                     A;
 908                     void test(U u) {}
 909                 }
 910                 """,
 911                 """
 912                 enum E {
 913                     A;
 914                     void test1() {
 915                         class X { void test2(T t) {} }
 916                     }
 917                 }
 918                 """,
 919                 """
 920                 enum E {
 921                     A;
 922                     void test1() {
 923                         class X { void test2(U u) {} }
 924                     }
 925                 }
 926                 """,
 927 
 928                 """
 929                 static class SC {
 930                     void test() { System.err.println(field); }
 931                 }
 932                 """,
 933                 """
 934                 static class SC {
 935                     void test1() {
 936                         class X {
 937                             void test2() { System.err.println(field); }
 938                         }
 939                     }
 940                 }
 941                 """,
 942                 """
 943                 static class SC {
 944                     void test() { System.err.println(param); }
 945                 }
 946                 """,
 947                 """
 948                 static class SC {
 949                     void test1() {
 950                         class X {
 951                             void test2() { System.err.println(param); }
 952                         }
 953                     }
 954                 }
 955                 """,
 956                 """
 957                 static class SC {
 958                     void test() { System.err.println(local); }
 959                 }
 960                 """,
 961                 """
 962                 static class SC {
 963                     void test1() {
 964                         class X {
 965                             void test2() { System.err.println(local); }
 966                         }
 967                     }
 968                 }
 969                 """,
 970                 """
 971                 static class SC {
 972                     void test(T t) {}
 973                 }
 974                 """,
 975                 """
 976                 static class SC {
 977                     void test(U u) {}
 978                 }
 979                 """,
 980                 """
 981                 static class SC {
 982                     void test1() {
 983                         class X { void test2(T t) {} }
 984                     }
 985                 }
 986                 """,
 987                 """
 988                 static class SC {
 989                     void test1() {
 990                         class X { void test2(U u) {} }
 991                     }
 992                 }
 993                 """
 994         )) {
 995             assertFail("compiler.err.non-static.cant.be.ref",
 996                     """
 997                     class C<T> {
 998                         String field = "field";
 999                         <U> U m(String param) {
1000                             String local = "local";
1001                             class Local {
1002                                 class Inner { #S }
1003                             }
1004                             return null;
1005                         }
1006                     }
1007                     """.replaceFirst("#S", s));
1008         }
1009 
1010         // inner classes can contain static methods too
1011         assertOK(
1012                 """
1013                 class C {
1014                     class Inner {
1015                         // static method inside inner class
1016                         static void m() {}
1017                     }
1018                 }
1019                 """
1020         );
1021 
1022         assertOK(
1023                 """
1024                 class C {
1025                      void m() {
1026                          new Object() {
1027                             // static method inside inner class
1028                             static void m() {}
1029                          };
1030                      }
1031                 }
1032                 """
1033         );
1034 
1035         // but still non-static declarations can't be accessed from a static method inside a local class
1036         for (String s : List.of(
1037                 "System.out.println(localVar)",
1038                 "System.out.println(param)",
1039                 "System.out.println(field)",
1040                 "T t",
1041                 "U u"
1042         )) {
1043             assertFail("compiler.err.non-static.cant.be.ref",
1044                     """
1045                     class C<T> {
1046                         int field = 0;
1047                         <U> void foo(int param) {
1048                             int localVar = 1;
1049                             class Local {
1050                                 static void m() {
1051                                     #S;
1052                                 }
1053                             }
1054                         }
1055                     }
1056                     """.replaceFirst("#S", s));
1057         }
1058     }
1059 
1060     @Test
1061     void testReturnInCanonical_Compact() {
1062         assertFail("compiler.err.invalid.canonical.constructor.in.record", "record R(int x) { # }",
1063                 "public R { return; }");
1064         assertFail("compiler.err.invalid.canonical.constructor.in.record", "record R(int x) { # }",
1065                 "public R { if (i < 0) { return; }}");
1066         assertOK("record R(int x) { public R(int x) { this.x = x; return; } }");
1067         assertOK("record R(int x) { public R { Runnable r = () -> { return; };} }");
1068     }
1069 
1070     @Test
1071     void testArgumentsAreNotFinalInCompact() {
1072         assertOK(
1073                 """
1074                 record R(int x) {
1075                     public R {
1076                         x++;
1077                     }
1078                 }
1079                 """);
1080     }
1081 
1082     @Test
1083     void testNoNativeMethods() {
1084         assertFail("compiler.err.mod.not.allowed.here", "record R(int x) { # }",
1085                 "public native R {}");
1086         assertFail("compiler.err.mod.not.allowed.here", "record R(int x) { # }",
1087                 "public native void m();");
1088     }
1089 
1090     @Test
1091     void testRecordsInsideInner() {
1092         assertOK(
1093                 """
1094                 class Outer {
1095                     class Inner {
1096                         record R(int a) {}
1097                     }
1098                 }
1099                 """
1100         );
1101         assertOK(
1102                 """
1103                 class Outer {
1104                     public void test() {
1105                         class Inner extends Outer {
1106                             record R(int i) {}
1107                         }
1108                     }
1109                 }
1110                 """);
1111         assertOK(
1112                 """
1113                 class Outer {
1114                     Runnable run = new Runnable() {
1115                         record TestRecord(int i) {}
1116                         public void run() {}
1117                     };
1118                 }
1119                 """);
1120         assertOK(
1121                 """
1122                 class Outer {
1123                     void m() {
1124                         record A() {
1125                             record B() { }
1126                         }
1127                     }
1128                 }
1129                 """);
1130     }
1131 
1132     @Test
1133     void testAnnoInsideLocalOrAnonymous() {
1134         assertFail("compiler.err.annotation.decl.not.allowed.here",
1135                 """
1136                 class Outer {
1137                     public void test() {
1138                         class Local {
1139                             @interface A {}
1140                         }
1141                     }
1142                 }
1143                 """);
1144         assertFail("compiler.err.annotation.decl.not.allowed.here",
1145                 """
1146                 class Outer {
1147                     public void test() {
1148                         interface I {
1149                             @interface A {}
1150                         }
1151                     }
1152                 }
1153                 """);
1154         assertFail("compiler.err.annotation.decl.not.allowed.here",
1155                 """
1156                 class Outer {
1157                     public void test() {
1158                         record R() {
1159                             @interface A {}
1160                         }
1161                     }
1162                 }
1163                 """);
1164         assertFail("compiler.err.annotation.decl.not.allowed.here",
1165                 """
1166                 class Outer {
1167                     public void test() {
1168                         enum E {
1169                             E1;
1170                             @interface A {}
1171                         }
1172                     }
1173                 }
1174                 """);
1175 
1176         assertFail("compiler.err.annotation.decl.not.allowed.here",
1177                 """
1178                 class Outer {
1179                     public void test() {
1180                         class Local1 {
1181                             class Local2 {
1182                                 @interface A {}
1183                             }
1184                         }
1185                     }
1186                 }
1187                 """);
1188         assertFail("compiler.err.annotation.decl.not.allowed.here",
1189                 """
1190                 class Outer {
1191                     public void test() {
1192                         class Local {
1193                             interface I {
1194                                 @interface A {}
1195                             }
1196                         }
1197                     }
1198                 }
1199                 """);
1200         assertFail("compiler.err.annotation.decl.not.allowed.here",
1201                 """
1202                 class Outer {
1203                     public void test() {
1204                         class Local {
1205                             record R() {
1206                                 @interface A {}
1207                             }
1208                         }
1209                     }
1210                 }
1211                 """);
1212         assertFail("compiler.err.annotation.decl.not.allowed.here",
1213                 """
1214                 class Outer {
1215                     public void test() {
1216                         class Local {
1217                             enum E {
1218                                 E1;
1219                                 @interface A {}
1220                             }
1221                         }
1222                     }
1223                 }
1224                 """);
1225 
1226         assertFail("compiler.err.annotation.decl.not.allowed.here",
1227                 """
1228                 class Outer {
1229                     Runnable run = new Runnable() {
1230                         @interface A {}
1231                         public void run() {}
1232                     };
1233                 }
1234                 """);
1235     }
1236 
1237     @Test
1238     void testReceiverParameter() {
1239         assertFail("compiler.err.receiver.parameter.not.applicable.constructor.toplevel.class",
1240                 """
1241                 record R(int i) {
1242                     public R(R this, int i) {
1243                         this.i = i;
1244                     }
1245                 }
1246                 """);
1247         assertFail("compiler.err.non-static.cant.be.ref",
1248                 """
1249                 class Outer {
1250                     record R(int i) {
1251                         public R(Outer Outer.this, int i) {
1252                             this.i = i;
1253                         }
1254                     }
1255                 }
1256                 """);
1257         assertOK(
1258                 """
1259                 record R(int i) {
1260                     void m(R this) {}
1261                     public int i(R this) { return i; }
1262                 }
1263                 """);
1264     }
1265 
1266     @Test
1267     void testOnlyOneFieldRef() throws Exception {
1268         for (String source : List.of(
1269                 "record R(int recordComponent) {}",
1270                 """
1271                 class Test {
1272                     class Inner {
1273                         Inner() {
1274                             record R(int recordComponent) {}
1275                         }
1276                     }
1277                 }
1278                 """,
1279                 """
1280                 class Test {
1281                     class Inner {
1282                         void m() {
1283                             record R(int recordComponent) {}
1284                         }
1285                     }
1286                 }
1287                 """,
1288                 """
1289                 class Test {
1290                     void m() {
1291                         record R(int recordComponent) {}
1292                     }
1293                 }
1294                 """
1295         )) {
1296             File dir = assertOK(true, source);
1297             int numberOfFieldRefs = 0;
1298             for (final File fileEntry : Objects.requireNonNull(dir.listFiles())) {
1299                 if (fileEntry.getName().endsWith("R.class")) {
1300                     ClassModel classFile = Classfile.of().parse(fileEntry.toPath());
1301                     for (PoolEntry pe : classFile.constantPool()) {
1302                         if (pe instanceof FieldRefEntry fieldRefEntry) {
1303                             numberOfFieldRefs++;
1304                             NameAndTypeEntry nameAndType = (NameAndTypeEntry) classFile.constantPool()
1305                                             .entryByIndex(fieldRefEntry.nameAndType().index());
1306                             Assert.check(nameAndType.name().equalsString("recordComponent"));
1307                         }
1308                     }
1309                     Assert.check(numberOfFieldRefs == 1);
1310                 }
1311             }
1312         }
1313     }
1314 
1315     //  check that fields are initialized in a canonical constructor in the same declaration order as the corresponding
1316     //  record component
1317     @Test
1318     void testCheckInitializationOrderInCompactConstructor() throws Exception {
1319         FieldInstruction putField1 = null;
1320         FieldInstruction putField2 = null;
1321         File dir = assertOK(true, "record R(int i, String s) { R {} }");
1322         for (final File fileEntry : Objects.requireNonNull(dir.listFiles())) {
1323             if (fileEntry.getName().equals("R.class")) {
1324                 ClassModel classFile = Classfile.of().parse(fileEntry.toPath());
1325                 for (MethodModel method : classFile.methods()) {
1326                     if (method.methodName().equalsString("<init>")) {
1327                         CodeAttribute code_attribute = method.findAttribute(Attributes.CODE).orElseThrow();
1328                         for (CodeElement ce : code_attribute.elementList()) {
1329                             if (ce instanceof Instruction instruction && instruction.opcode() == Opcode.PUTFIELD) {
1330                                 if (putField1 != null && putField2 != null) {
1331                                     throw new AssertionError("was expecting only two putfield instructions in this method");
1332                                 }
1333                                 if (putField1 == null) {
1334                                     putField1 = (FieldInstruction) instruction;
1335                                 } else {
1336                                     putField2 = (FieldInstruction) instruction;
1337                                 }
1338                             }
1339                         }
1340                         // now we need to check that we are assigning to `i` first and to `s` afterwards
1341                         assert putField1 != null;
1342                         FieldRefEntry fieldref_info1 = putField1.field();
1343                         if (!fieldref_info1.name().equalsString("i")) {
1344                             throw new AssertionError("was expecting variable name 'i'");
1345                         }
1346                         assert putField2 != null;
1347                         FieldRefEntry fieldref_info2 = putField2.field();
1348                         if (!fieldref_info2.name().equalsString("s")) {
1349                             throw new AssertionError("was expecting variable name 's'");
1350                         }
1351                     }
1352                 }
1353             }
1354         }
1355     }
1356 
1357     @Test
1358     void testAcceptRecordId() {
1359         String[] previousOptions = getCompileOptions();
1360         try {
1361             String[] testOptions = {};
1362             setCompileOptions(testOptions);
1363             assertFail("compiler.err.illegal.start.of.type",
1364                     "class R {\n" +
1365                             "    record RR(int i) {\n" +
1366                             "        return null;\n" +
1367                             "    }\n" +
1368                             "    class record {}\n" +
1369                             "}");
1370         } finally {
1371             setCompileOptions(previousOptions);
1372         }
1373     }
1374 
1375     @Test
1376     void testMultipleAnnosInRecord() throws Exception {
1377         String[] previousOptions = getCompileOptions();
1378 
1379         try {
1380             String imports = """
1381                     import java.lang.annotation.ElementType;
1382                     import java.lang.annotation.Target;
1383                     """;
1384 
1385             String annotTemplate =
1386                     """
1387                     @Target(ElementType.#TARGET)
1388                     @interface anno#TARGET { }
1389                     """;
1390 
1391             String recordTemplate =
1392                     """
1393                     record R(#TARGETS String s) {}
1394                     """;
1395 
1396             String[] generalOptions = {
1397                     "-processor", Processor.class.getName(),
1398             };
1399 
1400             List<String> targets = List.of("FIELD", "RECORD_COMPONENT", "PARAMETER", "METHOD");
1401 
1402             var interfaces = targets.stream().map(t -> annotTemplate.replaceAll("#TARGET", t)).collect(Collectors.joining("\n"));
1403             var recordAnnotations = targets.stream().map(t -> "@anno" + t).collect(Collectors.joining(" "));
1404             String record = recordTemplate.replaceFirst("#TARGETS", recordAnnotations);
1405             String code = String.format("%s\n%s\n%s\n",imports,interfaces,record);
1406             String[] testOptions = generalOptions.clone();
1407             setCompileOptions(testOptions);
1408 
1409             assertOK(true, code);
1410 
1411         // let's reset the default compiler options for other tests
1412         } finally {
1413             setCompileOptions(previousOptions);
1414         }
1415     }
1416 
1417     @Test
1418     void testAnnos() throws Exception {
1419         String[] previousOptions = getCompileOptions();
1420         try {
1421             String srcTemplate =
1422                     """
1423                     import java.lang.annotation.*;
1424                     @Target({#TARGET})
1425                     @Retention(RetentionPolicy.RUNTIME)
1426                     @interface Anno { }
1427                     record R(@Anno String s) {}
1428                     """;
1429 
1430             // testing several combinations, adding even more combinations won't add too much value
1431             List<String> annoApplicableTargets = List.of(
1432                     "ElementType.FIELD",
1433                     "ElementType.METHOD",
1434                     "ElementType.PARAMETER",
1435                     "ElementType.RECORD_COMPONENT",
1436                     "ElementType.TYPE_USE",
1437                     "ElementType.TYPE_USE,ElementType.FIELD",
1438                     "ElementType.TYPE_USE,ElementType.METHOD",
1439                     "ElementType.TYPE_USE,ElementType.PARAMETER",
1440                     "ElementType.TYPE_USE,ElementType.RECORD_COMPONENT",
1441                     "ElementType.TYPE_USE,ElementType.FIELD,ElementType.METHOD",
1442                     "ElementType.TYPE_USE,ElementType.FIELD,ElementType.PARAMETER",
1443                     "ElementType.TYPE_USE,ElementType.FIELD,ElementType.RECORD_COMPONENT",
1444                     "ElementType.FIELD,ElementType.TYPE_USE",
1445                     "ElementType.FIELD,ElementType.CONSTRUCTOR",
1446                     "ElementType.FIELD,ElementType.LOCAL_VARIABLE",
1447                     "ElementType.FIELD,ElementType.ANNOTATION_TYPE",
1448                     "ElementType.FIELD,ElementType.PACKAGE",
1449                     "ElementType.FIELD,ElementType.TYPE_PARAMETER",
1450                     "ElementType.FIELD,ElementType.MODULE",
1451                     "ElementType.METHOD,ElementType.TYPE_USE",
1452                     "ElementType.PARAMETER,ElementType.TYPE_USE",
1453                     "ElementType.RECORD_COMPONENT,ElementType.TYPE_USE",
1454                     "ElementType.FIELD,ElementType.METHOD,ElementType.TYPE_USE",
1455                     "ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE_USE",
1456                     "ElementType.FIELD,ElementType.RECORD_COMPONENT,ElementType.TYPE_USE"
1457             );
1458 
1459             String[] generalOptions = {
1460                     "-processor", Processor.class.getName(),
1461                     "-Atargets="
1462             };
1463 
1464             for (String target : annoApplicableTargets) {
1465                 String code = srcTemplate.replaceFirst("#TARGET", target);
1466                 String[] testOptions = generalOptions.clone();
1467                 testOptions[testOptions.length - 1] = testOptions[testOptions.length - 1] + target;
1468                 setCompileOptions(testOptions);
1469 
1470                 File dir = assertOK(true, code);
1471 
1472                 ClassModel classFile = Classfile.of().parse(findClassFileOrFail(dir, "R.class").toPath());
1473 
1474                 // field first
1475                 Assert.check(classFile.fields().size() == 1);
1476                 FieldModel field = classFile.fields().get(0);
1477                 // if FIELD is one of the targets then there must be a declaration annotation applied to the field, apart from
1478                 // the type annotation
1479                 if (target.contains("ElementType.FIELD")) {
1480                     checkAnno(findAttributeOrFail(field.attributes(), RuntimeVisibleAnnotationsAttribute.class),
1481                             "Anno");
1482                 } else {
1483                     assertAttributeNotPresent(field.attributes(), RuntimeVisibleAnnotationsAttribute.class);
1484                 }
1485 
1486                 // lets check now for the type annotation
1487                 if (target.contains("ElementType.TYPE_USE")) {
1488                     checkTypeAnno(findAttributeOrFail(field.attributes(), RuntimeVisibleTypeAnnotationsAttribute.class),
1489                             "FIELD", "Anno");
1490                 } else {
1491                     assertAttributeNotPresent(field.attributes(), RuntimeVisibleTypeAnnotationsAttribute.class);
1492                 }
1493 
1494                 // checking for the annotation on the corresponding parameter of the canonical constructor
1495                 MethodModel init = findMethodOrFail(classFile, "<init>");
1496                 // if PARAMETER is one of the targets then there must be a declaration annotation applied to the parameter, apart from
1497                 // the type annotation
1498                 if (target.contains("ElementType.PARAMETER")) {
1499                     checkParameterAnno(
1500                             (RuntimeVisibleParameterAnnotationsAttribute) findAttributeOrFail(
1501                                     init.attributes(),
1502                                     RuntimeVisibleParameterAnnotationsAttribute.class),
1503                             "Anno");
1504                 } else {
1505                     assertAttributeNotPresent(init.attributes(), RuntimeVisibleAnnotationsAttribute.class);
1506                 }
1507                 // let's check now for the type annotation
1508                 if (target.contains("ElementType.TYPE_USE")) {
1509                     checkTypeAnno(findAttributeOrFail(init.attributes(), RuntimeVisibleTypeAnnotationsAttribute.class),
1510                             "METHOD_FORMAL_PARAMETER", "Anno");
1511                 } else {
1512                     assertAttributeNotPresent(init.attributes(), RuntimeVisibleTypeAnnotationsAttribute.class);
1513                 }
1514 
1515                 // checking for the annotation in the accessor
1516                 MethodModel accessor = findMethodOrFail(classFile, "s");
1517                 // if METHOD is one of the targets then there must be a declaration annotation applied to the accessor, apart from
1518                 // the type annotation
1519                 if (target.contains("ElementType.METHOD")) {
1520                     checkAnno(findAttributeOrFail(accessor.attributes(), RuntimeVisibleAnnotationsAttribute.class),
1521                             "Anno");
1522                 } else {
1523                     assertAttributeNotPresent(accessor.attributes(), RuntimeVisibleAnnotationsAttribute.class);
1524                 }
1525                 // let's check now for the type annotation
1526                 if (target.contains("ElementType.TYPE_USE")) {
1527                     checkTypeAnno(findAttributeOrFail(accessor.attributes(), RuntimeVisibleTypeAnnotationsAttribute.class),
1528                             "METHOD_RETURN", "Anno");
1529                 } else {
1530                     assertAttributeNotPresent(accessor.attributes(), RuntimeVisibleTypeAnnotationsAttribute.class);
1531                 }
1532 
1533                 // checking for the annotation in the Record attribute
1534                 RecordAttribute record = (RecordAttribute) findAttributeOrFail(classFile.attributes(), RecordAttribute.class);
1535                 Assert.check(record.components().size() == 1);
1536                 // if RECORD_COMPONENT is one of the targets then there must be a declaration annotation applied to the
1537                 // field, apart from the type annotation
1538                 if (target.contains("ElementType.RECORD_COMPONENT")) {
1539                     checkAnno(findAttributeOrFail(record.components().get(0).attributes(), RuntimeVisibleAnnotationsAttribute.class),
1540                             "Anno");
1541                 } else {
1542                     assertAttributeNotPresent(record.components().get(0).attributes(), RuntimeVisibleAnnotationsAttribute.class);
1543                 }
1544                 // lets check now for the type annotation
1545                 if (target.contains("ElementType.TYPE_USE")) {
1546                     checkTypeAnno(findAttributeOrFail(record.components().get(0).attributes(), RuntimeVisibleTypeAnnotationsAttribute.class),
1547                             "FIELD", "Anno");
1548                 } else {
1549                     assertAttributeNotPresent(record.components().get(0).attributes(), RuntimeVisibleTypeAnnotationsAttribute.class);
1550                 }
1551             }
1552 
1553             // let's reset the default compiler options for other tests
1554         } finally {
1555             setCompileOptions(previousOptions);
1556         }
1557     }
1558 
1559     // JDK-8292159: TYPE_USE annotations on generic type arguments
1560     //              of record components discarded
1561     @Test
1562     void testOnlyTypeAnnotationsOnComponentField() throws Exception {
1563         String code =
1564                 """
1565                 import java.lang.annotation.*;
1566                 import java.util.List;
1567                 @Target({ElementType.TYPE_USE})
1568                 @Retention(RetentionPolicy.RUNTIME)
1569                 @interface Anno { }
1570                 record R(List<@Anno String> s) {}
1571                 """;
1572 
1573         File dir = assertOK(true, code);
1574 
1575         ClassModel classFile = Classfile.of().parse(findClassFileOrFail(dir, "R.class").toPath());
1576 
1577         // field first
1578         Assert.check(classFile.fields().size() == 1);
1579         FieldModel field = classFile.fields().get(0);
1580         checkTypeAnno(findAttributeOrFail(field.attributes(), RuntimeVisibleTypeAnnotationsAttribute.class),
1581                 "FIELD",
1582                 "Anno");
1583 
1584         // checking for the annotation on the corresponding parameter of the canonical constructor
1585         MethodModel init = findMethodOrFail(classFile, "<init>");
1586         checkTypeAnno(findAttributeOrFail(init.attributes(), RuntimeVisibleTypeAnnotationsAttribute.class),
1587                 "METHOD_FORMAL_PARAMETER", "Anno");
1588 
1589         // checking for the annotation in the accessor
1590         MethodModel accessor = findMethodOrFail(classFile, "s");
1591         checkTypeAnno(findAttributeOrFail(accessor.attributes(), RuntimeVisibleTypeAnnotationsAttribute.class),
1592                 "METHOD_RETURN", "Anno");
1593 
1594         // checking for the annotation in the Record attribute
1595         RecordAttribute record = (RecordAttribute) findAttributeOrFail(classFile.attributes(), RecordAttribute.class);
1596         Assert.check(record.components().size() == 1);
1597         checkTypeAnno(findAttributeOrFail(record.components().get(0).attributes(),
1598                                 RuntimeVisibleTypeAnnotationsAttribute.class),
1599                         "FIELD", "Anno");
1600     }
1601 
1602     private void checkTypeAnno(Attribute<?> rtAnnos,
1603                                String positionType,
1604                                String annoName) {
1605         // containing only one type annotation
1606         TypeAnnotation tAnno;
1607         switch (rtAnnos) {
1608             case RuntimeVisibleTypeAnnotationsAttribute rtVAnnos -> {
1609                 Assert.check(rtVAnnos.annotations().size() == 1);
1610                 tAnno = rtVAnnos.annotations().get(0);
1611             }
1612             case RuntimeInvisibleTypeAnnotationsAttribute rtIAnnos -> {
1613                 Assert.check(rtIAnnos.annotations().size() == 1);
1614                 tAnno = rtIAnnos.annotations().get(0);
1615             }
1616             default -> throw new AssertionError();
1617         }
1618         assert tAnno != null;
1619         Assert.check(tAnno.targetInfo().targetType().name().equals(positionType));
1620         String annotationName = tAnno.classSymbol().displayName();
1621         Assert.check(annotationName.startsWith(annoName));
1622     }
1623     private void checkAnno(Attribute<?> rAnnos,
1624                            String annoName) {
1625         // containing only one type annotation
1626         Annotation anno;
1627         switch (rAnnos) {
1628             case RuntimeVisibleAnnotationsAttribute rVAnnos -> {
1629                 Assert.check(rVAnnos.annotations().size() == 1);
1630                 anno = rVAnnos.annotations().get(0);
1631             }
1632             case RuntimeInvisibleAnnotationsAttribute rIAnnos -> {
1633                 Assert.check(rIAnnos.annotations().size() == 1);
1634                 anno = rIAnnos.annotations().get(0);
1635             }
1636             default -> throw new AssertionError();
1637         }
1638         assert anno != null;
1639         String annotationName = anno.classSymbol().displayName();
1640         Assert.check(annotationName.startsWith(annoName));
1641     }
1642 
1643     // special case for parameter annotations
1644     private void checkParameterAnno(RuntimeVisibleParameterAnnotationsAttribute rAnnos,
1645                            String annoName) {
1646         // containing only one type annotation
1647         Assert.check(rAnnos.parameterAnnotations().size() == 1);
1648         Assert.check(rAnnos.parameterAnnotations().get(0).size() == 1);
1649         Annotation anno = rAnnos.parameterAnnotations().get(0).get(0);
1650         String annotationName = anno.classSymbol().displayName();
1651         Assert.check(annotationName.startsWith(annoName));
1652     }
1653 
1654     private File findClassFileOrFail(File dir, String name) {
1655         for (final File fileEntry : dir.listFiles()) {
1656             if (fileEntry.getName().equals(name)) {
1657                 return fileEntry;
1658             }
1659         }
1660         throw new AssertionError("file not found");
1661     }
1662 
1663     private MethodModel findMethodOrFail(ClassModel classFile, String name) {
1664         for (MethodModel method : classFile.methods()) {
1665             if (method.methodName().equalsString(name)) {
1666                 return method;
1667             }
1668         }
1669         throw new AssertionError("method not found");
1670     }
1671 
1672     private Attribute<?> findAttributeOrFail(List<Attribute<?>> attributes, Class<? extends Attribute<?>> attrClass) {
1673         for (Attribute<?> attribute : attributes) {
1674             if (attrClass.isAssignableFrom(attribute.getClass())) {
1675                 return attribute;
1676             }
1677         }
1678         throw new AssertionError("attribute not found" + attrClass.toString() + "!!!!" + attributes.getFirst().getClass().toString());
1679     }
1680 
1681     private void assertAttributeNotPresent(List<Attribute<?>> attributes, Class<? extends Attribute<?>> attrClass) {
1682         for (Attribute<?> attribute : attributes) {
1683             if (attribute.getClass() == attrClass) {
1684                 throw new AssertionError("attribute not expected");
1685             }
1686         }
1687     }
1688 
1689     @SupportedAnnotationTypes("*")
1690     public static final class Processor extends JavacTestingAbstractProcessor {
1691 
1692         String targets;
1693         int numberOfTypeAnnotations;
1694 
1695         @Override
1696         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1697             targets = processingEnv.getOptions().get("targets");
1698             for (TypeElement te : annotations) {
1699                 if (te.toString().equals("Anno")) {
1700                     checkElements(te, roundEnv, targets);
1701                     if (targets.contains("TYPE_USE")) {
1702                         Element element = processingEnv.getElementUtils().getTypeElement("R");
1703                         numberOfTypeAnnotations = 0;
1704                         checkTypeAnnotations(element);
1705                         Assert.check(numberOfTypeAnnotations == 4);
1706                     }
1707                 }
1708             }
1709             return true;
1710         }
1711 
1712         void checkElements(TypeElement te, RoundEnvironment renv, String targets) {
1713             Set<? extends Element> annoElements = renv.getElementsAnnotatedWith(te);
1714             Set<String> targetSet = new HashSet<>(Arrays.asList(targets.split(",")));
1715             // we will check for type annotation in another method
1716             targetSet.remove("ElementType.TYPE_USE");
1717             for (Element e : annoElements) {
1718                 Symbol s = (Symbol) e;
1719                 switch (s.getKind()) {
1720                     case FIELD -> {
1721                         Assert.check(targetSet.contains("ElementType.FIELD"));
1722                         targetSet.remove("ElementType.FIELD");
1723                     }
1724                     case METHOD -> {
1725                         Assert.check(targetSet.contains("ElementType.METHOD"));
1726                         targetSet.remove("ElementType.METHOD");
1727                     }
1728                     case PARAMETER -> {
1729                         Assert.check(targetSet.contains("ElementType.PARAMETER"));
1730                         targetSet.remove("ElementType.PARAMETER");
1731                     }
1732                     case RECORD_COMPONENT -> {
1733                         Assert.check(targetSet.contains("ElementType.RECORD_COMPONENT"));
1734                         targetSet.remove("ElementType.RECORD_COMPONENT");
1735                     }
1736                     default -> throw new AssertionError("unexpected element kind");
1737                 }
1738             }
1739         }
1740 
1741         private void checkTypeAnnotations(Element rootElement) {
1742             new ElementScanner<Void, Void>() {
1743                 @Override public Void visitVariable(VariableElement e, Void p) {
1744                     Symbol s = (Symbol) e;
1745                     if (s.getKind() == ElementKind.FIELD ||
1746                             s.getKind() == ElementKind.PARAMETER &&
1747                             s.name.toString().equals("s")) {
1748                         int currentTAs = numberOfTypeAnnotations;
1749                         verifyTypeAnnotations(e.asType().getAnnotationMirrors());
1750                         Assert.check(currentTAs + 1 == numberOfTypeAnnotations);
1751                     }
1752                     return null;
1753                 }
1754                 @Override
1755                 public Void visitExecutable(ExecutableElement e, Void p) {
1756                     Symbol s = (Symbol) e;
1757                     if (s.getKind() == ElementKind.METHOD &&
1758                                     s.name.toString().equals("s")) {
1759                         int currentTAs = numberOfTypeAnnotations;
1760                         verifyTypeAnnotations(e.getReturnType().getAnnotationMirrors());
1761                         Assert.check(currentTAs + 1 == numberOfTypeAnnotations);
1762                     }
1763                     scan(e.getParameters(), p);
1764                     return null;
1765                 }
1766                 @Override public Void visitRecordComponent(RecordComponentElement e, Void p) {
1767                     int currentTAs = numberOfTypeAnnotations;
1768                     verifyTypeAnnotations(e.asType().getAnnotationMirrors());
1769                     Assert.check(currentTAs + 1 == numberOfTypeAnnotations);
1770                     return null;
1771                 }
1772             }.scan(rootElement, null);
1773         }
1774 
1775         private void verifyTypeAnnotations(Iterable<? extends AnnotationMirror> annotations) {
1776             for (AnnotationMirror mirror : annotations) {
1777                 Assert.check(mirror.toString().startsWith("@Anno"));
1778                 if (mirror instanceof TypeCompound) {
1779                     numberOfTypeAnnotations++;
1780                 }
1781             }
1782         }
1783     }
1784 
1785     @Test
1786     void testMethodsInheritedFromRecordArePublicAndFinal() throws Exception {
1787         int numberOfFieldRefs = 0;
1788         File dir = assertOK(true, "record R() {}");
1789         for (final File fileEntry : Objects.requireNonNull(dir.listFiles())) {
1790             if (fileEntry.getName().equals("R.class")) {
1791                 ClassModel classFile = Classfile.of().parse(fileEntry.toPath());
1792                 for (MethodModel method : classFile.methods())
1793                     switch (method.methodName().stringValue()) {
1794                         case "toString", "equals", "hashCode" ->
1795                             Assert.check(((method.flags().flagsMask() & Classfile.ACC_PUBLIC) != 0) && ((method.flags().flagsMask() & Classfile.ACC_FINAL) != 0));
1796                         default -> {}
1797                     }
1798             }
1799         }
1800     }
1801 
1802     private static final List<String> ACCESSIBILITY = List.of(
1803             "public", "protected", "", "private");
1804 
1805     @Test
1806     void testCanonicalAccessibility() throws Exception {
1807         // accessibility of canonical can't be stronger than that of the record type
1808         for (String a1 : ACCESSIBILITY) {
1809             for (String a2 : ACCESSIBILITY) {
1810                 if (protection(a2) > protection(a1)) {
1811                     assertFail("compiler.err.invalid.canonical.constructor.in.record", "class R {# record RR() { # RR {} } }", a1, a2);
1812                 } else {
1813                     assertOK("class R {# record RR() { # RR {} } }", a1, a2);
1814                 }
1815             }
1816         }
1817 
1818         // now lets check that when compiler the compiler generates the canonical, it has the same accessibility
1819         // as the record type
1820         for (String a : ACCESSIBILITY) {
1821             File dir = assertOK(true, "class R {# record RR() {} }", a);
1822             for (final File fileEntry : Objects.requireNonNull(dir.listFiles())) {
1823                 if (fileEntry.getName().equals("R$RR.class")) {
1824                     ClassModel classFile = Classfile.of().parse(fileEntry.toPath());
1825                     for (MethodModel method : classFile.methods())
1826                         if (method.methodName().equalsString("<init>")) {
1827                             Assert.check(method.flags().flagsMask() == accessFlag(a),
1828                                     "was expecting access flag " + accessFlag(a) + " but found " + method.flags().flagsMask());
1829                         }
1830                 }
1831             }
1832         }
1833     }
1834 
1835     private int protection(String access) {
1836         return switch (access) {
1837             case "private" -> 3;
1838             case "protected" -> 1;
1839             case "public" -> 0;
1840             case "" -> 2;
1841             default -> throw new AssertionError();
1842         };
1843     }
1844 
1845     private int accessFlag(String access) {
1846         return switch (access) {
1847             case "private" -> Classfile.ACC_PRIVATE;
1848             case "protected" -> Classfile.ACC_PROTECTED;
1849             case "public" -> Classfile.ACC_PUBLIC;
1850             case "" -> 0;
1851             default -> throw new AssertionError();
1852         };
1853     }
1854 
1855     @Test
1856     void testSameArity() {
1857         for (String source : List.of(
1858                 """
1859                 record R(int... args) {
1860                     public R(int... args) {
1861                         this.args = args;
1862                     }
1863                 }
1864                 """,
1865                 """
1866                 record R(int[] args) {
1867                     public R(int[] args) {
1868                         this.args = args;
1869                     }
1870                 }
1871                 """,
1872                 """
1873                 record R(@A int... ints) {}
1874 
1875                 @java.lang.annotation.Target({
1876                         java.lang.annotation.ElementType.TYPE_USE,
1877                         java.lang.annotation.ElementType.RECORD_COMPONENT})
1878                 @interface A {}
1879                 """,
1880                 """
1881                 record R(@A int... ints) {
1882                     R(@A int... ints) {
1883                         this.ints = ints;
1884                     }
1885                 }
1886 
1887                 @java.lang.annotation.Target({
1888                         java.lang.annotation.ElementType.TYPE_USE,
1889                         java.lang.annotation.ElementType.RECORD_COMPONENT})
1890                 @interface A {}
1891                 """
1892         )) {
1893             assertOK(source);
1894         }
1895 
1896         for (String source : List.of(
1897                 """
1898                 record R(int... args) {
1899                     public R(int[] args) {
1900                         this.args = args;
1901                     }
1902                 }
1903                 """,
1904                 """
1905                 record R(int... args) {
1906                     public R(int[] args) {
1907                         this.args = args;
1908                     }
1909                 }
1910                 """,
1911                 """
1912                 record R(String... args) {
1913                     public R(String[] args) {
1914                         this.args = args;
1915                     }
1916                 }
1917                 """,
1918                 """
1919                 record R(String... args) {
1920                     public R(String[] args) {
1921                         this.args = args;
1922                     }
1923                 }
1924                 """
1925         )) {
1926             assertFail("compiler.err.invalid.canonical.constructor.in.record", source);
1927         }
1928     }
1929 
1930     @Test
1931     void testSafeVararsAnno() {
1932         assertFail("compiler.err.annotation.type.not.applicable",
1933                 """
1934                 @SafeVarargs
1935                 record R<T>(T... t) {}
1936                 """,
1937                 """
1938                 @SafeVarargs
1939                 record R<T>(T... t) {
1940                     R(T... t) {
1941                         this.t = t;
1942                     }
1943                 }
1944                 """
1945         );
1946 
1947         assertOK(
1948                 """
1949                 record R<T>(T... t) {
1950                     @SafeVarargs
1951                     R(T... t) {
1952                         this.t = t;
1953                     }
1954                 }
1955                 """
1956         );
1957 
1958         appendCompileOptions("-Xlint:unchecked");
1959         assertOKWithWarning("compiler.warn.unchecked.varargs.non.reifiable.type",
1960                 """
1961                 record R<T>(T... t) {
1962                     R(T... t) {
1963                         this.t = t;
1964                     }
1965                 }
1966                 """
1967         );
1968         removeLastCompileOptions(1);
1969 
1970         assertOK(
1971                 """
1972                 @SuppressWarnings("unchecked")
1973                 record R<T>(T... t) {
1974                     R(T... t) {
1975                         this.t = t;
1976                     }
1977                 }
1978                 """
1979         );
1980 
1981         assertOK(
1982                 """
1983                 record R<T>(T... t) {
1984                     @SuppressWarnings("unchecked")
1985                     R(T... t) {
1986                         this.t = t;
1987                     }
1988                 }
1989                 """
1990         );
1991     }
1992 
1993     @Test
1994     void testOverrideAtAccessor() {
1995         assertOK(
1996                 """
1997                 record R(int i) {
1998                     @Override
1999                     public int i() { return i; }
2000                 }
2001                 """,
2002                 """
2003                 record R(int i, int j) {
2004                     @Override
2005                     public int i() { return i; }
2006                     public int j() { return j; }
2007                 }
2008                 """,
2009                 """
2010                 interface I { int i(); }
2011                 record R(int i) implements I {
2012                     @Override
2013                     public int i() { return i; }
2014                 }
2015                 """,
2016                 """
2017                 interface I { int i(); }
2018                 record R(int i) implements I {
2019                     public int i() { return i; }
2020                 }
2021                 """,
2022                 """
2023                 interface I { default int i() { return 0; } }
2024                 record R(int i) implements I {
2025                     @Override
2026                     public int i() { return i; }
2027                 }
2028                 """
2029         );
2030     }
2031 
2032     @Test
2033     void testNoAssigmentInsideCompactRecord() {
2034         assertFail("compiler.err.cant.assign.val.to.var",
2035                 """
2036                 record R(int i) {
2037                     R {
2038                         this.i = i;
2039                     }
2040                 }
2041                 """
2042         );
2043         assertFail("compiler.err.cant.assign.val.to.var",
2044                 """
2045                 record R(int i) {
2046                     R {
2047                         (this).i = i;
2048                     }
2049                 }
2050                 """
2051         );
2052     }
2053 
2054     @Test
2055     void testNoNPEStaticAnnotatedFields() {
2056         assertOK(
2057                 """
2058                 import java.lang.annotation.Native;
2059                 record R() {
2060                     @Native public static final int i = 0;
2061                 }
2062                 """
2063         );
2064         assertOK(
2065                 """
2066                 import java.lang.annotation.Native;
2067                 class Outer {
2068                     record R() {
2069                         @Native public static final int i = 0;
2070                     }
2071                 }
2072                 """
2073         );
2074         assertOK(
2075                 """
2076                 import java.lang.annotation.Native;
2077                 class Outer {
2078                     void m() {
2079                         record R () {
2080                             @Native public static final int i = 0;
2081                         }
2082                     }
2083                 }
2084                 """
2085         );
2086     }
2087 
2088     @Test
2089     void testDoNotAllowCStyleArraySyntaxForRecComponents() {
2090         assertFail("compiler.err.record.component.and.old.array.syntax",
2091                 """
2092                 record R(int i[]) {}
2093                 """
2094         );
2095         assertFail("compiler.err.record.component.and.old.array.syntax",
2096                 """
2097                 record R(String s[]) {}
2098                 """
2099         );
2100         assertFail("compiler.err.record.component.and.old.array.syntax",
2101                 """
2102                 record R<T>(T t[]) {}
2103                 """
2104         );
2105     }
2106 
2107     @Test
2108     void testNoWarningForSerializableRecords() {
2109         if (!useAP) {
2110             // don't execute this test when the default annotation processor is on as it will fail due to
2111             // spurious warnings
2112             appendCompileOptions("-Werror", "-Xlint:serial");
2113             assertOK(
2114                     """
2115                     import java.io.*;
2116                     record R() implements java.io.Serializable {}
2117                     """
2118             );
2119             removeLastCompileOptions(2);
2120         }
2121     }
2122 
2123     @Test
2124     void testAnnotationsOnVarargsRecComp() {
2125         assertOK(
2126                 """
2127                 import java.lang.annotation.*;
2128 
2129                 @Target({ElementType.TYPE_USE})
2130                 @interface Simple {}
2131 
2132                 record R(@Simple int... val) {
2133                     static void test() {
2134                         R rec = new R(10, 20);
2135                     }
2136                 }
2137                 """
2138         );
2139         assertOK(
2140                 """
2141                 import java.lang.annotation.*;
2142 
2143                 @Target({ElementType.TYPE_USE})
2144                 @interface SimpleContainer{ Simple[] value(); }
2145 
2146                 @Repeatable(SimpleContainer.class)
2147                 @Target({ElementType.TYPE_USE})
2148                 @interface Simple {}
2149 
2150                 record R(@Simple int... val) {
2151                     static void test() {
2152                         R rec = new R(10, 20);
2153                     }
2154                 }
2155                 """
2156         );
2157     }
2158 
2159     @Test
2160     void testSaveVarargsAnno() {
2161         // the compiler would generate an erronous accessor
2162         assertFail("compiler.err.varargs.invalid.trustme.anno",
2163                 """
2164                 record R(@SafeVarargs String... s) {}
2165                 """
2166         );
2167         // but this is OK
2168         assertOK(
2169                 """
2170                 record R(@SafeVarargs String... s) {
2171                     public String[] s() { return s; }
2172                 }
2173                 """
2174         );
2175     }
2176 }