Skip to content

Commit

Permalink
Adjust Overloading
Browse files Browse the repository at this point in the history
  • Loading branch information
biboudis committed Jan 10, 2025
1 parent a78ebd9 commit e861e2d
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 77 deletions.
170 changes: 122 additions & 48 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.stream.Stream;

import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ElementVisitor;
import javax.tools.JavaFileObject;

import com.sun.source.tree.CaseTree;
Expand All @@ -52,6 +53,8 @@
import com.sun.tools.javac.comp.MatchBindingsComputer.MatchBindings;
import com.sun.tools.javac.jvm.*;

import static com.sun.tools.javac.comp.Attr.PatternResolutionPhase.*;
import static com.sun.tools.javac.comp.Resolve.MethodResolutionPhase.*;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Diamond;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArg;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArgs;
Expand Down Expand Up @@ -4344,7 +4347,7 @@ public void visitRecordPattern(JCRecordPattern tree) {
List<MethodSymbol> patternDeclarations = getPatternDeclarationCandidates(site, nestedPatternCount);

if (patternDeclarations.size() >= 1) {
MethodSymbol resolvedPatternDeclaration = null;
Symbol resolvedPatternDeclaration = null;

if (patternDeclarations.size() > 1) {
// precalculate types of pattern components (as in method invocation)
Expand All @@ -4361,9 +4364,9 @@ public void visitRecordPattern(JCRecordPattern tree) {
resolvedPatternDeclaration = patternDeclarations.getFirst();
}

if (resolvedPatternDeclaration != null) {
if (resolvedPatternDeclaration != null && resolvedPatternDeclaration.kind != AMBIGUOUS) {
expectedRecordTypes = types.memberType(site, resolvedPatternDeclaration).getBindingTypes();
tree.patternDeclaration = resolvedPatternDeclaration;
tree.patternDeclaration = (MethodSymbol) resolvedPatternDeclaration;
}
}
}
Expand Down Expand Up @@ -4399,65 +4402,136 @@ public void visitRecordPattern(JCRecordPattern tree) {
matchBindings = new MatchBindings(outBindings.toList(), List.nil());
}

private MethodSymbol selectBestPatternDeclarationInScope(JCRecordPattern tree,
enum PatternResolutionPhase {
LEAST_SPECIFIC_UNCONDITIONAL,
MOST_SPECIFIC_CONDITIONAL
}
final List<PatternResolutionPhase> patternResolutionPhases = List.of(LEAST_SPECIFIC_UNCONDITIONAL, MOST_SPECIFIC_CONDITIONAL);


private Symbol selectBestPatternDeclarationInScope(JCRecordPattern tree,
Type site,
List<MethodSymbol> patternDeclarations,
List<Type> patternTypes) {
List<Type> expectedRecordTypes = null;
ListBuffer<Integer> score = new ListBuffer<Integer>();
List<Type> patternSiteBindingTypes) {
List<Type> patternDeclarationBindingTypes = null;
Symbol bestSoFar = null;

for (PatternResolutionPhase phase : patternResolutionPhases) {
for (int n = 0; n < patternDeclarations.size(); n++) {
patternDeclarationBindingTypes = getBindingTypes(site, patternDeclarations.get(n));
if (isApplicable(patternDeclarationBindingTypes, patternSiteBindingTypes)) {
if (phase.equals(LEAST_SPECIFIC_UNCONDITIONAL) && isUnconditionalMatch(patternDeclarationBindingTypes, patternSiteBindingTypes)) {
if (bestSoFar == null) {
bestSoFar = patternDeclarations.get(n);
} else if (bestSoFar.kind != AMBIGUOUS) {
bestSoFar = findSpecific(site, (MethodSymbol) bestSoFar, patternDeclarations.get(n), LEAST_SPECIFIC_UNCONDITIONAL);
}
} else if (phase.equals(MOST_SPECIFIC_CONDITIONAL) && !isUnconditionalMatch(patternDeclarationBindingTypes, patternSiteBindingTypes)) {
if (bestSoFar == null) {
bestSoFar = patternDeclarations.get(n);
} else {
bestSoFar = findSpecific(site, (MethodSymbol) bestSoFar, patternDeclarations.get(n), MOST_SPECIFIC_CONDITIONAL);
}
}
}
}
}

ArrayList<List<Type>> typesMatrix = new ArrayList<>();
for (int j = 0; j < patternDeclarations.size(); j++) {
MethodSymbol matcher = patternDeclarations.get(j);
if (bestSoFar != null && bestSoFar.kind == AMBIGUOUS) {
log.error(tree.pos(),
Errors.MatcherOverloadingAmbiguity);
} else if (bestSoFar == null) {
log.error(tree.pos(),
Errors.NoCompatibleMatcherFound);
}

return bestSoFar;
}

List<Type> matcherComponentTypes = matcher.bindings()
.stream()
.map(rc -> types.memberType(site, rc))
.map(t -> types.upward(t, types.captures(t)).baseType())
.collect(List.collector());
class DeconstructorResolutionError extends Symbol {
final String debugName;

typesMatrix.add(matcherComponentTypes);
DeconstructorResolutionError(Kind kind, String debugName) {
super(kind, 0, null, null, null);
this.debugName = debugName;
}

int selected = -1;
boolean atLeastOne = false;
for (int n = 0; n < patternDeclarations.size(); n++) {
List<Type> matcherComponentTypes = typesMatrix.get(n);
boolean applicable = true;
@Override @DefinedBy(Api.LANGUAGE_MODEL)
public <R, P> R accept(ElementVisitor<R, P> v, P p) {
throw new AssertionError();
}

for (int i = 0; applicable && i < patternTypes.size(); i++) {
applicable &= types.isCastable(patternTypes.get(i), matcherComponentTypes.get(i));
}
@Override
public String toString() {
return debugName;
}

if (applicable) {
boolean found = true;
atLeastOne = true;
// for all pattern components
for (int i = 0; found && i < patternTypes.size(); i++) {
if (!types.isSameType(patternTypes.get(i), matcherComponentTypes.get(i)) &&
!sameBindingTypeInAllCandidateDtors(typesMatrix, n, i, matcherComponentTypes)) {
found = false;
}
}
if (found && selected < 0) {
selected = n;
}
}
@Override
public boolean exists() {
return false;
}

applicable = true;
@Override
public boolean isStatic() {
return false;
}

if (atLeastOne && selected < 0) {
log.error(tree.pos(),
Errors.MatcherOverloadingAmbiguity);
return null;
} else if (!atLeastOne) {
log.error(tree.pos(),
Errors.NoCompatibleMatcherFound);
return null;
protected Symbol access(Name name, TypeSymbol location) {
return types.createErrorType(name, location, syms.errSymbol.type).tsym;
}
}

private Symbol findSpecific(Type site, MethodSymbol bestSoFar, MethodSymbol candidatePatternDeclaration, PatternResolutionPhase patternResolutionPhase) {
List<Type> bindingTypesBestSoFar = getBindingTypes(site, bestSoFar);
List<Type> bindingTypesCandidatePatternDeclaration = getBindingTypes(site, candidatePatternDeclaration);

boolean bestSoFarToCandidate = isUnconditionalMatch(bindingTypesBestSoFar, bindingTypesCandidatePatternDeclaration);
boolean candidateToBestSoFar = isUnconditionalMatch(bindingTypesCandidatePatternDeclaration, bindingTypesBestSoFar);

return patternDeclarations.get(selected);
if (!bestSoFarToCandidate && !candidateToBestSoFar) {
return new DeconstructorResolutionError(AMBIGUOUS, "compiler.err.matcher.overloading.ambiguity");
}

return switch (patternResolutionPhase) {
case LEAST_SPECIFIC_UNCONDITIONAL ->
bestSoFarToCandidate ? candidatePatternDeclaration : bestSoFar;
case MOST_SPECIFIC_CONDITIONAL ->
candidateToBestSoFar ? candidatePatternDeclaration : bestSoFar;
};
}

private boolean isUnconditionalMatch(List<Type> patternDeclarationBindingTypes, List<Type> patternSiteBindingTypes) {
boolean ret = true;

while (patternDeclarationBindingTypes.nonEmpty() && patternSiteBindingTypes.nonEmpty() && ret) {
Type currentPatternDeclarationBindingType = patternDeclarationBindingTypes.head;
Type currentPatternSiteBindingType = patternSiteBindingTypes.head;

ret &= types.isUnconditionallyExact(currentPatternDeclarationBindingType, currentPatternSiteBindingType);

patternDeclarationBindingTypes = patternDeclarationBindingTypes.tail;
patternSiteBindingTypes = patternSiteBindingTypes.tail;
}

return ret;
}

private List<Type> getBindingTypes(Type site, MethodSymbol matcher) {
List<Type> matcherComponentTypes = matcher.bindings()
.stream()
.map(rc -> types.memberType(site, rc))
.map(t -> types.upward(t, types.captures(t)).baseType())
.collect(List.collector());
return matcherComponentTypes;
}

private boolean isApplicable(List<Type> matcherComponentTypes, List<Type> patternTypes) {
boolean applicable = true;

for (int i = 0; applicable && i < patternTypes.size(); i++) {
applicable &= types.isCastable(patternTypes.get(i), matcherComponentTypes.get(i));
}
return applicable;
}

private boolean sameBindingTypeInAllCandidateDtors(ArrayList<List<Type>> typesMatrix, int n, int i, List<Type> matcherComponentTypes) {
Expand Down
2 changes: 1 addition & 1 deletion test/jdk/jdk/classfile/PatternTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
import static helpers.TestUtil.assertEmpty;
import static org.junit.jupiter.api.Assertions.*;

import java.lang.classfile.components.ClassPrinter;
import jdk.internal.classfile.components.ClassPrinter;
import org.junit.jupiter.api.Test;

class PatternTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,22 @@
// options: --enable-preview -source ${jdk.version} -Xlint:preview

public class PatternDeclarationOverloadingAmbiguity {
private static int test(D o) {
if (o instanceof D(String data, Integer out)) {
return out;
interface I {}
static class I1 implements I {}
static class I2 implements I {}

private static void test(D o) {
if (o instanceof D(I data)) {
}
return -1;
}

public static class D {
public pattern D(Object v, Integer out) {
match D(1, 1);
public pattern D(I1 v) {
match D(new I1());
}

public pattern D(CharSequence v, Integer out) {
match D("2", 2);
public pattern D(I2 v) {
match D(new I2());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,37 @@
* @compile/fail/ref=OverloadedPatternDeclarationErrors.out -XDrawDiagnostics -XDdev OverloadedPatternDeclarationErrors.java
*/
public class OverloadedPatternDeclarationErrors {
interface I {}
static class I1 implements I {}
static class I2 implements I {}

private static int test(D o) {
if (o instanceof D(String data, Integer out)) { // no compatible matcher found
return out;
}
return -1;
}

private static int test2(D2 o) {
if (o instanceof D2(String data, Integer out)) { // ambiguous
return out;
private static void test2(D o) {
if (o instanceof D(I data)) { // ambiguity
}
return -1;
}

public static class D {
public pattern D(Object v1, Float out) {
match D(10.0f, 10.0f);
public pattern D(I1 v) {
match D(new I1());
}

public pattern D(Float out, Integer v1) {
match D(10.0f, 2);
public pattern D(I2 v) {
match D(new I2());
}
}

public static class D2 {
public pattern D2(Object v, Integer out) {
match D2("", 1);
public pattern D(Object v1, Float out) {
match D(10.0f, 10.0f);
}

public pattern D2(CharSequence v, Integer out) {
match D2("", 2);
public pattern D(Float out, Integer v1) {
match D(10.0f, 2);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
OverloadedPatternDeclarationErrors.java:9:26: compiler.err.no.compatible.matcher.found
OverloadedPatternDeclarationErrors.java:16:26: compiler.err.matcher.overloading.ambiguity
OverloadedPatternDeclarationErrors.java:13:26: compiler.err.no.compatible.matcher.found
OverloadedPatternDeclarationErrors.java:20:26: compiler.err.matcher.overloading.ambiguity
- compiler.note.preview.filename: OverloadedPatternDeclarationErrors.java, DEFAULT
- compiler.note.preview.recompile
2 errors
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ public void runTests() throws Exception {
public void testOne(Path base) throws Exception {
runSingle(base, "A", "B", "B", 2);
runSingle(base, "B", "C", "B", 1);
runSingle(base, "B", "C", "A", 0);
runSingle(base, "A", "B", "C", 0);
runSingle(base, "A", "C", "B", 0);
runSingle(base, "A", "D", "B", 0);
runSingle(base, "B", "C", "A", 1);
runSingle(base, "A", "B", "C", 2);
runSingle(base, "A", "C", "B", 2);
runSingle(base, "A", "D", "B", 2);
runSingle(base, "E", "F", "I", 0);
}

Expand Down

0 comments on commit e861e2d

Please sign in to comment.