Attempting to use the number of 'base checks' as the limit, instead of wall-clock time.

This commit is contained in:
Jan Lahoda 2026-01-23 17:18:22 +01:00
parent e41e72684a
commit 8f1ea6b9cd
3 changed files with 21 additions and 23 deletions

View File

@ -61,7 +61,7 @@ import static com.sun.tools.javac.code.Flags.RECORD;
* deletion without notice.</b> * deletion without notice.</b>
*/ */
public class ExhaustivenessComputer { public class ExhaustivenessComputer {
private static final long DEFAULT_TIMEOUT = 5000; //5s private static final long DEFAULT_MAX_BASE_CHECKS = 4_000_000;
protected static final Context.Key<ExhaustivenessComputer> exhaustivenessKey = new Context.Key<>(); protected static final Context.Key<ExhaustivenessComputer> exhaustivenessKey = new Context.Key<>();
@ -70,8 +70,8 @@ public class ExhaustivenessComputer {
private final Check chk; private final Check chk;
private final Infer infer; private final Infer infer;
private final Map<Pair<Type, Type>, Boolean> isSubtypeCache = new HashMap<>(); private final Map<Pair<Type, Type>, Boolean> isSubtypeCache = new HashMap<>();
private final long missingExhaustivenessTimeout; private final long maxBaseChecks;
private long startTime = -1; private long baseChecks = -1;
public static ExhaustivenessComputer instance(Context context) { public static ExhaustivenessComputer instance(Context context) {
ExhaustivenessComputer instance = context.get(exhaustivenessKey); ExhaustivenessComputer instance = context.get(exhaustivenessKey);
@ -88,18 +88,18 @@ public class ExhaustivenessComputer {
chk = Check.instance(context); chk = Check.instance(context);
infer = Infer.instance(context); infer = Infer.instance(context);
Options options = Options.instance(context); Options options = Options.instance(context);
String timeout = options.get("exhaustivityTimeout"); String baseChecks = options.get("exhaustivityMaxBaseChecks");
long computedTimeout = DEFAULT_TIMEOUT; long computedMaxBaseChecks = DEFAULT_MAX_BASE_CHECKS;
if (timeout != null) { if (baseChecks != null) {
try { try {
computedTimeout = Long.parseLong(timeout); computedMaxBaseChecks = Long.parseLong(baseChecks);
} catch (NumberFormatException _) { } catch (NumberFormatException _) {
//ignore invalid values and use the default timeout //ignore invalid values and use the default timeout
} }
} }
missingExhaustivenessTimeout = computedTimeout; maxBaseChecks = computedMaxBaseChecks;
} }
public ExhaustivenessResult exhausts(JCExpression selector, List<JCCase> cases) { public ExhaustivenessResult exhausts(JCExpression selector, List<JCCase> cases) {
@ -458,8 +458,6 @@ public class ExhaustivenessComputer {
.filter(pd -> pd.nested.length == nestedPatternsCount) .filter(pd -> pd.nested.length == nestedPatternsCount)
.collect(groupingBy(pd -> useHashes ? pd.hashCode(mismatchingCandidateFin) : 0)); .collect(groupingBy(pd -> useHashes ? pd.hashCode(mismatchingCandidateFin) : 0));
for (var candidates : groupEquivalenceCandidates.values()) { for (var candidates : groupEquivalenceCandidates.values()) {
checkTimeout();
var candidatesArr = candidates.toArray(RecordPattern[]::new); var candidatesArr = candidates.toArray(RecordPattern[]::new);
for (int firstCandidate = 0; for (int firstCandidate = 0;
@ -691,9 +689,9 @@ public class ExhaustivenessComputer {
} }
protected void checkTimeout() { protected void checkTimeout() {
if (startTime != (-1) && if (baseChecks != (-1) &&
(System.currentTimeMillis() - startTime) > missingExhaustivenessTimeout) { ++baseChecks > maxBaseChecks) {
throw new TimeoutException(null); throw new TooManyChecksException(null);
} }
} }
@ -831,21 +829,21 @@ public class ExhaustivenessComputer {
//computation of missing patterns: //computation of missing patterns:
protected Set<PatternDescription> computeMissingPatternDescriptions(Type selectorType, protected Set<PatternDescription> computeMissingPatternDescriptions(Type selectorType,
Set<PatternDescription> incompletePatterns) { Set<PatternDescription> incompletePatterns) {
if (missingExhaustivenessTimeout == 0) { if (maxBaseChecks == 0) {
return Set.of(); return Set.of();
} }
try { try {
startTime = System.currentTimeMillis(); baseChecks = 0;
PatternDescription defaultPattern = new BindingPattern(selectorType); PatternDescription defaultPattern = new BindingPattern(selectorType);
return expandMissingPatternDescriptions(selectorType, return expandMissingPatternDescriptions(selectorType,
selectorType, selectorType,
defaultPattern, defaultPattern,
incompletePatterns, incompletePatterns,
Set.of(defaultPattern)); Set.of(defaultPattern));
} catch (TimeoutException ex) { } catch (TooManyChecksException ex) {
return ex.missingPatterns != null ? ex.missingPatterns : Set.of(); return ex.missingPatterns != null ? ex.missingPatterns : Set.of();
} finally { } finally {
startTime = -1; baseChecks = -1;
} }
} }
@ -858,9 +856,9 @@ public class ExhaustivenessComputer {
return doExpandMissingPatternDescriptions(selectorType, targetType, return doExpandMissingPatternDescriptions(selectorType, targetType,
toExpand, basePatterns, toExpand, basePatterns,
inMissingPatterns); inMissingPatterns);
} catch (TimeoutException ex) { } catch (TooManyChecksException ex) {
if (ex.missingPatterns == null) { if (ex.missingPatterns == null) {
ex = new TimeoutException(inMissingPatterns); ex = new TooManyChecksException(inMissingPatterns);
} }
throw ex; throw ex;
} }
@ -1233,11 +1231,11 @@ public class ExhaustivenessComputer {
LOOSE; LOOSE;
} }
protected static class TimeoutException extends RuntimeException { protected static class TooManyChecksException extends RuntimeException {
private static final long serialVersionUID = 0L; private static final long serialVersionUID = 0L;
private transient final Set<PatternDescription> missingPatterns; private transient final Set<PatternDescription> missingPatterns;
public TimeoutException(Set<PatternDescription> missingPatterns) { public TooManyChecksException(Set<PatternDescription> missingPatterns) {
super(null, null, false, false); super(null, null, false, false);
this.missingPatterns = missingPatterns; this.missingPatterns = missingPatterns;
} }

View File

@ -2523,7 +2523,7 @@ public class Exhaustiveness extends TestRunner {
"-Xlint:-preview", "-Xlint:-preview",
"--class-path", libClasses.toString(), "--class-path", libClasses.toString(),
"-XDshould-stop.at=FLOW", "-XDshould-stop.at=FLOW",
"-XDexhaustivityTimeout=0", "-XDexhaustivityMaxBaseChecks=0",
stopAtFlow ? "-XDshould-stop.ifNoError=FLOW" stopAtFlow ? "-XDshould-stop.ifNoError=FLOW"
: "-XDnoop") : "-XDnoop")
.outdir(classes) .outdir(classes)

View File

@ -564,7 +564,7 @@ public class ExhaustivenessConvenientErrors extends TestRunner {
"--class-path", libClasses.toString(), "--class-path", libClasses.toString(),
"-XDshould-stop.at=FLOW", "-XDshould-stop.at=FLOW",
"-XDshould-stop.ifNoError=FLOW", "-XDshould-stop.ifNoError=FLOW",
"-XDexhaustivityTimeout=" + Long.MAX_VALUE) //never timeout "-XDexhaustivityMaxBaseChecks=" + Long.MAX_VALUE) //never give up
.outdir(classes) .outdir(classes)
.files(tb.findJavaFiles(src)) .files(tb.findJavaFiles(src))
.diagnosticListener(d -> { .diagnosticListener(d -> {