8198512: compiler support for local-variable syntax for lambda parameters
Reviewed-by: mcimadamore
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Thu Feb 22 12:26:01 2018 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Thu Feb 22 15:49:32 2018 -0500
@@ -1673,6 +1673,8 @@
CAST,
EXPLICIT_LAMBDA,
IMPLICIT_LAMBDA,
+ IMPLICIT_LAMBDA_ALL_VAR,
+ BAD_LAMBDA,
PARENS
}
@@ -1681,9 +1683,92 @@
formalParameters(true) :
implicitParameters(hasParens);
+ if (explicitParams) {
+ LambdaClassfier lambdaClassfier = new LambdaClassfier();
+ for (JCVariableDecl param: params) {
+ if (param.vartype != null &&
+ isRestrictedLocalVarTypeName(param.vartype) &&
+ param.vartype.hasTag(TYPEARRAY)) {
+ log.error(DiagnosticFlag.SYNTAX, param.pos, Errors.VarNotAllowedArray);
+ }
+ if (param.vartype != null && param.name != names.empty) {
+ if (isRestrictedLocalVarTypeName(param.vartype)) {
+ lambdaClassfier.addImplicitVarParameter();
+ } else {
+ lambdaClassfier.addExplicitParameter();
+ }
+ }
+ if (param.vartype == null && param.name != names.empty ||
+ param.vartype != null && param.name == names.empty) {
+ lambdaClassfier.addImplicitParameter();
+ }
+ if (lambdaClassfier.result() == ParensResult.BAD_LAMBDA) {
+ break;
+ }
+ }
+ if (lambdaClassfier.diagFragment != null) {
+ log.error(DiagnosticFlag.SYNTAX, pos, Errors.InvalidLambdaParameterDeclaration(lambdaClassfier.diagFragment));
+ }
+ }
return lambdaExpressionOrStatementRest(params, pos);
}
+ class LambdaClassfier {
+ ParensResult kind; //ParensResult.EXPLICIT_LAMBDA;
+ Fragment diagFragment;
+ List<JCVariableDecl> params;
+
+ void addExplicitParameter() {
+ reduce(ParensResult.EXPLICIT_LAMBDA);
+ }
+
+ void addImplicitVarParameter() {
+ reduce(ParensResult.IMPLICIT_LAMBDA_ALL_VAR);
+ }
+
+ void addImplicitParameter() {
+ reduce(ParensResult.IMPLICIT_LAMBDA);
+ }
+
+ private void reduce(ParensResult newKind) {
+ if (kind == null) {
+ kind = newKind;
+ } else if (kind != newKind && kind != ParensResult.BAD_LAMBDA) {
+ ParensResult currentKind = kind;
+ kind = ParensResult.BAD_LAMBDA;
+ switch (currentKind) {
+ case EXPLICIT_LAMBDA:
+ if (newKind == ParensResult.IMPLICIT_LAMBDA) {
+ diagFragment = Fragments.ImplicitAndExplicitNotAllowed;
+ } else if (newKind == ParensResult.IMPLICIT_LAMBDA_ALL_VAR) {
+ diagFragment = Fragments.VarAndExplicitNotAllowed;
+ }
+ break;
+ case IMPLICIT_LAMBDA:
+ if (newKind == ParensResult.EXPLICIT_LAMBDA) {
+ diagFragment = Fragments.ImplicitAndExplicitNotAllowed;
+ } else if (newKind == ParensResult.IMPLICIT_LAMBDA_ALL_VAR) {
+ diagFragment = Fragments.VarAndImplicitNotAllowed;
+ }
+ break;
+ case IMPLICIT_LAMBDA_ALL_VAR:
+ if (newKind == ParensResult.EXPLICIT_LAMBDA) {
+ diagFragment = Fragments.VarAndExplicitNotAllowed;
+ } else if (newKind == ParensResult.IMPLICIT_LAMBDA) {
+ diagFragment = Fragments.VarAndImplicitNotAllowed;
+ }
+ break;
+ default:
+ throw new AssertionError("unexpected option for field kind");
+ }
+ }
+ }
+
+ ParensResult result() {
+ return kind;
+ }
+ }
+
JCExpression lambdaExpressionOrStatementRest(List<JCVariableDecl> args, int pos) {
checkSourceLevel(Feature.LAMBDA);
accept(ARROW);
@@ -3044,7 +3129,21 @@
return toP(F.at(pos).ReceiverVarDef(mods, pn, type));
}
} else {
- name = ident();
+ if (!lambdaParameter ||
+ LAX_IDENTIFIER.accepts(token.kind) ||
+ mods.flags != Flags.PARAMETER ||
+ mods.annotations.nonEmpty()) {
+ name = ident();
+ } else {
+ /** if it is a lambda parameter and the token kind is not an identifier,
+ * and there are no modifiers or annotations, then this means that the compiler
+ * supposed the lambda to be explicit but it can contain a mix of implicit,
+ * var or explicit parameters. So we assign the error name to the parameter name
+ * instead of issuing an error and analyze the lambda parameters as a whole at
+ * a higher level.
+ */
+ name = names.empty;
+ }
}
}
if ((mods.flags & Flags.VARARGS) != 0 &&
@@ -3947,7 +4046,7 @@
// need to distinguish between vararg annos and array annos
// look at typeAnnotationsPushedBack comment
this.permitTypeAnnotationsPushBack = true;
- JCExpression type = parseType();
+ JCExpression type = parseType(lambdaParameter);
this.permitTypeAnnotationsPushBack = false;
if (token.kind == ELLIPSIS) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Thu Feb 22 12:26:01 2018 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Thu Feb 22 15:49:32 2018 -0500
@@ -1229,6 +1229,20 @@
compiler.err.var.not.allowed.compound=\
''var'' is not allowed in a compound declaration
+# 0: fragment
+compiler.err.invalid.lambda.parameter.declaration=\
+ invalid lambda parameter declaration\n\
+ ({0})
+
+compiler.misc.implicit.and.explicit.not.allowed=\
+ cannot mix implicitly-typed and explicitly-typed parameters
+
+compiler.misc.var.and.explicit.not.allowed=\
+ cannot mix ''var'' and explicitly-typed parameters
+
+compiler.misc.var.and.implicit.not.allowed=\
+ cannot mix ''var'' and implicitly-typed parameters
+
compiler.misc.local.cant.infer.null=\
variable initializer is ''null''
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/diags/examples/BracketsNotAllowedImplicitLambda.java Thu Feb 22 15:49:32 2018 -0500
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.err.var.not.allowed.array
+
+import java.util.function.*;
+
+class BracketsNotAllowedImplicitLambda {
+ BiFunction<String[], String, String> f = (var s1[], var s2) -> s2;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/diags/examples/ExplicitImplicitLambda.java Thu Feb 22 15:49:32 2018 -0500
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.err.invalid.lambda.parameter.declaration
+// key: compiler.misc.implicit.and.explicit.not.allowed
+
+import java.util.function.*;
+
+class ExplicitImplicitLambda {
+ IntBinaryOperator f = (int x, y) -> x + y;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/diags/examples/VarAllOrNothing.java Thu Feb 22 15:49:32 2018 -0500
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.err.invalid.lambda.parameter.declaration
+// key: compiler.misc.var.and.implicit.not.allowed
+
+import java.util.function.*;
+
+class VarAllOrNothing {
+ IntBinaryOperator f = (x, var y) -> x + y;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/diags/examples/VarExplicitLambda.java Thu Feb 22 15:49:32 2018 -0500
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.err.invalid.lambda.parameter.declaration
+// key: compiler.misc.var.and.explicit.not.allowed
+
+import java.util.function.*;
+
+class VarExplicitLambda {
+ IntBinaryOperator f = (int x, var y) -> x + y;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/diags/examples/VarNotAllowedExplicitLambda.java Thu Feb 22 15:49:32 2018 -0500
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// key: compiler.err.invalid.lambda.parameter.declaration
+// key: compiler.misc.var.and.explicit.not.allowed
+
+class VarNotAllowedExplicitLambda {
+ F f = (String s, var v)->{};
+}
--- a/test/langtools/tools/javac/lambda/LambdaParserTest.java Thu Feb 22 12:26:01 2018 -0800
+++ b/test/langtools/tools/javac/lambda/LambdaParserTest.java Thu Feb 22 15:49:32 2018 -0500
@@ -40,6 +40,7 @@
*/
import java.io.IOException;
+import java.util.Arrays;
import combo.ComboInstance;
import combo.ComboParameter;
@@ -105,26 +106,44 @@
}
}
+ enum SourceKind {
+ SOURCE_9("9"),
+ SOURCE_10("10");
+
+ String sourceNumber;
+
+ SourceKind(String sourceNumber) {
+ this.sourceNumber = sourceNumber;
+ }
+ }
+
enum LambdaParameterKind implements ComboParameter {
- IMPLICIT(""),
- EXPLIICT_SIMPLE("A"),
- EXPLIICT_SIMPLE_ARR1("A[]"),
- EXPLIICT_SIMPLE_ARR2("A[][]"),
- EXPLICIT_VARARGS("A..."),
- EXPLICIT_GENERIC1("A<X>"),
- EXPLICIT_GENERIC2("A<? extends X, ? super Y>"),
- EXPLICIT_GENERIC2_VARARGS("A<? extends X, ? super Y>..."),
- EXPLICIT_GENERIC2_ARR1("A<? extends X, ? super Y>[]"),
- EXPLICIT_GENERIC2_ARR2("A<? extends X, ? super Y>[][]");
+
+ IMPLICIT_1("", ExplicitKind.IMPLICIT),
+ IMPLICIT_2("var", ExplicitKind.IMPLICIT_VAR),
+ EXPLIICT_SIMPLE("A", ExplicitKind.EXPLICIT),
+ EXPLIICT_SIMPLE_ARR1("A[]", ExplicitKind.EXPLICIT),
+ EXPLIICT_SIMPLE_ARR2("A[][]", ExplicitKind.EXPLICIT),
+ EXPLICIT_VARARGS("A...", ExplicitKind.EXPLICIT),
+ EXPLICIT_GENERIC1("A<X>", ExplicitKind.EXPLICIT),
+ EXPLICIT_GENERIC2("A<? extends X, ? super Y>", ExplicitKind.EXPLICIT),
+ EXPLICIT_GENERIC2_VARARGS("A<? extends X, ? super Y>...", ExplicitKind.EXPLICIT),
+ EXPLICIT_GENERIC2_ARR1("A<? extends X, ? super Y>[]", ExplicitKind.EXPLICIT),
+ EXPLICIT_GENERIC2_ARR2("A<? extends X, ? super Y>[][]", ExplicitKind.EXPLICIT);
+
+ enum ExplicitKind {
+ IMPLICIT,
+ IMPLICIT_VAR,
+ EXPLICIT;
+ }
String parameterType;
+ ExplicitKind explicitKind;
- LambdaParameterKind(String parameterType) {
+
+ LambdaParameterKind(String parameterType, ExplicitKind ekind) {
this.parameterType = parameterType;
- }
-
- boolean explicit() {
- return this != IMPLICIT;
+ this.explicitKind = ekind;
}
boolean isVarargs() {
@@ -136,12 +155,23 @@
public String expand(String optParameter) {
return parameterType;
}
+
+ ExplicitKind explicitKind(SourceKind sk) {
+ switch (explicitKind) {
+ case IMPLICIT_VAR:
+ return (sk == SourceKind.SOURCE_9) ?
+ ExplicitKind.EXPLICIT : ExplicitKind.IMPLICIT_VAR;
+ default:
+ return explicitKind;
+ }
+ }
}
enum ModifierKind implements ComboParameter {
NONE(""),
FINAL("final"),
- PUBLIC("public");
+ PUBLIC("public"),
+ ANNO("@A");
String modifier;
@@ -152,7 +182,8 @@
boolean compatibleWith(LambdaParameterKind pk) {
switch (this) {
case PUBLIC: return false;
- case FINAL: return pk != LambdaParameterKind.IMPLICIT;
+ case ANNO:
+ case FINAL: return pk != LambdaParameterKind.IMPLICIT_1;
case NONE: return true;
default: throw new AssertionError("Invalid modifier kind " + this);
}
@@ -208,6 +239,7 @@
new ComboTestHelper<LambdaParserTest>()
.withFilter(LambdaParserTest::redundantTestFilter)
.withFilter(LambdaParserTest::badImplicitFilter)
+ .withDimension("SOURCE", (x, sk) -> x.sk = sk, SourceKind.values())
.withDimension("LAMBDA", (x, lk) -> x.lk = lk, LambdaKind.values())
.withDimension("NAME", (x, name) -> x.pn = name, LambdaParameterName.values())
.withArrayDimension("TYPE", (x, type, idx) -> x.pks[idx] = type, 2, LambdaParameterKind.values())
@@ -221,6 +253,7 @@
ModifierKind[] mks = new ModifierKind[2];
LambdaKind lk;
LambdaParameterName pn;
+ SourceKind sk;
boolean badImplicitFilter() {
return !(mks[0] != ModifierKind.NONE && lk.isShort());
@@ -240,13 +273,15 @@
return true;
}
- String template = "class Test {\n" +
- " SAM s = #{EXPR};\n" +
- "}";
+ String template = "@interface A { }\n" +
+ "class Test {\n" +
+ " SAM s = #{EXPR};\n" +
+ "}";
@Override
public void doWork() throws IOException {
newCompilationTask()
+ .withOptions(Arrays.asList("-source", sk.sourceNumber))
.withSourceFromTemplate(template)
.parse(this::check);
}
@@ -256,7 +291,7 @@
(lk.arity() > 1 && !mks[1].compatibleWith(pks[1]));
if (lk.arity() == 2 &&
- (pks[0].explicit() != pks[1].explicit() ||
+ (pks[0].explicitKind(sk) != pks[1].explicitKind(sk) ||
pks[0].isVarargs())) {
errorExpected = true;
}
--- a/test/langtools/tools/javac/lvti/ParserTest.java Thu Feb 22 12:26:01 2018 -0800
+++ b/test/langtools/tools/javac/lvti/ParserTest.java Thu Feb 22 15:49:32 2018 -0500
@@ -60,7 +60,7 @@
List<? extends var> l2; //error
List<? super var> l3; //error
try {
- Function<var, String> f = (var x2) -> ""; //error
+ Function<var, String> f = (var x2) -> ""; //ok
} catch (var ex) { } //error
}
--- a/test/langtools/tools/javac/lvti/ParserTest.out Thu Feb 22 12:26:01 2018 -0800
+++ b/test/langtools/tools/javac/lvti/ParserTest.out Thu Feb 22 15:49:32 2018 -0500
@@ -18,8 +18,7 @@
ParserTest.java:60:24: compiler.err.var.not.allowed.here
ParserTest.java:61:22: compiler.err.var.not.allowed.here
ParserTest.java:63:22: compiler.err.var.not.allowed.here
-ParserTest.java:63:40: compiler.err.var.not.allowed.here
ParserTest.java:64:18: compiler.err.var.not.allowed.here
ParserTest.java:68:35: compiler.err.var.not.allowed.here
ParserTest.java:69:22: compiler.err.var.not.allowed.here
-24 errors
+23 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01.java Thu Feb 22 15:49:32 2018 -0500
@@ -0,0 +1,18 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8198512
+ * @summary compiler support for local-variable syntax for lambda parameters
+ * @compile/fail/ref=VarInImplicitLambdaNegTest01.out -XDrawDiagnostics VarInImplicitLambdaNegTest01.java
+ */
+
+import java.util.function.*;
+
+class VarInImplicitLambdaNegTest01 {
+ IntBinaryOperator f1 = (x, var y) -> x + y; // error implicit and var
+ IntBinaryOperator f2 = (var x, y) -> x + y; // error var and implicit
+ IntBinaryOperator f3 = (int x, var y) -> x + y; // error var and explicit
+ IntBinaryOperator f4 = (int x, y) -> x + y; // error explicit and implicit
+
+ BiFunction<String[], String, String> f5 = (var s1[], var s2) -> s2; // error var and array
+ BiFunction<Function<String, String>, String, String> f = (Function<String, String> s1, String s2) -> s2; // ok
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01.out Thu Feb 22 15:49:32 2018 -0500
@@ -0,0 +1,6 @@
+VarInImplicitLambdaNegTest01.java:11:28: compiler.err.invalid.lambda.parameter.declaration: (compiler.misc.var.and.implicit.not.allowed)
+VarInImplicitLambdaNegTest01.java:12:28: compiler.err.invalid.lambda.parameter.declaration: (compiler.misc.var.and.implicit.not.allowed)
+VarInImplicitLambdaNegTest01.java:13:28: compiler.err.invalid.lambda.parameter.declaration: (compiler.misc.var.and.explicit.not.allowed)
+VarInImplicitLambdaNegTest01.java:14:28: compiler.err.invalid.lambda.parameter.declaration: (compiler.misc.implicit.and.explicit.not.allowed)
+VarInImplicitLambdaNegTest01.java:16:52: compiler.err.var.not.allowed.array
+5 errors