From db6dc4d5bcbba81a3f9d0f85c8301ebb6762a615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bendeg=C3=BAz=20Nagy?= Date: Wed, 19 Jul 2017 19:35:16 +0200 Subject: [PATCH] Java, typeres: add incorporation and tests --- .../typeinference/BoundOrConstraint.java | 2 +- .../typeinference/TypeInferenceResolver.java | 177 ++++++++++++++++++ .../pmd/typeresolution/TypeInferenceTest.java | 105 ++++++++++- 3 files changed, 277 insertions(+), 7 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/TypeInferenceResolver.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/BoundOrConstraint.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/BoundOrConstraint.java index 9a0dfa9457..376536b26e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/BoundOrConstraint.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/BoundOrConstraint.java @@ -129,7 +129,7 @@ public abstract class BoundOrConstraint { return rightTypeVariable; } - public InferenceRuleType getRuleType() { + public InferenceRuleType ruleType() { return ruleType; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/TypeInferenceResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/TypeInferenceResolver.java new file mode 100644 index 0000000000..2cce89db6f --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typeinference/TypeInferenceResolver.java @@ -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 incorporateBounds(List currentBounds, List 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 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); + } + } + } + } + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/TypeInferenceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/TypeInferenceTest.java index 34d96cb523..4d1f1011bf 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/TypeInferenceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/TypeInferenceTest.java @@ -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 result; + List current = new ArrayList<>(); + List 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 incorporationResult(Bound firstBound, Bound secondBound) { + List current = new ArrayList<>(); + List 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 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); } }