Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8302344: Compiler Implementation for Unnamed patterns and variables (…
…Preview)

8307444: java.lang.AssertionError when using unnamed patterns
8307482: Compiler should accept var _ in nested patterns in switch case
8307007: Implementation for javax.lang.model for unnamed variables (Preview)
8308312: Compiler should fail when a local variable declaration does not include an Identifier and does not have an initializer
8308309: Compiler should accept mixed masked and unmasked variables in lambda parameters

Co-authored-by: Jan Lahoda <jlahoda@openjdk.org>
Co-authored-by: Aggelos Biboudis <abimpoudis@openjdk.org>
Reviewed-by: vromero, darcy
  • Loading branch information
biboudis and lahodaj committed May 22, 2023
1 parent b588797 commit 8aa5028
Show file tree
Hide file tree
Showing 53 changed files with 1,636 additions and 449 deletions.
Expand Up @@ -70,6 +70,8 @@ public enum Feature {
FOREIGN,
@JEP(number=430, title="String Templates", status="First Preview")
STRING_TEMPLATES,
@JEP(number=443, title="Unnamed Patterns and Variables")
UNNAMED,
/**
* A key for testing.
*/
Expand Down
Expand Up @@ -131,9 +131,10 @@ public interface Element extends javax.lang.model.AnnotatedConstruct {
* {@code java.util.Set<E>} is {@code "Set"}.
*
* If this element represents an unnamed {@linkplain
* PackageElement#getSimpleName package} or unnamed {@linkplain
* ModuleElement#getSimpleName module}, an {@linkplain
* Name##empty_name empty name} is returned.
* PackageElement#getSimpleName package}, an unnamed {@linkplain
* ModuleElement#getSimpleName module} or an unnamed {@linkplain
* VariableElement#getSimpleName variable}, an {@linkplain Name##empty_name empty name}
* is returned.
*
* If it represents a {@linkplain ExecutableElement#getSimpleName
* constructor}, the name "{@code <init>}" is returned. If it
Expand Down
Expand Up @@ -25,6 +25,8 @@

package javax.lang.model.element;

import jdk.internal.javac.PreviewFeature;

import javax.lang.model.util.Elements;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeKind;
Expand Down Expand Up @@ -81,6 +83,9 @@ public interface VariableElement extends Element {
* parameters of the same executable. If the original source
* names are not available, an implementation may synthesize names
* subject to the distinctness requirement above.
*
* <p>For variables, the name of each variable is returned, or an empty name
* if the variable is unnamed.
*/
@Override
Name getSimpleName();
Expand All @@ -93,4 +98,21 @@ public interface VariableElement extends Element {
*/
@Override
Element getEnclosingElement();

/**
* {@return {@code true} if this is an unnamed variable and {@code
* false} otherwise}
*
* @implSpec
* The default implementation of this method calls {@code
* getSimpleName()} and returns {@code true} if the result is
* empty and {@code false} otherwise.
*
* @jls 6.1 Declarations
* @jls 14.4 Local Variable Declarations
*
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED, reflective = true)
default boolean isUnnamed() { return getSimpleName().isEmpty(); }
}
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2023, 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.
*/
package com.sun.source.tree;

import jdk.internal.javac.PreviewFeature;

/**
* A tree node for a binding pattern that matches a pattern
* with a variable of any name and a type of the match candidate;
* an unnamed pattern.
*
* For example the use of underscore {@code _} below:
* <pre>
* if (r instanceof R(_)) {}
* </pre>
*
* @jls 14.30.1 Kinds of Patterns
*
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED)
public interface AnyPatternTree extends PatternTree {
}
8 changes: 8 additions & 0 deletions src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java
Expand Up @@ -227,6 +227,14 @@ public enum Kind {
*/
PARENTHESIZED(ParenthesizedTree.class),

/**
* Used for instances of {@link BindingPatternTree}.
*
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED)
ANY_PATTERN(AnyPatternTree.class),

/**
* Used for instances of {@link BindingPatternTree}.
*
Expand Down
Expand Up @@ -268,6 +268,16 @@ public interface TreeVisitor<R,P> {
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES, reflective=true)
R visitStringTemplate(StringTemplateTree node, P p);

/**
* Visits a {@code AnyPatternTree} node.
* @param node the node being visited
* @param p a parameter value
* @return a result value
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED)
R visitAnyPattern(AnyPatternTree node, P p);

/**
* Visits a {@code BindingPatternTree} node.
* @param node the node being visited
Expand Down
Expand Up @@ -51,7 +51,8 @@ public interface VariableTree extends StatementTree {
ModifiersTree getModifiers();

/**
* Returns the name of the variable being declared.
* Returns the name of the variable being declared or empty name if both the variable
* is unnamed and the preview features are enabled (Unnamed Patterns and Variables).
* @return the name
*/
Name getName();
Expand Down
Expand Up @@ -641,6 +641,22 @@ public R visitStringTemplate(StringTemplateTree node, P p) {
return defaultAction(node, p);
}

/**
* {@inheritDoc}
*
* @implSpec This implementation calls {@code defaultAction}.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
* @since 21
*/
@Override
@PreviewFeature(feature=PreviewFeature.Feature.UNNAMED)
public R visitAnyPattern(AnyPatternTree node, P p) {
return defaultAction(node, p);
}

/**
* {@inheritDoc}
*
Expand Down
Expand Up @@ -760,6 +760,21 @@ public R visitInstanceOf(InstanceOfTree node, P p) {
return r;
}

/**
* {@inheritDoc}
*
* @implSpec This implementation returns {@code null}.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
* @since 21
*/
@Override
public R visitAnyPattern(AnyPatternTree node, P p) {
return null;
}

/**
* {@inheritDoc}
*
Expand Down
Expand Up @@ -210,7 +210,7 @@ public boolean isEnabled() {
public boolean isPreview(Feature feature) {
return switch (feature) {
case STRING_TEMPLATES -> true;

case UNNAMED_VARIABLES -> true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true'
//for those selected features, and 'false' for all the others.
Expand Down
Expand Up @@ -239,6 +239,7 @@ public enum Feature {
RECORD_PATTERNS(JDK21, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL),
STRING_TEMPLATES(JDK21, Fragments.FeatureStringTemplates, DiagKind.PLURAL),
WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
UNNAMED_VARIABLES(JDK21, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL),
;

enum DiagKind {
Expand Down
Expand Up @@ -1641,6 +1641,7 @@ public List<Type> getPermittedSubclasses() {

/** A class for variable symbols
*/
@SuppressWarnings("preview")
public static class VarSymbol extends Symbol implements VariableElement {

/** The variable's declaration position.
Expand Down Expand Up @@ -1783,6 +1784,10 @@ public void setData(Object data) {
public <R, P> R accept(Symbol.Visitor<R, P> v, P p) {
return v.visitVarSymbol(this, p);
}

public boolean isUnnamedVariable() {
return name.isEmpty();
}
}

public static class RecordComponent extends VarSymbol implements RecordComponentElement {
Expand Down
23 changes: 19 additions & 4 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
Expand Up @@ -1688,7 +1688,7 @@ private void handleSwitch(JCTree switchTree,
wasError = true;
}
MatchBindings currentBindings = null;
boolean wasUnconditionalPattern = hasUnconditionalPattern;
MatchBindings guardBindings = null;
for (List<JCCaseLabel> labels = c.labels; labels.nonEmpty(); labels = labels.tail) {
JCCaseLabel label = labels.head;
if (label instanceof JCConstantCaseLabel constLabel) {
Expand Down Expand Up @@ -1761,15 +1761,17 @@ private void handleSwitch(JCTree switchTree,
checkCastablePattern(pat.pos(), seltype, primaryType);
Type patternType = types.erasure(primaryType);
JCExpression guard = c.guard;
if (labels.tail.isEmpty() && guard != null) {
if (guardBindings == null && guard != null) {
MatchBindings afterPattern = matchBindings;
Env<AttrContext> bodyEnv = bindingEnv(switchEnv, matchBindings.bindingsWhenTrue);
try {
attribExpr(guard, bodyEnv, syms.booleanType);
} finally {
bodyEnv.info.scope.leave();
}
matchBindings = matchBindingsComputer.caseGuard(c, afterPattern, matchBindings);

guardBindings = matchBindings;
matchBindings = afterPattern;

if (TreeInfo.isBooleanWithValue(guard, 0)) {
log.error(guard.pos(), Errors.GuardHasConstantExpressionFalse);
Expand All @@ -1796,6 +1798,10 @@ private void handleSwitch(JCTree switchTree,
currentBindings = matchBindingsComputer.switchCase(label, currentBindings, matchBindings);
}

if (guardBindings != null) {
currentBindings = matchBindingsComputer.caseGuard(c, currentBindings, guardBindings);
}

Env<AttrContext> caseEnv =
bindingEnv(switchEnv, c, currentBindings.bindingsWhenTrue);
try {
Expand Down Expand Up @@ -4145,6 +4151,11 @@ private boolean checkCastablePattern(DiagnosticPosition pos,
}
}

@Override
public void visitAnyPattern(JCAnyPattern tree) {
result = tree.type = resultInfo.pt;
}

public void visitBindingPattern(JCBindingPattern tree) {
Type type;
if (tree.var.vartype != null) {
Expand All @@ -4166,7 +4177,11 @@ public void visitBindingPattern(JCBindingPattern tree) {
}
chk.validate(tree.var.vartype, env, true);
result = tree.type;
matchBindings = new MatchBindings(List.of(v), List.nil());
if (v.isUnnamedVariable()) {
matchBindings = MatchBindingsComputer.EMPTY;
} else {
matchBindings = new MatchBindings(List.of(v), List.nil());
}
}

@Override
Expand Down
Expand Up @@ -4572,8 +4572,11 @@ void checkSwitchCaseStructure(List<JCCase> cases) {
}
}
}
} else {
if (c.labels.tail.nonEmpty()) {
} else if (c.labels.tail.nonEmpty()) {
var patterCaseLabels = c.labels.stream().filter(ll -> ll instanceof JCPatternCaseLabel).map(cl -> (JCPatternCaseLabel)cl);
var allUnderscore = patterCaseLabels.allMatch(pcl -> !hasBindings(pcl.getPattern()));

if (!allUnderscore) {
log.error(c.labels.tail.head.pos(), Errors.FlowsThroughFromPattern);
}
}
Expand Down Expand Up @@ -4608,7 +4611,7 @@ boolean hasBindings(JCPattern p) {
new TreeScanner() {
@Override
public void visitBindingPattern(JCBindingPattern tree) {
bindings[0] = true;
bindings[0] = !tree.var.sym.isUnnamedVariable();
super.visitBindingPattern(tree);
}
}.scan(p);
Expand Down

1 comment on commit 8aa5028

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.