Java, typeres: add incorporation and tests

This commit is contained in:
Bendegúz Nagy
2017-07-19 19:35:16 +02:00
parent 3bec082a40
commit db6dc4d5bc
3 changed files with 277 additions and 7 deletions

View File

@ -129,7 +129,7 @@ public abstract class BoundOrConstraint {
return rightTypeVariable;
}
public InferenceRuleType getRuleType() {
public InferenceRuleType ruleType() {
return ruleType;
}

View File

@ -0,0 +1,177 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.typeresolution.typeinference;
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.EQUALITY;
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.SUBTYPE;
import java.util.ArrayList;
import java.util.List;
public final class TypeInferenceResolver {
private TypeInferenceResolver() {
}
/**
* https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html#jls-18.3
*/
public static List<Constraint> incorporateBounds(List<Bound> currentBounds, List<Bound> newBounds) {
// (In this section, S and T are inference variables or types, and U is a proper type. For conciseness, a bound
// of the form α = T may also match a bound of the form T = α.)
List<Constraint> newConstraints = new ArrayList<>();
for (Bound first : currentBounds) {
for (Bound second : newBounds) {
Sides sides = getUnequalSides(first, second);
if (sides == null) {
continue;
}
if (first.ruleType() == EQUALITY && second.ruleType() == EQUALITY) {
// α = S and α = T imply S = T
newConstraints.add(copyConstraint(first, second, getUnequalSides(first, second), EQUALITY));
} else if (first.ruleType() == EQUALITY && second.ruleType() == SUBTYPE) {
if (sides.second == Side.RIGHT) {
// α = S and α <: T imply S <: T
newConstraints.add(copyConstraint(first, second, sides, SUBTYPE));
} else {
// α = S and T <: α imply T <: S
newConstraints.add(copyConstraint(second, first, sides.copySwap(), SUBTYPE));
}
} else if (first.ruleType() == SUBTYPE && second.ruleType() == EQUALITY) {
if (sides.first == Side.RIGHT) {
// α <: T and α = S imply S <: T
newConstraints.add(copyConstraint(second, first, sides.copySwap(), SUBTYPE));
} else {
// T <: α and α = S imply T <: S
newConstraints.add(copyConstraint(first, second, sides, SUBTYPE));
}
} else if (first.ruleType() == SUBTYPE && second.ruleType() == SUBTYPE) {
if (sides.first == Side.LEFT && sides.second == Side.RIGHT) {
// S <: α and α <: T imply S <: T
newConstraints.add(copyConstraint(first, second, sides, SUBTYPE));
} else if (sides.first == Side.RIGHT && sides.second == Side.LEFT) {
// α <: T and S <: α imply S <: T
newConstraints.add(copyConstraint(second, first, sides.copySwap(), SUBTYPE));
}
}
// α = U and S = T imply S[α:=U] = T[α:=U] TODO
// α = U and S <: T imply S[α:=U] <: T[α:=U] TODO
}
}
return newConstraints;
}
private enum Side {
LEFT, RIGHT
}
private static class Sides {
/* default */ final Side first;
/* default */ final Side second;
/* default */ Sides(Side first, Side second) {
this.first = first;
this.second = second;
}
/* default */ Sides copySwap() {
return new Sides(second, first);
}
}
private static Sides getUnequalSides(BoundOrConstraint first, BoundOrConstraint second) {
if (first.leftVariable() != null) {
if (first.leftVariable() == second.leftVariable()) {
return new Sides(Side.RIGHT, Side.RIGHT);
} else if (first.leftVariable() == second.rightVariable()) {
return new Sides(Side.RIGHT, Side.LEFT);
}
} else if (first.rightVariable() != null) {
if (first.rightVariable() == second.leftVariable()) {
return new Sides(Side.LEFT, Side.RIGHT);
} else if (first.rightVariable() == second.rightVariable()) {
return new Sides(Side.LEFT, Side.LEFT);
}
}
return null;
}
private static Constraint copyConstraint(BoundOrConstraint first, BoundOrConstraint second, Sides sides,
InferenceRuleType rule) {
if (sides.first == Side.LEFT) {
if (sides.second == Side.LEFT) {
if (first.leftVariable() != null) {
if (second.leftVariable() != null) {
return new Constraint(first.leftVariable(), second.leftVariable(), rule);
} else {
return new Constraint(first.leftVariable(), second.leftProper(), rule);
}
} else {
if (second.leftVariable() != null) {
return new Constraint(first.leftProper(), second.leftVariable(), rule);
} else {
return new Constraint(first.leftProper(), second.leftProper(), rule);
}
}
} else {
if (first.leftVariable() != null) {
if (second.rightVariable() != null) {
return new Constraint(first.leftVariable(), second.rightVariable(), rule);
} else {
return new Constraint(first.leftVariable(), second.rightProper(), rule);
}
} else {
if (second.rightVariable() != null) {
return new Constraint(first.leftProper(), second.rightVariable(), rule);
} else {
return new Constraint(first.leftProper(), second.rightProper(), rule);
}
}
}
} else {
if (sides.second == Side.LEFT) {
if (first.rightVariable() != null) {
if (second.leftVariable() != null) {
return new Constraint(first.rightVariable(), second.leftVariable(), rule);
} else {
return new Constraint(first.rightVariable(), second.leftProper(), rule);
}
} else {
if (second.leftVariable() != null) {
return new Constraint(first.rightProper(), second.leftVariable(), rule);
} else {
return new Constraint(first.rightProper(), second.leftProper(), rule);
}
}
} else {
if (first.rightVariable() != null) {
if (second.rightVariable() != null) {
return new Constraint(first.rightVariable(), second.rightVariable(), rule);
} else {
return new Constraint(first.rightVariable(), second.rightProper(), rule);
}
} else {
if (second.rightVariable() != null) {
return new Constraint(first.rightProper(), second.rightVariable(), rule);
} else {
return new Constraint(first.rightProper(), second.rightProper(), rule);
}
}
}
}
}
}

View File

@ -4,7 +4,6 @@
package net.sourceforge.pmd.typeresolution;
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.CONTAINS;
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.EQUALITY;
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.LOOSE_INVOCATION;
@ -12,17 +11,18 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.Inferen
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition;
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.Bound;
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.BoundOrConstraint;
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.Constraint;
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType;
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.TypeInferenceResolver;
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.Variable;
public class TypeInferenceTest {
@ -197,13 +197,106 @@ public class TypeInferenceTest {
// If T is a wildcard of the form ? super T': TODO
}
@Test
public void testIncorporation() {
List<Constraint> result;
List<Bound> current = new ArrayList<>();
List<Bound> newBounds = new ArrayList<>();
JavaTypeDefinition s = JavaTypeDefinition.forClass(int.class);
JavaTypeDefinition t = JavaTypeDefinition.forClass(double.class);
Variable alpha = a;
// ### Original rule 1. : α = S and α = T imply S = T
result = incorporationResult(new Bound(alpha, s, EQUALITY), new Bound(alpha, t, EQUALITY));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, EQUALITY, Constraint.class);
// α = S and T = α imply S = T
result = incorporationResult(new Bound(alpha, s, EQUALITY), new Bound(t, alpha, EQUALITY));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, EQUALITY, Constraint.class);
// S = α and α = T imply S = T
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(alpha, t, EQUALITY));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, EQUALITY, Constraint.class);
// S = α and T = α imply S = T
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(t, alpha, EQUALITY));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, EQUALITY, Constraint.class);
// ### Original rule 2. : α = S and α <: T imply S <: T
result = incorporationResult(new Bound(alpha, s, EQUALITY), new Bound(alpha, t, SUBTYPE));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
// S = α and α <: T imply S <: T
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(alpha, t, SUBTYPE));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
// α <: T and α = S imply S <: T
result = incorporationResult(new Bound(alpha, t, SUBTYPE), new Bound(alpha, s, EQUALITY));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
// α <: T and S = α imply S <: T
result = incorporationResult(new Bound(alpha, t, SUBTYPE), new Bound(s, alpha, EQUALITY));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
// ### Original rule 3. : α = S and T <: α imply T <: S
result = incorporationResult(new Bound(alpha, s, EQUALITY), new Bound(t, alpha, SUBTYPE));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), t, s, SUBTYPE, Constraint.class);
// S = α and T <: α imply T <: S
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(t, alpha, SUBTYPE));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), t, s, SUBTYPE, Constraint.class);
// T <: α and α = S imply T <: S
result = incorporationResult(new Bound(t, alpha, SUBTYPE), new Bound(alpha, s, EQUALITY));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), t, s, SUBTYPE, Constraint.class);
// T <: α and S = α imply T <: S
result = incorporationResult(new Bound(t, alpha, SUBTYPE), new Bound(s, alpha, EQUALITY));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), t, s, SUBTYPE, Constraint.class);
// ### Original rule 4. : S <: α and α <: T imply S <: T
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(alpha, t, SUBTYPE));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
// α <: T and S <: α imply S <: T
result = incorporationResult(new Bound(alpha, t, SUBTYPE), new Bound(s, alpha, EQUALITY));
assertEquals(result.size(), 1);
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
}
private List<Constraint> incorporationResult(Bound firstBound, Bound secondBound) {
List<Bound> current = new ArrayList<>();
List<Bound> newBounds = new ArrayList<>();
current.add(firstBound);
newBounds.add(secondBound);
return TypeInferenceResolver.incorporateBounds(current, newBounds);
}
private void testBoundOrConstraint(BoundOrConstraint val, JavaTypeDefinition left, JavaTypeDefinition right,
InferenceRuleType rule, Class<? extends BoundOrConstraint> type) {
assertTrue(val.getClass() == type);
assertEquals(val.leftProper(), left);
assertEquals(val.rightProper(), right);
assertEquals(val.getRuleType(), rule);
assertEquals(val.ruleType(), rule);
}
@ -212,7 +305,7 @@ public class TypeInferenceTest {
assertTrue(val.getClass() == type);
assertEquals(val.leftProper(), left);
assertEquals(val.rightVariable(), right);
assertEquals(val.getRuleType(), rule);
assertEquals(val.ruleType(), rule);
}
private void testBoundOrConstraint(BoundOrConstraint val, Variable left, JavaTypeDefinition right,
@ -220,7 +313,7 @@ public class TypeInferenceTest {
assertTrue(val.getClass() == type);
assertEquals(val.leftVariable(), left);
assertEquals(val.rightProper(), right);
assertEquals(val.getRuleType(), rule);
assertEquals(val.ruleType(), rule);
}
private void testBoundOrConstraint(BoundOrConstraint val, Variable left, Variable right,
@ -228,6 +321,6 @@ public class TypeInferenceTest {
assertTrue(val.getClass() == type);
assertEquals(val.leftVariable(), left);
assertEquals(val.rightVariable(), right);
assertEquals(val.getRuleType(), rule);
assertEquals(val.ruleType(), rule);
}
}