2121
2222import java .util .*;
2323import java .util .Map .Entry ;
24+ import java .util .stream .Collectors ;
2425import java .util .stream .Stream ;
2526import org .eclipse .jdt .internal .compiler .ast .Wildcard ;
2627import org .eclipse .jdt .internal .compiler .util .Tuples .Pair ;
@@ -56,7 +57,6 @@ private static class ThreeSets {
5657 Set <TypeBound > sameBounds ;
5758 Set <TypeBound > subBounds ;
5859 TypeBinding instantiation ;
59- Map <InferenceVariable ,TypeBound > inverseBounds ; // from right inference variable to bound
6060 Set <InferenceVariable > dependencies ;
6161 public ThreeSets () {
6262 // empty, the sets are lazily initialized
@@ -138,18 +138,6 @@ public TypeBinding[] upperBounds(boolean onlyProper, InferenceVariable variable)
138138 InferenceContext18 .sortTypes (rights );
139139 return rights ;
140140 }
141- // pre: beta is a prototype
142- public boolean hasDependency (InferenceVariable beta ) {
143- if (this .dependencies != null && this .dependencies .contains (beta ))
144- return true ;
145- if (this .inverseBounds != null ) {
146- if (this .inverseBounds .containsKey (beta )) {
147- // TODO: not yet observed in tests
148- return true ;
149- }
150- }
151- return false ;
152- }
153141 /** Total number of type bounds in this container. */
154142 public int size () {
155143 int size = 0 ;
@@ -322,6 +310,16 @@ else if (type.hasNullTypeAnnotations())
322310 * On both sides we only enter types with nonnull arguments.
323311 */
324312 HashMap <ParameterizedTypeBinding ,ParameterizedTypeBinding > captures = new LinkedHashMap <>();
313+ /**
314+ * NON-JLS: maintain a separate set alongside {@link #captures}:
315+ * <dl>
316+ * <dt>captures<dd>IC18.resumeSuspendedInference() will reset these to avoid captures from nested
317+ * inference spilling blindly into the current inference.<br>
318+ * {@link InferenceContext18#collectDependencies(BoundSet, boolean, boolean[])} considers only "local" {@link #captures}.
319+ * <dt>allCaptures<dd>Still {@link #hasCaptureBound(Set)} and {@link #incorporate(InferenceContext18)} will
320+ * operate on {@link #allCaptures}.
321+ */
322+ HashMap <ParameterizedTypeBinding ,ParameterizedTypeBinding > allCaptures = new LinkedHashMap <>();
325323 /** 18.1.3 bullet 5: throws α */
326324 Set <InferenceVariable > inThrows = new LinkedHashSet <>();
327325
@@ -375,6 +373,7 @@ public BoundSet copy() {
375373 }
376374 copy .inThrows .addAll (this .inThrows );
377375 copy .captures .putAll (this .captures );
376+ copy .allCaptures .putAll (this .allCaptures );
378377 if (this .incorporatedBounds .length > 0 )
379378 System .arraycopy (this .incorporatedBounds , 0 , copy .incorporatedBounds = new TypeBound [this .incorporatedBounds .length ], 0 , this .incorporatedBounds .length );
380379 if (this .unincorporatedBoundsCount > 0 )
@@ -435,9 +434,6 @@ else if (boundNullBits != 0) // combine bits from both sources, even if this cre
435434 three = this .boundsPerVariable .get (rightIV );
436435 if (three == null )
437436 this .boundsPerVariable .put (rightIV , (three = new ThreeSets ()));
438- if (three .inverseBounds == null )
439- three .inverseBounds = new HashMap <>();
440- three .inverseBounds .put (rightIV , bound );
441437 }
442438 }
443439 }
@@ -458,6 +454,7 @@ public void addBounds(BoundSet that, LookupEnvironment environment, boolean incl
458454 this .inThrows .addAll (that .inThrows );
459455 if (includeCaptureBounds ) {
460456 this .captures .putAll (that .captures );
457+ this .allCaptures .putAll (that .allCaptures );
461458 }
462459 }
463460
@@ -491,7 +488,7 @@ public int numUninstantiatedVariables(InferenceVariable[] variables) {
491488
492489 // Driver for the real workhorse - Implements generational incorporation a la generational garbage collector.
493490 boolean incorporate (InferenceContext18 context ) throws InferenceFailureException {
494- if (this .unincorporatedBoundsCount == 0 && this .captures .isEmpty ())
491+ if (this .unincorporatedBoundsCount == 0 && this .allCaptures .isEmpty ())
495492 return true ;
496493
497494 try {
@@ -634,7 +631,8 @@ boolean incorporate(InferenceContext18 context, TypeBound [] first, TypeBound []
634631 } while (first != next && ++iteration <= 2 );
635632 }
636633 }
637- for (Entry <ParameterizedTypeBinding , ParameterizedTypeBinding > capt : this .captures .entrySet ()) {
634+ Set <ParameterizedTypeBinding > capturesToRemove = new HashSet <>();
635+ for (Entry <ParameterizedTypeBinding , ParameterizedTypeBinding > capt : this .allCaptures .entrySet ()) {
638636 ParameterizedTypeBinding gAlpha = capt .getKey ();
639637 ParameterizedTypeBinding gA = capt .getValue ();
640638 ReferenceBinding g = (ReferenceBinding ) gA .original ();
@@ -693,7 +691,8 @@ protected TypeBinding getP(int i) {
693691 System .arraycopy (otherBounds , 0 , allBounds , 1 , n -1 );
694692 bi = context .environment .createIntersectionType18 (allBounds );
695693 }
696- addTypeBoundsFromWildcardBound (context , theta , wildcardBinding .boundKind , t , r , bi );
694+ if (addTypeBoundsFromWildcardBound (context , theta , wildcardBinding .boundKind , t , r , bi ))
695+ capturesToRemove .add (gAlpha );
697696 }
698697 }
699698 }
@@ -704,33 +703,38 @@ protected TypeBinding getP(int i) {
704703 while (it .hasNext ()) {
705704 TypeBound bound = it .next ();
706705 if (!(bound .right instanceof InferenceVariable )) {
707- if (wildcardBinding .boundKind == Wildcard .SUPER )
706+ if (wildcardBinding .boundKind == Wildcard .SUPER ) {
708707 reduceOneConstraint (context , ConstraintTypeFormula .create (bound .right , t , ReductionResult .SUBTYPE ));
709- else
708+ capturesToRemove .add (gAlpha );
709+ } else {
710710 return false ;
711+ }
711712 }
712713 }
713714 }
714715 }
715716 } else {
716717 addBound (new TypeBound (alpha , ai , ReductionResult .SAME ), context .environment );
718+ capturesToRemove .add (gAlpha );
717719 }
718720 }
719721 }
720722 if (InferenceContext18 .DEBUG ) {
721- if (!this . captures .isEmpty ()) {
722- for (Entry < ParameterizedTypeBinding , ParameterizedTypeBinding > entry : this . captures . entrySet () ) {
723- System .out .println ("Dropping capture bound " + //$NON-NLS-1$
724- String .valueOf (entry . getKey () .shortReadableName ()) +
725- "=capture(" +String .valueOf (entry . getKey ( ).shortReadableName ())+")" ); //$NON-NLS-1$ //$NON-NLS-2$
723+ if (!capturesToRemove .isEmpty ()) {
724+ for (ParameterizedTypeBinding toRemove : capturesToRemove ) {
725+ System .out .println ("Removing capture bound " + //$NON-NLS-1$
726+ String .valueOf (toRemove .shortReadableName ()) +
727+ "=capture(" +String .valueOf (this . captures . get ( toRemove ).shortReadableName ())+")" ); //$NON-NLS-1$ //$NON-NLS-2$
726728 }
727729 }
728730 }
729- this .captures . clear ( );
731+ capturesToRemove . stream (). forEach ( c -> this .allCaptures . remove ( c ) );
730732 return true ;
731733 }
732734
733- void addTypeBoundsFromWildcardBound (InferenceContext18 context , InferenceSubstitution theta , int boundKind , TypeBinding t ,
735+ // try to infer and reduce a new constraint based on details of a given capture bound
736+ // return true iff a new constraint has been added indeed
737+ boolean addTypeBoundsFromWildcardBound (InferenceContext18 context , InferenceSubstitution theta , int boundKind , TypeBinding t ,
734738 TypeBinding r , TypeBinding bi ) throws InferenceFailureException {
735739 ConstraintFormula formula = null ;
736740 if (boundKind == Wildcard .EXTENDS ) {
@@ -741,8 +745,11 @@ void addTypeBoundsFromWildcardBound(InferenceContext18 context, InferenceSubstit
741745 } else {
742746 formula = ConstraintTypeFormula .create (theta .substitute (theta , bi ), r , ReductionResult .SUBTYPE );
743747 }
744- if (formula != null )
748+ if (formula != null ) {
745749 reduceOneConstraint (context , formula );
750+ return true ;
751+ }
752+ return false ;
746753 }
747754
748755 private ConstraintTypeFormula combineSameSame (TypeBound boundS , TypeBound boundT , Map <InferenceVariable ,TypeBound > properTypesByInferenceVariable ) {
@@ -1030,85 +1037,9 @@ public boolean reduceOneConstraint(InferenceContext18 context, ConstraintFormula
10301037 return true ; // no FALSE encountered
10311038 }
10321039
1033- /**
1034- * Helper for resolution (18.4):
1035- * Does this bound set define a direct dependency between the two given inference variables?
1036- */
1037- public boolean dependsOnResolutionOf (InferenceVariable alpha , InferenceVariable beta ) {
1038- alpha = alpha .prototype ();
1039- beta = beta .prototype ();
1040- if (TypeBinding .equalsEquals (alpha , beta ))
1041- return true ; // An inference variable α depends on the resolution of itself.
1042- boolean betaIsInCaptureLhs = false ;
1043- for (Entry <ParameterizedTypeBinding , ParameterizedTypeBinding > entry : this .captures .entrySet ()) { // TODO: optimization: consider separate index structure (by IV)
1044- ParameterizedTypeBinding g = entry .getKey ();
1045- for (int i = 0 ; i < g .arguments .length ; i ++) {
1046- if (TypeBinding .equalsEquals (g .arguments [i ], alpha )) {
1047- // An inference variable α appearing on the left-hand side of a bound of the form G<..., α, ...> = capture(G<...>)
1048- // depends on the resolution of every other inference variable mentioned in this bound (on both sides of the = sign).
1049- ParameterizedTypeBinding captured = entry .getValue ();
1050- if (captured .mentionsAny (new TypeBinding []{beta }, -1 /*don't care about index*/ ))
1051- return true ;
1052- if (g .mentionsAny (new TypeBinding []{beta }, i )) // exclude itself
1053- return true ;
1054- } else if (TypeBinding .equalsEquals (g .arguments [i ], beta )) {
1055- betaIsInCaptureLhs = true ;
1056- }
1057- }
1058- }
1059- if (betaIsInCaptureLhs ) { // swap α and β in the rule text to cover "then β depends on the resolution of α"
1060- ThreeSets sets = this .boundsPerVariable .get (beta );
1061- if (sets != null && sets .hasDependency (alpha ))
1062- return true ;
1063- } else {
1064- ThreeSets sets = this .boundsPerVariable .get (alpha );
1065- if (sets != null && sets .hasDependency (beta ))
1066- return true ;
1067- }
1068- return false ;
1069- }
1070-
1071- List <Set <InferenceVariable >> computeConnectedComponents (InferenceVariable [] inferenceVariables ) {
1072- // create all dependency edges (as bi-directional):
1073- Map <InferenceVariable , Set <InferenceVariable >> allEdges = new HashMap <>();
1074- for (int i = 0 ; i < inferenceVariables .length ; i ++) {
1075- InferenceVariable iv1 = inferenceVariables [i ];
1076- Set <InferenceVariable > targetSet = new LinkedHashSet <>();
1077- allEdges .put (iv1 , targetSet ); // eventually ensures: forall iv in inferenceVariables : allEdges.get(iv) != null
1078- for (int j = 0 ; j < i ; j ++) {
1079- InferenceVariable iv2 = inferenceVariables [j ];
1080- if (dependsOnResolutionOf (iv1 , iv2 ) || dependsOnResolutionOf (iv2 , iv1 )) {
1081- targetSet .add (iv2 );
1082- allEdges .get (iv2 ).add (iv1 );
1083- }
1084- }
1085- }
1086- // collect all connected IVs into one component:
1087- Set <InferenceVariable > visited = new LinkedHashSet <>();
1088- List <Set <InferenceVariable >> allComponents = new ArrayList <>();
1089- for (InferenceVariable inferenceVariable : inferenceVariables ) {
1090- Set <InferenceVariable > component = new LinkedHashSet <>();
1091- addConnected (component , inferenceVariable , allEdges , visited );
1092- if (!component .isEmpty ())
1093- allComponents .add (component );
1094- }
1095- return allComponents ;
1096- }
1097-
1098- private void addConnected (Set <InferenceVariable > component , InferenceVariable seed ,
1099- Map <InferenceVariable , Set <InferenceVariable >> allEdges , Set <InferenceVariable > visited )
1100- {
1101- if (visited .add (seed )) {
1102- // add all IVs starting from seed and reachable via any in allEdges:
1103- component .add (seed );
1104- for (InferenceVariable next : allEdges .get (seed ))
1105- addConnected (component , next , allEdges , visited );
1106- }
1107- }
1108-
11091040 // helper for 18.4
11101041 public boolean hasCaptureBound (Set <InferenceVariable > variableSet ) {
1111- for (ParameterizedTypeBinding g : this .captures .keySet ()) {
1042+ for (ParameterizedTypeBinding g : this .allCaptures .keySet ()) {
11121043 for (TypeBinding argument : g .arguments )
11131044 if (variableSet .contains (argument ))
11141045 return true ;
@@ -1159,6 +1090,13 @@ TypeBinding[] lowerBounds(InferenceVariable variable, boolean onlyProper) {
11591090 // appears as RHS the bound is by construction an inference variable,too.
11601091 }
11611092
1093+ public TypeBinding [] sameBounds (InferenceVariable variable ) {
1094+ ThreeSets three = this .boundsPerVariable .get (variable .prototype ());
1095+ if (three == null || three .sameBounds == null )
1096+ return Binding .NO_TYPES ;
1097+ return three .sameBounds .stream ().map (b -> b .right ).collect (Collectors .toList ()).toArray (TypeBinding []::new );
1098+ }
1099+
11621100 // debugging:
11631101 @ Override
11641102 public String toString () {
@@ -1167,7 +1105,7 @@ public String toString() {
11671105 for (TypeBound bound : flattened ) {
11681106 buf .append ('\t' ).append (bound .toString ()).append ('\n' );
11691107 }
1170- buf .append ("Capture Bounds:" ); //$NON-NLS-1$
1108+ buf .append ("Local Capture Bounds:" ); //$NON-NLS-1$
11711109 if (this .captures .isEmpty ()) {
11721110 buf .append (" <empty>\n " ); //$NON-NLS-1$
11731111 } else {
@@ -1178,6 +1116,20 @@ public String toString() {
11781116 buf .append ('\t' ).append (lhs ).append (" = capt(" ).append (rhs ).append (")\n " ); //$NON-NLS-1$ //$NON-NLS-2$
11791117 }
11801118 }
1119+ buf .append ("Other Capture Bounds:" ); //$NON-NLS-1$
1120+ if (this .allCaptures .isEmpty ()) {
1121+ buf .append (" <empty>\n " ); //$NON-NLS-1$
1122+ } else {
1123+ buf .append ('\n' );
1124+ for (Entry <ParameterizedTypeBinding , ParameterizedTypeBinding > entry : this .allCaptures .entrySet ()) {
1125+ if (this .captures .containsKey (entry .getKey ()))
1126+ continue ;
1127+ String lhs = String .valueOf (entry .getKey ().shortReadableName ());
1128+ String rhs = String .valueOf (entry .getValue ().shortReadableName ());
1129+ buf .append ('\t' );
1130+ buf .append (lhs ).append (" = capt(" ).append (rhs ).append (")\n " ); //$NON-NLS-1$ //$NON-NLS-2$
1131+ }
1132+ }
11811133 return buf .toString ();
11821134 }
11831135
0 commit comments