@@ -56,7 +56,7 @@ public String toString() {
5656 /**
5757 * A list of constraints in this set. It does not contain constraints that are equal. This needs
5858 * to be kept in the order created, which should be lexically left to right. This is so the {@link
59- * #getClosedSubset(Dependencies)} is computed correctly.
59+ * #getClosedSubset(ConstraintSet, Dependencies)} is computed correctly.
6060 */
6161 private final List <Constraint > list ;
6262
@@ -139,6 +139,28 @@ public Constraint pop() {
139139 return list .remove (0 );
140140 }
141141
142+ /**
143+ * Adds the constraint to the beginning of this set.
144+ *
145+ * @param constraint a constraint
146+ */
147+ public void push (Constraint constraint ) {
148+ if (constraint != null && !list .contains (constraint )) {
149+ list .add (0 , constraint );
150+ }
151+ }
152+
153+ /**
154+ * Adds the constraints to the beginning of this set and maintains the order of the constraints.
155+ *
156+ * @param constraints constraints
157+ */
158+ public void pushAll (ConstraintSet constraints ) {
159+ for (int i = constraints .list .size () - 1 ; i > -1 ; i --) {
160+ this .push (constraints .list .get (i ));
161+ }
162+ }
163+
142164 /**
143165 * Remove all constraints in {@code subset} from this constraint set.
144166 *
@@ -153,35 +175,61 @@ public void remove(ConstraintSet subset) {
153175 }
154176
155177 /**
156- * A subset of constraints is selected in this constraint set, satisfying the property that, for
157- * each constraint, no input variable can influence an output variable of another constraint in
158- * this constraint set . (See JLS 18.5.2.2)
178+ * Returns a subset of {@code c}; for each constraint in the subset, no input variable can
179+ * influence an output variable of another constraint in C. If that subset is empty, returns a set
180+ * containing a single constraint that participates in a constraint cycle . (See JLS 18.5.2.2)
159181 *
182+ * @param c a constraint set
160183 * @param dependencies an object describing the dependencies of inference variables
161- * @return s a subset of constraints is this constraint set
184+ * @return s a subset of constraints in {@code c} whose inputs do not affect {@code c}'s outputs,
185+ * or a singleton constraint from a constraint cycle
162186 */
163- public ConstraintSet getClosedSubset (Dependencies dependencies ) {
187+ public static ConstraintSet getClosedSubset (ConstraintSet c , Dependencies dependencies ) {
164188 ConstraintSet subset = new ConstraintSet ();
165- Set <Variable > inputDependencies = new LinkedHashSet <>();
166- Set <Variable > outDependencies = new LinkedHashSet <>();
167- for (Constraint constraint : list ) {
189+ // Collection of all outputs of c.
190+ Set <Variable > allOutputsOfC = new LinkedHashSet <>();
191+ for (Constraint constraint : c .list ) {
192+ if (constraint instanceof TypeConstraint ) {
193+ allOutputsOfC .addAll (((TypeConstraint ) constraint ).getOutputVariables ());
194+ }
195+ // No other constraints have output variables
196+ }
197+
198+ // From JLS 18.5.2.2:
199+ // A subset of constraints is selected in C, satisfying the property that, for each
200+ // constraint, no input variable can influence an output variable of another
201+ // constraint in C. The terms input variable and output variable are defined
202+ // below. An inference variable alpha can influence an inference variable beta if alpha
203+ // depends on the resolution of beta (§18.4), or vice versa; or if there exists a third
204+ // inference variable gamma such that alpha can influence gamma and gamma can influence beta.
205+
206+ // Put another way:
207+ // Find a subset of the set c where the following is true for all the constraints in the subset:
208+ // no input variable of a constraint can influence an output variable of any constraint in c.
209+ // (Influence means that neither variable can depend on the other.)
210+ // The JLS does not specify whether this subset should be as large as possible, but this
211+ // implementation returns the largest subset possible.
212+ for (Constraint constraint : c .list ) {
168213 if (constraint .getKind () == Kind .EXPRESSION
169214 || constraint .getKind () == Kind .LAMBDA_EXCEPTION
170215 || constraint .getKind () == Kind .METHOD_REF_EXCEPTION ) {
171- TypeConstraint c = (TypeConstraint ) constraint ;
172- Set <Variable > newInputs = dependencies .get (c .getInputVariables ());
173- Set <Variable > newOutputs = dependencies .get (c .getOutputVariables ());
174- if (Collections .disjoint (newInputs , outDependencies )
175- && Collections .disjoint (newOutputs , inputDependencies )) {
176- inputDependencies .addAll (newInputs );
177- outDependencies .addAll (newOutputs );
178- subset .add (c );
179- } else {
180- // A cycle (or cycles) in the graph of dependencies between constraints exists.
181- subset = new ConstraintSet ();
182- break ;
216+ List <Variable > inputsOfSingleConstraint = ((TypeConstraint ) constraint ).getInputVariables ();
217+ boolean foundInfluence = false ;
218+ inputLoop :
219+ for (Variable in : inputsOfSingleConstraint ) {
220+ for (Variable out : allOutputsOfC ) {
221+ if (dependencies .get (in ).contains (out ) || dependencies .get (out ).contains (in )) {
222+ foundInfluence = true ;
223+ break inputLoop ;
224+ }
225+ }
226+ }
227+ if (!foundInfluence ) {
228+ // None of the inputs of constraint influence any output of any constraint in C.
229+ subset .add (constraint );
183230 }
184231 } else {
232+ // Other kinds of constraints do not have input variables.
185233 subset .add (constraint );
186234 }
187235 }
@@ -190,40 +238,47 @@ public ConstraintSet getClosedSubset(Dependencies dependencies) {
190238 return subset ;
191239 }
192240
193- outDependencies .clear ();
194- inputDependencies .clear ();
195- // If this subset is empty, then there is a cycle (or cycles) in the graph of dependencies
196- // between constraints.
241+ // TODO: double check that this code is correct.
242+ // checker/tests/all-systems/java8inference/MapEntryGetFails.java is a test that uses this code.
243+
244+ Set <Variable > inputDependencies = new LinkedHashSet <>();
245+ Set <Variable > outDependencies = new LinkedHashSet <>();
246+ // If this subset is empty then no closed subset was found and there is a cycle (or cycles) in
247+ // the graph of dependencies between constraints.
248+
249+ // From JLS 18.5.2.2:
250+ // In this case, the constraints in C that participate in a dependency cycle (or cycles) and do
251+ // not depend on any constraints outside of the cycle (or cycles) are considered.
252+ // A single constraint is selected from the considered constraints, as follows:
253+
254+ // If any of the considered constraints have the form <Expression -> T>, then the selected
255+ // constraint is the considered constraint of this form that contains the expression to the
256+ // left (3.5) of the expression of every other considered constraint of this form.
257+
258+ // If no considered constraint has the form <Expression -> T>, then the selected constraint
259+ // is the considered constraint that contains the expression to the left of the expression
260+ // of every other considered constraint.
197261 List <Constraint > consideredConstraints = new ArrayList <>();
198- for (Constraint constraint : list ) {
262+ for (Constraint constraint : c . list ) {
199263 if (!(constraint instanceof TypeConstraint )) {
200264 continue ;
201265 }
202- TypeConstraint c = (TypeConstraint ) constraint ;
203- Set <Variable > newInputs = dependencies .get (c .getInputVariables ());
204- Set <Variable > newOutputs = dependencies .get (c .getOutputVariables ());
266+
267+ TypeConstraint typeConstraint = (TypeConstraint ) constraint ;
268+ Set <Variable > newInputs = dependencies .get (typeConstraint .getInputVariables ());
269+ Set <Variable > newOutputs = dependencies .get (typeConstraint .getOutputVariables ());
205270 if (inputDependencies .isEmpty ()
206271 || !Collections .disjoint (newInputs , outDependencies )
207272 || !Collections .disjoint (newOutputs , inputDependencies )) {
208273 inputDependencies .addAll (newInputs );
209274 outDependencies .addAll (newOutputs );
210- consideredConstraints .add (c );
275+ consideredConstraints .add (typeConstraint );
211276 }
212277 }
213278
214- // A single constraint is selected from the considered constraints, as follows:
215-
216- // If any of the considered constraints have the form <Expression -> T>, then the selected
217- // constraint is the considered constraint of this form that contains the expression to the
218- // left (3.5) of the expression of every other considered constraint of this form.
219-
220- // If no considered constraint has the form <Expression -> T>, then the selected constraint
221- // is the considered constraint that contains the expression to the left of the expression
222- // of every other considered constraint.
223-
224- for (Constraint c : consideredConstraints ) {
225- if (c .getKind () == Kind .EXPRESSION ) {
226- return new ConstraintSet (c );
279+ for (Constraint constraint : consideredConstraints ) {
280+ if (constraint .getKind () == Kind .EXPRESSION ) {
281+ return new ConstraintSet (constraint );
227282 }
228283 }
229284
@@ -334,9 +389,19 @@ public BoundSet reduceOneStep(Java8InferenceContext context) {
334389 }
335390 this .addAll (((ReductionResultPair ) result ).constraintSet );
336391 } else if (result instanceof TypeConstraint ) {
337- this .add ((Constraint ) result );
392+ // Add the new constraints to the beginning of the list so they are reduced first. This is
393+ // because each constraint is supposed to be reduced until no other constraints are produced
394+ // before moving onto another constraint.
395+ this .push ((Constraint ) result );
338396 } else if (result instanceof ConstraintSet ) {
339- this .addAll ((ConstraintSet ) result );
397+ if (result == TRUE_ANNO_FAIL ) {
398+ this .annotationFailure = true ;
399+ } else {
400+ // Add the new constraints to the beginning of the list so they are reduced first. This is
401+ // because each constraint is supposed to be reduced until no other constraints are produced
402+ // before moving onto another constraint.
403+ this .pushAll ((ConstraintSet ) result );
404+ }
340405 } else if (result instanceof BoundSet ) {
341406 boundSet .merge ((BoundSet ) result );
342407 if (boundSet .containsFalse ()) {
0 commit comments