diff options
author | Stephan Herrmann | 2013-12-10 01:08:27 +0000 |
---|---|---|
committer | Stephan Herrmann | 2013-12-10 11:24:38 +0000 |
commit | d3de41afc4bae04cfee3367369f0e9174a3b79dd (patch) | |
tree | d0cbad2c94e470c7ff1f08d9ce32e15656bbd93c | |
parent | 854bd41c292a3f10302052623fca30040d1c56c7 (diff) | |
download | eclipse.jdt.core-d3de41afc4bae04cfee3367369f0e9174a3b79dd.tar.gz eclipse.jdt.core-d3de41afc4bae04cfee3367369f0e9174a3b79dd.tar.xz eclipse.jdt.core-d3de41afc4bae04cfee3367369f0e9174a3b79dd.zip |
WIP for Bug 419048
- tentative spec bug fix: 18.3 2nd list bullet 5 misses to apply theta!
- improve our substitution to handle types that are not type variables
5 files changed, 233 insertions, 158 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java index 0a8960bbd5..200715895b 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java @@ -22,7 +22,7 @@ import junit.framework.Test; public class LambdaExpressionsTest extends AbstractRegressionTest { static { -// TESTS_NAMES = new String[] { "testLambdaInference2"}; + TESTS_NAMES = new String[] { "testBug419048_1"}; // TESTS_NUMBERS = new int[] { 50 }; // TESTS_RANGE = new int[] { 11, -1 }; } @@ -2006,6 +2006,31 @@ public void testLambdaInference2() { ""); } +public void testBug419048_1() { + runConformTest( + new String[] { + "X.java", + "import java.util.*;\n" + + "import java.util.stream.*;\n" + + "public class X {\n" + + " public void test() {\n" + + " List<Person> roster = new ArrayList<>();\n" + + " \n" + + " Map<String, Person> map = \n" + + " roster\n" + + " .stream()\n" + + " .collect(\n" + + " Collectors.toMap(\n" + + " p -> p.getLast(),\n" + + " p -> p\n" + + " ));\n" + + " }\n" + + "}\n" + + "class Person {\n" + + " public String getLast() { return null; }\n" + + "}\n" + }); +} public static Class testClass() { return LambdaExpressionsTest.class; } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java index 7de5c75719..453aa27f82 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java @@ -456,7 +456,7 @@ class BoundSet { } } } else { - addBound(new TypeBound(alpha, ai, ReductionResult.SAME)); + addBound(new TypeBound(alpha, context.substitute(ai), ReductionResult.SAME)); } } } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java index eb5e66cc04..cfd8703589 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java @@ -95,7 +95,8 @@ public class InferenceContext18 { * Substitute any type variables mentioned in 'type' by the corresponding inference variable, if one exists. */ public TypeBinding substitute(TypeBinding type) { - return Scope.substitute(new InferenceSubstitution(this.environment, this.inferenceVariables), type); + InferenceSubstitution inferenceSubstitution = new InferenceSubstitution(this.environment, this.inferenceVariables); + return inferenceSubstitution.substitute(inferenceSubstitution, type); } /** JLS 18.5.1: compute bounds from formal and actual parameters. */ diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java index 759f1ac995..0f345bdc75 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java @@ -17,7 +17,7 @@ package org.eclipse.jdt.internal.compiler.lookup; /** * A type variable substitution strategy based on inference variables (JLS8 18.1.1) */ -public class InferenceSubstitution implements Substitution { +public class InferenceSubstitution extends Scope.Substitutor implements Substitution { private LookupEnvironment environment; private InferenceVariable[] variables; @@ -26,6 +26,20 @@ public class InferenceSubstitution implements Substitution { this.environment = environment; this.variables = variables; } + + /** + * Override method {@link Scope.Substitutor#substitute(Substitution, TypeBinding)}, + * to add substitution of types other than type variables. + */ + public TypeBinding substitute(Substitution substitution, TypeBinding originalType) { + for (int i = 0; i < this.variables.length; i++) { + InferenceVariable variable = this.variables[i]; + if (TypeBinding.equalsEquals(variable.typeParameter, originalType)) + return variable; + } + + return super.substitute(substitution, originalType); + } public TypeBinding substitute(TypeVariableBinding typeVariable) { ReferenceBinding superclass = typeVariable.superclass; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java index a9f44ae010..02b9c1a8c6 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java @@ -385,24 +385,7 @@ public abstract class Scope { * Only allocate an array if anything is different. */ public static ReferenceBinding[] substitute(Substitution substitution, ReferenceBinding[] originalTypes) { - if (originalTypes == null) return null; - ReferenceBinding[] substitutedTypes = originalTypes; - for (int i = 0, length = originalTypes.length; i < length; i++) { - ReferenceBinding originalType = originalTypes[i]; - TypeBinding substitutedType = substitute(substitution, originalType); - if (!(substitutedType instanceof ReferenceBinding)) { - return null; // impossible substitution - } - if (substitutedType != originalType) { //$IDENTITY-COMPARISON$ - if (substitutedTypes == originalTypes) { - System.arraycopy(originalTypes, 0, substitutedTypes = new ReferenceBinding[length], 0, i); - } - substitutedTypes[i] = (ReferenceBinding)substitutedType; - } else if (substitutedTypes != originalTypes) { - substitutedTypes[i] = originalType; - } - } - return substitutedTypes; + return defaultSubstitutor.substitute(substitution, originalTypes); } /** @@ -416,127 +399,7 @@ public abstract class Scope { * of its type in the generic declaration corresponding to C." */ public static TypeBinding substitute(Substitution substitution, TypeBinding originalType) { - if (originalType == null) return null; - switch (originalType.kind()) { - - case Binding.TYPE_PARAMETER: - return substitution.substitute((TypeVariableBinding) originalType); - - case Binding.PARAMETERIZED_TYPE: - ParameterizedTypeBinding originalParameterizedType = (ParameterizedTypeBinding) originalType; - ReferenceBinding originalEnclosing = originalType.enclosingType(); - ReferenceBinding substitutedEnclosing = originalEnclosing; - if (originalEnclosing != null) { - substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing); - if (isMemberTypeOfRaw(originalType, substitutedEnclosing)) - return originalParameterizedType.environment.createRawType( - originalParameterizedType.genericType(), substitutedEnclosing, originalType.getTypeAnnotations()); - } - TypeBinding[] originalArguments = originalParameterizedType.arguments; - TypeBinding[] substitutedArguments = originalArguments; - if (originalArguments != null) { - if (substitution.isRawSubstitution()) { - return originalParameterizedType.environment.createRawType(originalParameterizedType.genericType(), substitutedEnclosing, originalType.getTypeAnnotations()); - } - substitutedArguments = substitute(substitution, originalArguments); - } - if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) { //$IDENTITY-COMPARISON$ - return originalParameterizedType.environment.createParameterizedType( - originalParameterizedType.genericType(), substitutedArguments, substitutedEnclosing, originalType.getTypeAnnotations()); - } - break; - - case Binding.ARRAY_TYPE: - ArrayBinding originalArrayType = (ArrayBinding) originalType; - TypeBinding originalLeafComponentType = originalArrayType.leafComponentType; - TypeBinding substitute = substitute(substitution, originalLeafComponentType); // substitute could itself be array type, TODO(Srikanth): need a test case. - if (substitute != originalLeafComponentType) { //$IDENTITY-COMPARISON$ - return originalArrayType.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions(), originalType.getTypeAnnotations()); - } - break; - - case Binding.WILDCARD_TYPE: - case Binding.INTERSECTION_TYPE: - WildcardBinding wildcard = (WildcardBinding) originalType; - if (wildcard.boundKind != Wildcard.UNBOUND) { - TypeBinding originalBound = wildcard.bound; - TypeBinding substitutedBound = substitute(substitution, originalBound); - TypeBinding[] originalOtherBounds = wildcard.otherBounds; - TypeBinding[] substitutedOtherBounds = substitute(substitution, originalOtherBounds); - if (substitutedBound != originalBound || originalOtherBounds != substitutedOtherBounds) { //$IDENTITY-COMPARISON$ - if (originalOtherBounds != null) { - /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=347145: the constituent intersecting types have changed - in the last round of substitution. Reevaluate the composite intersection type, as there is a possibility - of the intersection collapsing into one of the constituents, the other being fully subsumed. - */ - TypeBinding [] bounds = new TypeBinding[1 + substitutedOtherBounds.length]; - bounds[0] = substitutedBound; - System.arraycopy(substitutedOtherBounds, 0, bounds, 1, substitutedOtherBounds.length); - TypeBinding[] glb = Scope.greaterLowerBound(bounds, null, substitution.environment()); // re-evaluate - if (glb != null && glb != bounds) { - substitutedBound = glb[0]; - if (glb.length == 1) { - substitutedOtherBounds = null; - } else { - System.arraycopy(glb, 1, substitutedOtherBounds = new TypeBinding[glb.length - 1], 0, glb.length - 1); - } - } - } - return wildcard.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, substitutedOtherBounds, wildcard.boundKind, wildcard.getTypeAnnotations()); - } - } - break; - - case Binding.TYPE: - if (!originalType.isMemberType()) break; - ReferenceBinding originalReferenceType = (ReferenceBinding) originalType; - originalEnclosing = originalType.enclosingType(); - substitutedEnclosing = originalEnclosing; - if (originalEnclosing != null) { - substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing); - if (isMemberTypeOfRaw(originalType, substitutedEnclosing)) - return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations()); - } - - // treat as if parameterized with its type variables (non generic type gets 'null' arguments) - if (substitutedEnclosing != originalEnclosing) { //$IDENTITY-COMPARISON$ - return substitution.isRawSubstitution() - ? substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations()) - : substitution.environment().createParameterizedType(originalReferenceType, null, substitutedEnclosing, originalType.getTypeAnnotations()); - } - break; - case Binding.GENERIC_TYPE: - originalReferenceType = (ReferenceBinding) originalType; - originalEnclosing = originalType.enclosingType(); - substitutedEnclosing = originalEnclosing; - if (originalEnclosing != null) { - substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing); - if (isMemberTypeOfRaw(originalType, substitutedEnclosing)) - return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations()); - } - - if (substitution.isRawSubstitution()) { - return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations()); - } - // treat as if parameterized with its type variables (non generic type gets 'null' arguments) - originalArguments = originalReferenceType.typeVariables(); - substitutedArguments = substitute(substitution, originalArguments); - return substitution.environment().createParameterizedType(originalReferenceType, substitutedArguments, substitutedEnclosing, originalType.getTypeAnnotations()); - } - return originalType; - } - - private static boolean isMemberTypeOfRaw(TypeBinding originalType, ReferenceBinding substitutedEnclosing) { - // 4.8: - // "a raw type is defined to be one of: - // ... - // * A non-static member type of a raw type R that is not - // inherited from a superclass or superinterface of R." - - // Due to staticness, e.g., Map.Entry<String,Object> is *not* considered as a raw type - - return (substitutedEnclosing != null && substitutedEnclosing.isRawType()) - && ((originalType instanceof ReferenceBinding) && !((ReferenceBinding)originalType).isStatic()); + return defaultSubstitutor.substitute(substitution, originalType); } /** @@ -544,21 +407,193 @@ public abstract class Scope { * Only allocate an array if anything is different. */ public static TypeBinding[] substitute(Substitution substitution, TypeBinding[] originalTypes) { - if (originalTypes == null) return null; - TypeBinding[] substitutedTypes = originalTypes; - for (int i = 0, length = originalTypes.length; i < length; i++) { - TypeBinding originalType = originalTypes[i]; - TypeBinding substitutedParameter = substitute(substitution, originalType); - if (substitutedParameter != originalType) { //$IDENTITY-COMPARISON$ - if (substitutedTypes == originalTypes) { - System.arraycopy(originalTypes, 0, substitutedTypes = new TypeBinding[length], 0, i); - } - substitutedTypes[i] = substitutedParameter; - } else if (substitutedTypes != originalTypes) { - substitutedTypes[i] = originalType; - } - } - return substitutedTypes; + return defaultSubstitutor.substitute(substitution, originalTypes); + } + + /** Bridge to non-static implementation in {@link Substitutor}, to make methods overridable. */ + private static Substitutor defaultSubstitutor = new Substitutor(); + public static class Substitutor { + /** + * Returns an array of types, where original types got substituted given a substitution. + * Only allocate an array if anything is different. + */ + public ReferenceBinding[] substitute(Substitution substitution, ReferenceBinding[] originalTypes) { + if (originalTypes == null) return null; + ReferenceBinding[] substitutedTypes = originalTypes; + for (int i = 0, length = originalTypes.length; i < length; i++) { + ReferenceBinding originalType = originalTypes[i]; + TypeBinding substitutedType = substitute(substitution, originalType); + if (!(substitutedType instanceof ReferenceBinding)) { + return null; // impossible substitution + } + if (substitutedType != originalType) { //$IDENTITY-COMPARISON$ + if (substitutedTypes == originalTypes) { + System.arraycopy(originalTypes, 0, substitutedTypes = new ReferenceBinding[length], 0, i); + } + substitutedTypes[i] = (ReferenceBinding)substitutedType; + } else if (substitutedTypes != originalTypes) { + substitutedTypes[i] = originalType; + } + } + return substitutedTypes; + } + + /** + * Returns a type, where original type was substituted using the receiver + * parameterized type. + * In raw mode (see {@link Substitution#isRawSubstitution()}), + * all parameterized types are converted to raw types. + * Cf. 4.8: "The type of a constructor (8.8), instance method (8.4, 9.4), + * or non-static field (8.3) M of a raw type C that is not inherited from its + * superclasses or superinterfaces is the raw type that corresponds to the erasure + * of its type in the generic declaration corresponding to C." + */ + public TypeBinding substitute(Substitution substitution, TypeBinding originalType) { + if (originalType == null) return null; + + switch (originalType.kind()) { + + case Binding.TYPE_PARAMETER: + return substitution.substitute((TypeVariableBinding) originalType); + + case Binding.PARAMETERIZED_TYPE: + ParameterizedTypeBinding originalParameterizedType = (ParameterizedTypeBinding) originalType; + ReferenceBinding originalEnclosing = originalType.enclosingType(); + ReferenceBinding substitutedEnclosing = originalEnclosing; + if (originalEnclosing != null) { + substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing); + if (isMemberTypeOfRaw(originalType, substitutedEnclosing)) + return originalParameterizedType.environment.createRawType( + originalParameterizedType.genericType(), substitutedEnclosing, originalType.getTypeAnnotations()); + } + TypeBinding[] originalArguments = originalParameterizedType.arguments; + TypeBinding[] substitutedArguments = originalArguments; + if (originalArguments != null) { + if (substitution.isRawSubstitution()) { + return originalParameterizedType.environment.createRawType(originalParameterizedType.genericType(), substitutedEnclosing, originalType.getTypeAnnotations()); + } + substitutedArguments = substitute(substitution, originalArguments); + } + if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) { //$IDENTITY-COMPARISON$ + return originalParameterizedType.environment.createParameterizedType( + originalParameterizedType.genericType(), substitutedArguments, substitutedEnclosing, originalType.getTypeAnnotations()); + } + break; + + case Binding.ARRAY_TYPE: + ArrayBinding originalArrayType = (ArrayBinding) originalType; + TypeBinding originalLeafComponentType = originalArrayType.leafComponentType; + TypeBinding substitute = substitute(substitution, originalLeafComponentType); // substitute could itself be array type, TODO(Srikanth): need a test case. + if (substitute != originalLeafComponentType) { //$IDENTITY-COMPARISON$ + return originalArrayType.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions(), originalType.getTypeAnnotations()); + } + break; + + case Binding.WILDCARD_TYPE: + case Binding.INTERSECTION_TYPE: + WildcardBinding wildcard = (WildcardBinding) originalType; + if (wildcard.boundKind != Wildcard.UNBOUND) { + TypeBinding originalBound = wildcard.bound; + TypeBinding substitutedBound = substitute(substitution, originalBound); + TypeBinding[] originalOtherBounds = wildcard.otherBounds; + TypeBinding[] substitutedOtherBounds = substitute(substitution, originalOtherBounds); + if (substitutedBound != originalBound || originalOtherBounds != substitutedOtherBounds) { //$IDENTITY-COMPARISON$ + if (originalOtherBounds != null) { + /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=347145: the constituent intersecting types have changed + in the last round of substitution. Reevaluate the composite intersection type, as there is a possibility + of the intersection collapsing into one of the constituents, the other being fully subsumed. + */ + TypeBinding [] bounds = new TypeBinding[1 + substitutedOtherBounds.length]; + bounds[0] = substitutedBound; + System.arraycopy(substitutedOtherBounds, 0, bounds, 1, substitutedOtherBounds.length); + TypeBinding[] glb = Scope.greaterLowerBound(bounds, null, substitution.environment()); // re-evaluate + if (glb != null && glb != bounds) { + substitutedBound = glb[0]; + if (glb.length == 1) { + substitutedOtherBounds = null; + } else { + System.arraycopy(glb, 1, substitutedOtherBounds = new TypeBinding[glb.length - 1], 0, glb.length - 1); + } + } + } + return wildcard.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, substitutedOtherBounds, wildcard.boundKind, wildcard.getTypeAnnotations()); + } + } + break; + + case Binding.TYPE: + if (!originalType.isMemberType()) break; + ReferenceBinding originalReferenceType = (ReferenceBinding) originalType; + originalEnclosing = originalType.enclosingType(); + substitutedEnclosing = originalEnclosing; + if (originalEnclosing != null) { + substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing); + if (isMemberTypeOfRaw(originalType, substitutedEnclosing)) + return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations()); + } + + // treat as if parameterized with its type variables (non generic type gets 'null' arguments) + if (substitutedEnclosing != originalEnclosing) { //$IDENTITY-COMPARISON$ + return substitution.isRawSubstitution() + ? substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations()) + : substitution.environment().createParameterizedType(originalReferenceType, null, substitutedEnclosing, originalType.getTypeAnnotations()); + } + break; + case Binding.GENERIC_TYPE: + originalReferenceType = (ReferenceBinding) originalType; + originalEnclosing = originalType.enclosingType(); + substitutedEnclosing = originalEnclosing; + if (originalEnclosing != null) { + substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing); + if (isMemberTypeOfRaw(originalType, substitutedEnclosing)) + return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations()); + } + + if (substitution.isRawSubstitution()) { + return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing, originalType.getTypeAnnotations()); + } + // treat as if parameterized with its type variables (non generic type gets 'null' arguments) + originalArguments = originalReferenceType.typeVariables(); + substitutedArguments = substitute(substitution, originalArguments); + return substitution.environment().createParameterizedType(originalReferenceType, substitutedArguments, substitutedEnclosing, originalType.getTypeAnnotations()); + } + return originalType; + } + + private static boolean isMemberTypeOfRaw(TypeBinding originalType, ReferenceBinding substitutedEnclosing) { + // 4.8: + // "a raw type is defined to be one of: + // ... + // * A non-static member type of a raw type R that is not + // inherited from a superclass or superinterface of R." + + // Due to staticness, e.g., Map.Entry<String,Object> is *not* considered as a raw type + + return (substitutedEnclosing != null && substitutedEnclosing.isRawType()) + && ((originalType instanceof ReferenceBinding) && !((ReferenceBinding)originalType).isStatic()); + } + + /** + * Returns an array of types, where original types got substituted given a substitution. + * Only allocate an array if anything is different. + */ + public TypeBinding[] substitute(Substitution substitution, TypeBinding[] originalTypes) { + if (originalTypes == null) return null; + TypeBinding[] substitutedTypes = originalTypes; + for (int i = 0, length = originalTypes.length; i < length; i++) { + TypeBinding originalType = originalTypes[i]; + TypeBinding substitutedParameter = substitute(substitution, originalType); + if (substitutedParameter != originalType) { //$IDENTITY-COMPARISON$ + if (substitutedTypes == originalTypes) { + System.arraycopy(originalTypes, 0, substitutedTypes = new TypeBinding[length], 0, i); + } + substitutedTypes[i] = substitutedParameter; + } else if (substitutedTypes != originalTypes) { + substitutedTypes[i] = originalType; + } + } + return substitutedTypes; + } } /* |