OpenJDK / jdk / jdk
changeset 48920:916690b5edc9
8194892: add compiler support for local-variable syntax for lambda parameters
Reviewed-by: mcimadamore
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Tue Feb 20 07:51:51 2018 -0800 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java Tue Feb 20 11:45:16 2018 -0500 @@ -43,11 +43,10 @@ import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Error; import com.sun.tools.javac.util.JCDiagnostic.Warning; +import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.JCDiagnostic.Fragment; -import com.sun.tools.javac.util.List; import static com.sun.tools.javac.parser.Tokens.TokenKind.*; import static com.sun.tools.javac.parser.Tokens.TokenKind.ASSERT; @@ -290,9 +289,31 @@ /** Skip forward until a suitable stop token is found. */ - protected void skip(boolean stopAtImport, boolean stopAtMemberDecl, boolean stopAtIdentifier, boolean stopAtStatement) { + protected void skip(boolean stopAtImport, boolean stopAtMemberDecl, boolean stopAtIdentifier, boolean stopAtStatement, boolean stopAtLambdaBody) { while (true) { switch (token.kind) { + case ARROW: + if (stopAtLambdaBody) { + //find end of lambda expression; this could be either a comma or an rparen (if in method context), + //or a semi colon (if in assignment context). + int depth = 0; + while (true) { + switch (token.kind) { + case EOF: + case LBRACE: depth++; break; + case RBRACE: depth--; break; + case COMMA: + case RPAREN: + case SEMI: + if (depth == 0) { + return; + } + break; + } + nextToken(); + } + } + break; case SEMI: nextToken(); return; @@ -1076,7 +1097,8 @@ break; case LPAREN: if (typeArgs == null && (mode & EXPR) != 0) { - ParensResult pres = analyzeParens(); + LambdaClassfier lambdaClassifier = new LambdaClassfier(); + ParensResult pres = analyzeParens(lambdaClassifier); switch (pres) { case CAST: accept(LPAREN); @@ -1095,9 +1117,15 @@ mode = EXPR; JCExpression t1 = term3(); return F.at(pos).TypeCast(t, t1); + case BAD_LAMBDA: + Assert.checkNonNull(lambdaClassifier.diagFragment); + t = syntaxError(pos, List.nil(), Errors.InvalidLambdaParameterDeclaration(lambdaClassifier.diagFragment)); + skip(false, false, false, false, true); + break; case IMPLICIT_LAMBDA: case EXPLICIT_LAMBDA: - t = lambdaExpressionOrStatement(true, pres == ParensResult.EXPLICIT_LAMBDA, pos); + case IMPLICIT_LAMBDA_ALL_VAR: + t = lambdaExpressionOrStatement(true, pres != ParensResult.IMPLICIT_LAMBDA, pos); break; default: //PARENS accept(LPAREN); @@ -1509,15 +1537,17 @@ * matching '>' and see if the subsequent terminal is either '.' or '::'. */ @SuppressWarnings("fallthrough") - ParensResult analyzeParens() { + ParensResult analyzeParens(LambdaClassfier lambdaClassifier) { int depth = 0; boolean type = false; outer: for (int lookahead = 0 ; ; lookahead++) { - TokenKind tk = S.token(lookahead).kind; + Token lookaheadToken = S.token(lookahead); + TokenKind tk = lookaheadToken.kind; switch (tk) { case COMMA: type = true; - case EXTENDS: case SUPER: case DOT: case AMP: + break; + case FINAL: case EXTENDS: case SUPER: case DOT: case AMP: //skip break; case QUES: @@ -1534,7 +1564,8 @@ return ParensResult.CAST; } else if (peekToken(lookahead, LAX_IDENTIFIER)) { //Type, Identifier/'_'/'assert'/'enum' -> explicit lambda - return ParensResult.EXPLICIT_LAMBDA; + lambdaClassifier.addExplicitParameter(); + lookahead++; //skip Identifier } break; case LPAREN: @@ -1547,24 +1578,29 @@ } break; case RPAREN: - // if we have seen something that looks like a type, - // then it's a cast expression - if (type) return ParensResult.CAST; - // otherwise, disambiguate cast vs. parenthesized expression - // based on subsequent token. - switch (S.token(lookahead + 1).kind) { - /*case PLUSPLUS: case SUBSUB: */ - case BANG: case TILDE: - case LPAREN: case THIS: case SUPER: - case INTLITERAL: case LONGLITERAL: case FLOATLITERAL: - case DOUBLELITERAL: case CHARLITERAL: case STRINGLITERAL: - case TRUE: case FALSE: case NULL: - case NEW: case IDENTIFIER: case ASSERT: case ENUM: case UNDERSCORE: - case BYTE: case SHORT: case CHAR: case INT: - case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID: - return ParensResult.CAST; - default: - return ParensResult.PARENS; + if (peekToken(lookahead, ARROW)) { + //this is a lambda + return lambdaClassifier.result(); + } else { + // if we have seen something that looks like a type, + // then it's a cast expression + if (type) return ParensResult.CAST; + // otherwise, disambiguate cast vs. parenthesized expression + // based on subsequent token. + switch (S.token(lookahead + 1).kind) { + /*case PLUSPLUS: case SUBSUB: */ + case BANG: case TILDE: + case LPAREN: case THIS: case SUPER: + case INTLITERAL: case LONGLITERAL: case FLOATLITERAL: + case DOUBLELITERAL: case CHARLITERAL: case STRINGLITERAL: + case TRUE: case FALSE: case NULL: + case NEW: case IDENTIFIER: case ASSERT: case ENUM: case UNDERSCORE: + case BYTE: case SHORT: case CHAR: case INT: + case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID: + return ParensResult.CAST; + default: + return ParensResult.PARENS; + } } case UNDERSCORE: case ASSERT: @@ -1572,17 +1608,23 @@ case IDENTIFIER: if (peekToken(lookahead, LAX_IDENTIFIER)) { // Identifier, Identifier/'_'/'assert'/'enum' -> explicit lambda - return ParensResult.EXPLICIT_LAMBDA; - } else if (peekToken(lookahead, RPAREN, ARROW)) { - // Identifier, ')' '->' -> implicit lambda - return ParensResult.IMPLICIT_LAMBDA; + if (Feature.LOCAL_VARIABLE_TYPE_INFERENCE.allowedInSource(source) && + lookaheadToken.name() == names.var) { + lambdaClassifier.addImplicitVarParameter(); + } else { + lambdaClassifier.addExplicitParameter(); + } + lookahead++; //skip Identifier + } else if ((!type && peekToken(lookahead, COMMA)) || peekToken(lookahead, RPAREN)) { + lambdaClassifier.addImplicitParameter(); } type = false; break; - case FINAL: case ELLIPSIS: - //those can only appear in explicit lambdas - return ParensResult.EXPLICIT_LAMBDA; + //those can only appear in explicit, non-var, lambdas + lambdaClassifier.addExplicitParameter(); + lookahead++; //skip Identifier + break; case MONKEYS_AT: type = true; lookahead += 1; //skip '@' @@ -1614,7 +1656,9 @@ case LBRACKET: if (peekToken(lookahead, RBRACKET, LAX_IDENTIFIER)) { // '[', ']', Identifier/'_'/'assert'/'enum' -> explicit lambda - return ParensResult.EXPLICIT_LAMBDA; + lambdaClassifier.addExplicitParameter(); + lookahead += 2; //skip ']' Identifier + break; } else if (peekToken(lookahead, RBRACKET, RPAREN) || peekToken(lookahead, RBRACKET, AMP)) { // '[', ']', ')' -> cast @@ -1643,12 +1687,15 @@ // '>', '&' -> cast return ParensResult.CAST; } else if (peekToken(lookahead, LAX_IDENTIFIER, COMMA) || - peekToken(lookahead, LAX_IDENTIFIER, RPAREN, ARROW) || - peekToken(lookahead, ELLIPSIS)) { + peekToken(lookahead, LAX_IDENTIFIER, RPAREN, ARROW)) { // '>', Identifier/'_'/'assert'/'enum', ',' -> explicit lambda // '>', Identifier/'_'/'assert'/'enum', ')', '->' -> explicit lambda + lambdaClassifier.addExplicitParameter(); + lookahead++; //skip Identifier + break; + } else if (peekToken(lookahead, ELLIPSIS)) { // '>', '...' -> explicit lambda - return ParensResult.EXPLICIT_LAMBDA; + break; //this is handled in the outer switch } //it looks a type, but could still be (i) a cast to generic type, //(ii) an unbound method reference or (iii) an explicit lambda @@ -1666,6 +1713,61 @@ } } + class LambdaClassfier { + ParensResult kind; + Fragment diagFragment; + + 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; + } + } + /** Accepts all identifier-like tokens */ protected Filter<TokenKind> LAX_IDENTIFIER = t -> t == IDENTIFIER || t == UNDERSCORE || t == ASSERT || t == ENUM; @@ -1673,7 +1775,15 @@ CAST, EXPLICIT_LAMBDA, IMPLICIT_LAMBDA, - PARENS + IMPLICIT_LAMBDA_ALL_VAR, + BAD_LAMBDA, + PARENS; + } + + enum BAD_LAMBDA_KIND { + EXPLICIT_AND_VAR, + IMPLICIT_AND_VAR, + EXPLICIT_AND_IMPLICIT, } JCExpression lambdaExpressionOrStatement(boolean hasParens, boolean explicitParams, int pos) { @@ -1896,6 +2006,9 @@ List<JCAnnotation> nextLevelAnnotations = typeAnnotationsOpt(); if (token.kind == LBRACKET) { + if (t == null) { + log.error(token.pos, Errors.VarNotAllowedArray); + } int pos = token.pos; nextToken(); t = bracketsOptCont(t, pos, nextLevelAnnotations); @@ -2294,7 +2407,7 @@ if (token.pos == lastErrPos) return stats.toList(); if (token.pos <= endPosTable.errorEndPos) { - skip(false, true, true, true); + skip(false, true, true, true, false); lastErrPos = token.pos; } stats.addAll(stat); @@ -3133,7 +3246,7 @@ while (token.kind != EOF) { if (token.pos <= endPosTable.errorEndPos) { // error recovery - skip(checkForImports, false, false, false); + skip(checkForImports, false, false, false, false); if (token.kind == EOF) break; } @@ -3270,7 +3383,7 @@ defs.append(toP(F.at(pos).Provides(serviceName, implNames))); } else { log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.ExpectedStr("'" + names.with + "'")); - skip(false, false, false, false); + skip(false, false, false, false, false); } } else if (token.name() == names.uses) { nextToken(); @@ -3475,7 +3588,7 @@ false)); if (token.pos <= endPosTable.errorEndPos) { // error recovery - skip(false, true, true, false); + skip(false, true, true, false, false); } } } @@ -3537,7 +3650,7 @@ accept(LBRACE); if (token.pos <= endPosTable.errorEndPos) { // error recovery - skip(false, true, false, false); + skip(false, true, false, false, false); if (token.kind == LBRACE) nextToken(); } @@ -3546,7 +3659,7 @@ defs.appendList(classOrInterfaceBodyDeclaration(className, isInterface)); if (token.pos <= endPosTable.errorEndPos) { // error recovery - skip(false, true, true, false); + skip(false, true, true, false, false); } } accept(RBRACE); @@ -3713,7 +3826,7 @@ accept(SEMI); if (token.pos <= endPosTable.errorEndPos) { // error recovery - skip(false, true, false, false); + skip(false, true, false, false, false); if (token.kind == LBRACE) { body = block(); } @@ -3947,7 +4060,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) { @@ -3964,6 +4077,10 @@ } typeAnnotationsPushedBack = List.nil(); } + if (lambdaParameter && isRestrictedLocalVarTypeName(type)) { + //implicit lambda parameter type (with 'var') + type = null; + } return variableDeclaratorId(mods, type, lambdaParameter); }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Tue Feb 20 07:51:51 2018 -0800 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Tue Feb 20 11:45:16 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''
--- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java Tue Feb 20 07:51:51 2018 -0800 +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java Tue Feb 20 11:45:16 2018 -0500 @@ -104,7 +104,7 @@ while (token.kind != EOF) { if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) { // error recovery - skip(true, false, false, false); + skip(true, false, false, false, false); if (token.kind == EOF) { break; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/diags/examples/BracketsNotAllowedImplicitLambda.java Tue Feb 20 11:45:16 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 Tue Feb 20 11:45:16 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 Tue Feb 20 11:45:16 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 Tue Feb 20 11:45:16 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 Tue Feb 20 11:45:16 2018 -0500 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017, 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/8131742/T8131742.out Tue Feb 20 07:51:51 2018 -0800 +++ b/test/langtools/tools/javac/lambda/8131742/T8131742.out Tue Feb 20 11:45:16 2018 -0500 @@ -1,4 +1,4 @@ -T8131742.java:8:38: compiler.err.expected3: ',', ')', '[' +T8131742.java:8:31: compiler.err.expected: ')' T8131742.java:8:39: compiler.err.this.as.identifier -T8131742.java:8:43: compiler.err.expected: ';' +T8131742.java:8:43: compiler.err.expected: token.identifier 3 errors
--- a/test/langtools/tools/javac/lambda/LambdaParserTest.java Tue Feb 20 07:51:51 2018 -0800 +++ b/test/langtools/tools/javac/lambda/LambdaParserTest.java Tue Feb 20 11:45:16 2018 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -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 Tue Feb 20 07:51:51 2018 -0800 +++ b/test/langtools/tools/javac/lvti/ParserTest.java Tue Feb 20 11:45:16 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 Tue Feb 20 07:51:51 2018 -0800 +++ b/test/langtools/tools/javac/lvti/ParserTest.out Tue Feb 20 11:45:16 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
--- a/test/langtools/tools/javac/parser/extend/TrialParser.java Tue Feb 20 07:51:51 2018 -0800 +++ b/test/langtools/tools/javac/parser/extend/TrialParser.java Tue Feb 20 11:45:16 2018 -0500 @@ -104,7 +104,7 @@ while (token.kind != EOF) { if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) { // error recovery - skip(true, false, false, false); + skip(true, false, false, false, false); if (token.kind == EOF) { break; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01.java Tue Feb 20 11:45:16 2018 -0500 @@ -0,0 +1,17 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8194892 + * @summary add 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; + IntBinaryOperator f2 = (var x, y) -> x + y; + IntBinaryOperator f3 = (int x, var y) -> x + y; + IntBinaryOperator f4 = (int x, y) -> x + y; + + BiFunction<String[], String, String> f5 = (var s1[], var s2) -> s2; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01.out Tue Feb 20 11:45:16 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:54: compiler.err.var.not.allowed.array +5 errors