Qualified class name inside qualified instance creation expression

B. Blaser bsrbnd at gmail.com
Fri Feb 1 15:00:08 UTC 2019


Hi,

On Thu, 31 Jan 2019 at 23:05, Alex Buckley <alex.buckley at oracle.com> wrote:
>
> On 1/31/2019 3:33 AM, Tagir Valeev wrote:
> > Consider the following code:
> >
> > class Test {
> >      class Inner {}
> >
> >      void test() {
> >          new Test.Inner(); // compiles successfully
> >          this.new Test.Inner(); // error
> >      }
> > }
>
> Both the unqualified class instance creation expression and the
> qualified class instance creation expression should compile. Each has
> `this` as the immediately enclosing instance.
>
> > javac reports:
> > Test.java:6: error: '(' expected
> >          this.new Test.Inner();
> >                       ^
> > 1 error
> >
> > I read JLS 15.9 [1]:
> > ClassInstanceCreationExpression:
> >      UnqualifiedClassInstanceCreationExpression
> >      ExpressionName . UnqualifiedClassInstanceCreationExpression
> >      Primary . UnqualifiedClassInstanceCreationExpression
> > UnqualifiedClassInstanceCreationExpression:
> >      new [TypeArguments] ClassOrInterfaceTypeToInstantiate (
> > [ArgumentList] ) [ClassBody]
> > ClassOrInterfaceTypeToInstantiate:
> >      {Annotation} Identifier {. {Annotation} Identifier} [TypeArgumentsOrDiamond]
> >
> > Thus this.new Test.Inner() could be parsed as Primary .
> > UnqualifiedClassInstanceCreationExpression where
> > ClassOrInterfaceTypeToInstantiate could be parsed as Identifier .
> > Identifier, so there should not be parse error.
>
> Correct. There has been shimmer in this part of the language. In Java SE
> 7, `this.new Test.Inner()` was illegal; only `this.new SIMPLENAME()` was
> allowed. (See
> https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.9)
> In Java SE 8, the grammar of `ClassInstanceCreationExpression` was
> modified to support type annotations, and `this.new QUALIFIEDNAME()` was
> allowed. In principle, there is no reason to disallow a qualified name
> in a qualified CICE. The phrase `new Test.Inner()` is meaningful as an
> unqualified CICE, and enriching the phrase to be a qualified CICE, with
> an explicit immediately enclosing instance, doesn't make it
> unmeaningful. I suspect javac is still expecting (modulo type arguments)
> a simple name after `new`.

Right, I note that the parser doesn't conform to the current JLS grammar:

http://hg.openjdk.java.net/jdk/jdk/file/c53a3355dbb4/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java#l2272

InnerCreator = [Annotations] Ident [TypeArguments] ClassCreatorRest

It seems 'InnerCreator' is missing '{. [Annotations] Ident}'.
Is there any open JBS issue for this?

I tried a quick fix, here under, in 'JavacParser' and 'Attr' which
solve the problem; any feedback is welcome.
Langtools:tier1 is OK but I'm not sure all is right with type
annotations, etc...
I might dig a bit more and send out a RFR soon but please let me know
the priority of this before investing too much time now?

Thanks,
Bernard


diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -2311,7 +2311,9 @@
             // TODO 308: in <expr>.new C, do we also want to add the
type annotations
             // from expr to the combined type, or not? Yes, do this.
             clazzid1 = make.at(clazz.pos).Select(make.Type(encltype),
-                                                 ((JCIdent) clazzid).name);
+                    clazzid.hasTag(SELECT) ?
+                    ((JCFieldAccess) clazzid).name :
+                    ((JCIdent) clazzid).name);

             EndPosTable endPosTable = this.env.toplevel.endPositions;
             endPosTable.storeEnd(clazzid1, tree.getEndPosition(endPosTable));
@@ -2351,7 +2353,16 @@

         clazztype = chk.checkDiamond(tree, clazztype);
         chk.validate(clazz, localEnv);
-        if (tree.encl != null) {
+        if (tree.encl != null && clazzid.hasTag(SELECT)) {
+            try {
+                Type qualified = attribType(tree.clazz, env);
+                if (!types.isSameType(clazztype, qualified))
+                    log.error(tree.pos(),
Errors.IncomparableTypes(clazztype, qualified));
+            } finally {
+                env.info.isNewClass = false;
+            }
+        }
+        else if (tree.encl != null) {
             // We have to work in this case to store
             // symbol + type back into the attributed tree.
             tree.clazz.type = clazztype;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -2269,7 +2269,7 @@
         }
     }

-    /** InnerCreator = [Annotations] Ident [TypeArguments] ClassCreatorRest
+    /** InnerCreator = [Annotations] Ident {. [Annotations] Ident}
[TypeArguments] ClassCreatorRest
      */
     JCExpression innerCreator(int newpos, List<JCExpression>
typeArgs, JCExpression encl) {
         List<JCAnnotation> newAnnotations = typeAnnotationsOpt();
@@ -2280,6 +2280,15 @@
             t =
toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, t));
         }

+        while (token.kind == DOT) {
+            nextToken();
+            newAnnotations = typeAnnotationsOpt();
+            t = toP(F.at(token.pos).Select(t, ident()));
+            if (newAnnotations.nonEmpty()) {
+                t =
toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, t));
+            }
+        }
+
         if (token.kind == LT) {
             int oldmode = mode;
             t = typeArguments(t, true);
diff --git a/test/langtools/tools/javac/QualifiedAccess/QualifiedAccess_4.out
b/test/langtools/tools/javac/QualifiedAccess/QualifiedAccess_4.out
--- a/test/langtools/tools/javac/QualifiedAccess/QualifiedAccess_4.out
+++ b/test/langtools/tools/javac/QualifiedAccess/QualifiedAccess_4.out
@@ -1,2 +1,2 @@
-QualifiedAccess_4.java:17:28: compiler.err.expected: '('
+QualifiedAccess_4.java:17:19: compiler.err.cant.resolve.location:
kindname.variable, x, , , (compiler.misc.location: kindname.class,
CMain, null)
 1 error


More information about the compiler-dev mailing list