# HG changeset patch # User vromero # Date 1519332572 18000 # Node ID 03ae177c26b016353e5ea1cab6ffd051dfa086ca # Parent 3390b463d7a205d0b95eed3d9ca1c97028a8aea9 8198512: compiler support for local-variable syntax for lambda parameters Reviewed-by: mcimadamore diff -r 3390b463d7a2 -r 03ae177c26b0 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 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 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 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) { diff -r 3390b463d7a2 -r 03ae177c26b0 src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties --- 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'' diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/diags/examples/BracketsNotAllowedImplicitLambda.java --- /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 f = (var s1[], var s2) -> s2; +} diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/diags/examples/ExplicitImplicitLambda.java --- /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; +} diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/diags/examples/VarAllOrNothing.java --- /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; +} diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/diags/examples/VarExplicitLambda.java --- /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; +} diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/diags/examples/VarNotAllowedExplicitLambda.java --- /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)->{}; +} diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/lambda/LambdaParserTest.java --- 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"), - EXPLICIT_GENERIC2("A"), - EXPLICIT_GENERIC2_VARARGS("A..."), - EXPLICIT_GENERIC2_ARR1("A[]"), - EXPLICIT_GENERIC2_ARR2("A[][]"); + + 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", ExplicitKind.EXPLICIT), + EXPLICIT_GENERIC2("A", ExplicitKind.EXPLICIT), + EXPLICIT_GENERIC2_VARARGS("A...", ExplicitKind.EXPLICIT), + EXPLICIT_GENERIC2_ARR1("A[]", ExplicitKind.EXPLICIT), + EXPLICIT_GENERIC2_ARR2("A[][]", 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() .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; } diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/lvti/ParserTest.java --- 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 l2; //error List l3; //error try { - Function f = (var x2) -> ""; //error + Function f = (var x2) -> ""; //ok } catch (var ex) { } //error } diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/lvti/ParserTest.out --- 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 diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01.java --- /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 f5 = (var s1[], var s2) -> s2; // error var and array + BiFunction, String, String> f = (Function s1, String s2) -> s2; // ok +} diff -r 3390b463d7a2 -r 03ae177c26b0 test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01.out --- /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