diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index 0e3a2c0b1db..93c4eaa03ee 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -199,7 +199,27 @@ public class TransPatterns extends TreeTranslator { @Override public void visitTypeTest(JCInstanceOf tree) { - if (tree.pattern instanceof JCPattern pattern) { + // Translates regular instanceof type operation to instanceof pattern operator when + // the expression was originally T but was subsequently erased to Object. + // + // $expr instanceof $primitiveType + // => + // $expr instanceof T $temp && $temp instanceof $primitiveType + if (tree.erasedExprOriginalType!=null && !types.isSameType(tree.expr.type, tree.erasedExprOriginalType)) { + BindingSymbol temp = new BindingSymbol(Flags.FINAL | Flags.SYNTHETIC, + names.fromString("temp" + variableIndex++ + target.syntheticNameChar()), + tree.erasedExprOriginalType, + currentMethodSym); + + JCVariableDecl tempDecl = make.at(tree.pos()).VarDef(temp, null); + + JCTree resultExpr = + makeBinary(Tag.AND, + make.TypeTest(tree.expr, make.BindingPattern(tempDecl).setType(tree.erasedExprOriginalType)).setType(syms.booleanType), + make.TypeTest(make.Ident(tempDecl), tree.pattern).setType(syms.booleanType)); + + result = translate(resultExpr); + } else if (tree.pattern instanceof JCPattern pattern) { //first, resolve any record patterns: JCExpression extraConditions = null; if (pattern instanceof JCRecordPattern recordPattern) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java index d232f2e6d9f..a0b1ef27255 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java @@ -813,8 +813,7 @@ public class TransTypes extends TreeTranslator { Type selsuper = types.supertype(tree.selector.type); boolean enumSwitch = selsuper != null && selsuper.tsym == syms.enumSym; - Type target = enumSwitch ? erasure(tree.selector.type) : syms.intType; - tree.selector = translate(tree.selector, target); + tree.selector = translate(tree.selector, erasure(tree.selector.type)); tree.cases = translateCases(tree.cases); result = tree; } @@ -852,8 +851,7 @@ public class TransTypes extends TreeTranslator { Type selsuper = types.supertype(tree.selector.type); boolean enumSwitch = selsuper != null && selsuper.tsym == syms.enumSym; - Type target = enumSwitch ? erasure(tree.selector.type) : syms.intType; - tree.selector = translate(tree.selector, target); + tree.selector = translate(tree.selector, erasure(tree.selector.type)); tree.cases = translate(tree.cases, tree.type); tree.type = erasure(tree.type); result = retype(tree, tree.type, pt); @@ -1067,8 +1065,14 @@ public class TransTypes extends TreeTranslator { } public void visitTypeTest(JCInstanceOf tree) { - tree.expr = translate(tree.expr, null); tree.pattern = translate(tree.pattern, null); + if (tree.pattern.type.isPrimitive()) { + tree.erasedExprOriginalType = erasure(tree.expr.type); + tree.expr = translate(tree.expr, null); + } + else { + tree.expr = translate(tree.expr, null); + } result = tree; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index 6041da6723a..003f70eeecc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -2272,6 +2272,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { /**{@code true} if this instanceof test should have * value {@code true} when the {@code expr} is {@code null}.*/ public boolean allowNull; + public Type erasedExprOriginalType; + protected JCInstanceOf(JCExpression expr, JCTree pattern) { this.expr = expr; this.pattern = pattern; diff --git a/test/langtools/tools/javac/patterns/PrimitiveTypesInTestingContextErasure.java b/test/langtools/tools/javac/patterns/PrimitiveTypesInTestingContextErasure.java new file mode 100644 index 00000000000..40289f31d56 --- /dev/null +++ b/test/langtools/tools/javac/patterns/PrimitiveTypesInTestingContextErasure.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024, 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. + */ +/* + * @test + * @bug 8341408 + * @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Second Preview) + * @enablePreview + */ + +import java.util.List; + +public class PrimitiveTypesInTestingContextErasure { + public static void main(String[] args) { + erasureSwitch(); + erasureInstanceofTypeComparisonOperator(); + erasureInstanceofPatternMatchingOperator(); + + pollutedInstanceofPatternMatchingOperatorReference(); + pollutedInstanceofPatternMatchingOperator(); + pollutedInstanceofTypeComparisonOperator(); + pollutedSwitch(); + } + + public static void erasureSwitch() { + List ls = List.of((short) 42); + Short s = 42; + + assertTrue(switch(ls.get(0)) { + case int _ -> true; // Short to int + default -> false; + }); + } + + public static void erasureInstanceofTypeComparisonOperator() { + List ls = List.of((short) 42); + + assertTrue(ls.get(0) instanceof int); // Short to int + } + + public static void erasureInstanceofPatternMatchingOperator() { + List ls = List.of((short) 42); + + assertTrue(ls.get(0) instanceof int i); // Short to int + } + + public static void pollutedInstanceofPatternMatchingOperator() { + List ls = (List) List.of("42"); + + assertTrue(!(ls.get(0) instanceof int i)); + } + + public static void pollutedInstanceofTypeComparisonOperator() { + List ls = (List) List.of("42"); + + assertTrue(!(ls.get(0) instanceof int)); + } + + public static void pollutedInstanceofPatternMatchingOperatorReference() { + List ls = (List) List.of("42"); + + assertTrue(!(ls.get(0) instanceof Short)); + } + + public static void pollutedSwitch() { + List ls = (List) List.of("42"); + + try { + var res = switch(ls.get(0)) { + case int _ -> true; + default -> false; + }; + throw new AssertionError("Expected: ClassCastException"); + } catch (ClassCastException e) { + ; + } + } + + static void assertTrue(boolean actual) { + if (!actual) { + throw new AssertionError("Expected: true, but got false"); + } + } +}