From 8bd080bcc9ae3eb368e4bcd5fd0f52408dd9050c Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 19 Nov 2024 09:13:37 +0000 Subject: [PATCH] 8341901: Using 'var' keyword switch pattern matching causes compiler error Reviewed-by: vromero, abimpoudis --- .../com/sun/tools/javac/comp/Attr.java | 2 +- .../com/sun/tools/javac/tree/TreeMaker.java | 10 +- .../patterns/BindingPatternVarTypeModel.java | 94 ++++++++++++++++++- 3 files changed, 102 insertions(+), 4 deletions(-) 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 index f565cff0788..62c12334629 100644 --- 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 @@ -4198,6 +4198,7 @@ public class Attr extends JCTree.Visitor { if (chk.checkUnique(tree.var.pos(), v, env.info.scope)) { chk.checkTransparentVar(tree.var.pos(), v, env.info.scope); } + chk.validate(tree.var.vartype, env, true); if (tree.var.isImplicitlyTyped()) { setSyntheticVariableType(tree.var, type == Type.noType ? syms.errType : type); @@ -4207,7 +4208,6 @@ public class Attr extends JCTree.Visitor { annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v, tree.var.pos()); } annotate.flush(); - chk.validate(tree.var.vartype, env, true); result = tree.type; if (v.isUnnamedVariable()) { matchBindings = MatchBindingsComputer.EMPTY; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java index 218099ac662..0d0852cb91b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -738,12 +738,18 @@ public class TreeMaker implements JCTree.Factory { } /** Create a qualified identifier from a symbol, adding enough qualifications - * to make the reference unique. + * to make the reference unique. The types in the AST nodes will be erased. */ public JCExpression QualIdent(Symbol sym) { - return isUnqualifiable(sym) + JCExpression result = isUnqualifiable(sym) ? Ident(sym) : Select(QualIdent(sym.owner), sym); + + if (sym.kind == TYP) { + result.setType(types.erasure(sym.type)); + } + + return result; } /** Create an identifier that refers to the variable declared in given variable diff --git a/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java b/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java index 8327580a086..e63cbeb2008 100644 --- a/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java +++ b/test/langtools/tools/javac/patterns/BindingPatternVarTypeModel.java @@ -23,18 +23,28 @@ /* * @test - * @bug 8332725 + * @bug 8332725 8341901 * @summary Verify the AST model works correctly for binding patterns with var */ import com.sun.source.tree.BindingPatternTree; import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; import com.sun.source.util.JavacTask; +import com.sun.source.util.TreePathScanner; import com.sun.source.util.TreeScanner; +import com.sun.source.util.Trees; import java.net.URI; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticListener; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; @@ -45,6 +55,7 @@ public class BindingPatternVarTypeModel { public static void main(String... args) throws Exception { new BindingPatternVarTypeModel().run(); + new BindingPatternVarTypeModel().runVarParameterized(); } private void run() throws Exception { @@ -86,4 +97,85 @@ public class BindingPatternVarTypeModel { throw new AssertionError("Didn't find the binding pattern!"); } } + + private void runVarParameterized() throws Exception { + JavaFileObject input = + SimpleJavaFileObject.forSource(URI.create("mem:///Test.java"), + """ + package test; + public class Test { + record R(N.I i) {} + int test(Object o) { + Test.N.I checkType0 = null; + var checkType1 = checkType0; + return switch (o) { + case R(var checkType2) -> 0; + default -> 0; + }; + } + static class N { + interface I {} + } + } + """); + DiagnosticListener noErrors = d -> { + if (d.getKind() == Diagnostic.Kind.ERROR) { + throw new IllegalStateException(d.toString()); + } + }; + JavacTask task = + (JavacTask) compiler.getTask(null, null, noErrors, null, null, List.of(input)); + CompilationUnitTree cut = task.parse().iterator().next(); + Trees trees = Trees.instance(task); + + task.analyze(); + + new TreePathScanner() { + private boolean checkAttributes; + @Override + public Void visitVariable(VariableTree node, Void p) { + boolean prevCheckAttributes = checkAttributes; + try { + checkAttributes |= + node.getName().toString().startsWith("checkType"); + return super.visitVariable(node, p); + } finally { + checkAttributes = prevCheckAttributes; + } + } + + @Override + public Void visitIdentifier(IdentifierTree node, Void p) { + checkType(); + return super.visitIdentifier(node, p); + } + + @Override + public Void visitMemberSelect(MemberSelectTree node, Void p) { + checkType(); + return super.visitMemberSelect(node, p); + } + + private void checkType() { + if (!checkAttributes) { + return ; + } + + TypeMirror type = trees.getTypeMirror(getCurrentPath()); + + if (type.getKind() == TypeKind.PACKAGE) { + return ; //OK + } + if (type.getKind() != TypeKind.DECLARED) { + throw new AssertionError("Expected a declared type, but got: " + + type.getKind()); + } + + if (!((DeclaredType) type).getTypeArguments().isEmpty()) { + throw new AssertionError("Unexpected type arguments: " + + ((DeclaredType) type).getTypeArguments()); + } + } + }.scan(cut, null); + } }