diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index cc742b6a0b49..7b15d745ce50 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -16,6 +16,7 @@ jobs: pwd ls -al env | sort + set displayName: show environment - bash: pwd && ls && bash ./configure --with-jtreg=/usr/share/jtreg --disable-warnings-as-errors displayName: configure @@ -41,6 +42,7 @@ jobs: pwd ls -al env | sort + set displayName: show environment - bash: | set -ex diff --git a/.azure/azure-pipelines.yml.m4 b/.azure/azure-pipelines.yml.m4 index 9e61f0992fc2..5bfd3efc35ba 100644 --- a/.azure/azure-pipelines.yml.m4 +++ b/.azure/azure-pipelines.yml.m4 @@ -19,6 +19,7 @@ jobs: pwd ls -al env | sort + set displayName: show environment - bash: pwd && ls && bash ./configure --with-jtreg=/usr/share/jtreg --disable-warnings-as-errors displayName: configure @@ -44,6 +45,7 @@ jobs: pwd ls -al env | sort + set displayName: show environment - bash: | set -ex diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92232b8dc223..7f951b3713a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,10 +42,7 @@ jobs: git config --get remote.origin.url || true pwd ls -al - echo "HOME=${HOME}" - echo "USER=${USER}" - echo "SHELL=${SHELL}" - echo "GITHUB_WORKSPACE=${GITHUB_WORKSPACE}" + set - name: configure run: | pwd @@ -73,10 +70,7 @@ jobs: git config --get remote.origin.url || true pwd ls -al - echo "HOME=${HOME}" - echo "USER=${USER}" - echo "SHELL=${SHELL}" - echo "GITHUB_WORKSPACE=${GITHUB_WORKSPACE}" + set - name: clone git-scripts run: | set -ex @@ -199,6 +193,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -218,6 +215,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -237,6 +237,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -256,6 +259,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -275,6 +281,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -294,6 +303,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -313,6 +325,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -332,6 +347,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -351,6 +369,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -370,6 +391,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -389,6 +413,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -408,6 +435,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -427,6 +457,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -446,6 +479,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -465,6 +501,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -484,6 +523,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -504,6 +546,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -523,6 +568,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -542,6 +590,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -561,6 +612,9 @@ jobs: fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework diff --git a/.github/workflows/ci.yml.m4 b/.github/workflows/ci.yml.m4 index f599bbc4b2c3..7ab2510013f9 100644 --- a/.github/workflows/ci.yml.m4 +++ b/.github/workflows/ci.yml.m4 @@ -46,10 +46,7 @@ jobs: git config --get remote.origin.url || true pwd ls -al - echo "HOME=${HOME}" - echo "USER=${USER}" - echo "SHELL=${SHELL}" - echo "GITHUB_WORKSPACE=${GITHUB_WORKSPACE}" + set - name: configure run: | pwd @@ -77,10 +74,7 @@ jobs: git config --get remote.origin.url || true pwd ls -al - echo "HOME=${HOME}" - echo "USER=${USER}" - echo "SHELL=${SHELL}" - echo "GITHUB_WORKSPACE=${GITHUB_WORKSPACE}" + set - name: clone git-scripts run: | set -ex diff --git a/.github/workflows/defs.m4 b/.github/workflows/defs.m4 index da9dc249f6a8..bfdff9adaf1f 100644 --- a/.github/workflows/defs.m4 +++ b/.github/workflows/defs.m4 @@ -25,6 +25,9 @@ define([cftests_job], [dnl fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -46,6 +49,9 @@ define([daikon_job], [dnl fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework @@ -66,6 +72,9 @@ define([plume_lib_job], [dnl fetch-depth: 25 show-progress: false persist-credentials: false + - name: git config + run: | + git config --global --add safe.directory /__w/jdk/jdk - name: clone git-scripts run: mkdir -p /tmp/$USER && git -C /tmp/$USER clone --depth=1 -q https://github.com/plume-lib/git-scripts.git - name: clone checker-framework diff --git a/README-merging.el b/README-merging.el deleted file mode 100644 index 1f8c243e7206..000000000000 --- a/README-merging.el +++ /dev/null @@ -1 +0,0 @@ -;; See file conflict-resolve.el diff --git a/README.md b/README.md index e848cb2e779d..8767fa374a25 100644 --- a/README.md +++ b/README.md @@ -141,19 +141,19 @@ To update that copy, run the command below from this directory: rm -f checker-qual.jar && \ cp -p $CHECKERFRAMEWORK/checker-qual/build/libs/checker-qual-*-sources.jar checker-qual.jar && \ (cd src/java.base/share/classes && rm -rf org/checkerframework && \ + touch META-INF && \ unzip ../../../../checker-qual.jar -x module-info.java -x 'META-INF*' && \ rm -f org/checkerframework/checker/signedness/SignednessUtilExtra.java && \ chmod -R u+w org/checkerframework) && \ jar tf checker-qual.jar | grep '\.java$' | grep -v 'module-info.java' | sed 's/\/[^/]*\.java/;/' | sed 's/\//./g' | sed 's/^/ exports /' | sort -u ``` -The result of the command will be a list of export lines. -Replace the existing export lines present in -`src/java.base/share/classes/module-info.java` with the newly-generated list of -exports. If no new packages were added, then likely no changes are needed -in the `module-info.java` file. +In addition to updating `.java` files, the command will output a list of export +lines. Place them in `src/java.base/share/classes/module-info.java`. If no new +packages were recently added to the Checker Framework, then likely no changes +are needed in the `module-info.java` file. -Commit the changes, including the new `checker.jar` file and any new `.java` +Commit the changes, including the new `checker-qual.jar` file and any new `.java` files in a `qual/` directory. (Both are used, by different parts of the build.) ## The typetools/jdk21u repository diff --git a/checker-qual.jar b/checker-qual.jar index 1f4c88a8511f..cfa203061908 100644 Binary files a/checker-qual.jar and b/checker-qual.jar differ diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java index f8343235e34b..377d7b30e1f0 100644 --- a/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java @@ -4099,6 +4099,7 @@ void clear() { /** * Returns number of handles registered in table. */ + @Pure int size() { return size; } @@ -4148,6 +4149,7 @@ public int get(int index) { return list[index]; } + @Pure public int size() { return size; } diff --git a/src/java.base/share/classes/java/io/ObjectOutputStream.java b/src/java.base/share/classes/java/io/ObjectOutputStream.java index 1361cdc2c34e..1b8b465e512d 100644 --- a/src/java.base/share/classes/java/io/ObjectOutputStream.java +++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java @@ -31,6 +31,7 @@ import org.checkerframework.checker.mustcall.qual.MustCallAlias; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.signedness.qual.PolySigned; +import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.framework.qual.AnnotatedFor; import java.security.AccessController; @@ -2344,6 +2345,7 @@ void clear() { /** * Returns the number of mappings currently in table. */ + @Pure int size() { return size; } @@ -2444,6 +2446,7 @@ void clear() { /** * Returns the number of mappings currently in table. */ + @Pure int size() { return htab.size(); } diff --git a/src/java.base/share/classes/java/lang/Iterable.java b/src/java.base/share/classes/java/lang/Iterable.java index 2b0cacbbfbc6..8a6b8658e80e 100644 --- a/src/java.base/share/classes/java/lang/Iterable.java +++ b/src/java.base/share/classes/java/lang/Iterable.java @@ -25,6 +25,7 @@ package java.lang; import org.checkerframework.checker.index.qual.PolyGrowShrink; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; import org.checkerframework.common.aliasing.qual.NonLeaked; import org.checkerframework.framework.qual.AnnotatedFor; @@ -51,7 +52,7 @@ public interface Iterable { * * @return an Iterator. */ - @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty Iterable this); + @MaybeModifiable @PolyGrowShrink @PolyNonEmpty Iterator iterator(@MaybeModifiable @PolyGrowShrink @PolyNonEmpty Iterable this); /** * Performs the given action for each element of the {@code Iterable} diff --git a/src/java.base/share/classes/java/lang/LiveStackFrame.java b/src/java.base/share/classes/java/lang/LiveStackFrame.java index 77a02d083c80..d5aba81992b0 100644 --- a/src/java.base/share/classes/java/lang/LiveStackFrame.java +++ b/src/java.base/share/classes/java/lang/LiveStackFrame.java @@ -24,6 +24,8 @@ */ package java.lang; +import org.checkerframework.dataflow.qual.Pure; + import java.lang.StackWalker.StackFrame; import java.util.EnumSet; import java.util.Set; @@ -125,6 +127,7 @@ public abstract class PrimitiveSlot { /** * Returns the size, in bytes, of the slot. */ + @Pure public abstract int size(); /** diff --git a/src/java.base/share/classes/java/lang/LiveStackFrameInfo.java b/src/java.base/share/classes/java/lang/LiveStackFrameInfo.java index b6583adf559f..d95db71f4055 100644 --- a/src/java.base/share/classes/java/lang/LiveStackFrameInfo.java +++ b/src/java.base/share/classes/java/lang/LiveStackFrameInfo.java @@ -24,6 +24,8 @@ */ package java.lang; +import org.checkerframework.dataflow.qual.Pure; + final class LiveStackFrameInfo extends StackFrameInfo implements LiveStackFrame { private static Object[] EMPTY_ARRAY = new Object[0]; @@ -92,6 +94,7 @@ private static class PrimitiveSlot32 extends PrimitiveSlot { } @Override + @Pure public int size() { return 4; } @@ -114,6 +117,7 @@ private static class PrimitiveSlot64 extends PrimitiveSlot { } @Override + @Pure public int size() { return 8; } diff --git a/src/java.base/share/classes/java/lang/ProcessBuilder.java b/src/java.base/share/classes/java/lang/ProcessBuilder.java index 73a4e5ce93b4..7896ab818ac3 100644 --- a/src/java.base/share/classes/java/lang/ProcessBuilder.java +++ b/src/java.base/share/classes/java/lang/ProcessBuilder.java @@ -25,6 +25,8 @@ package java.lang; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; import org.checkerframework.checker.mustcall.qual.MustCall; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.tainting.qual.Untainted; @@ -200,7 +202,7 @@ public final class ProcessBuilder // Lazily and racy initialize when needed, racy is ok, any logger is ok private static System.Logger LOGGER; - private List command; + @Modifiable @IteratorPolyMod private List command; private File directory; private Map environment; private boolean redirectErrorStream; @@ -284,7 +286,7 @@ public ProcessBuilder command(@Untainted String... command) { * * @return this process builder's program and its arguments */ - public List<@Untainted String> command() { + public @Modifiable @IteratorPolyMod List<@Untainted String> command() { return command; } diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index d30cb1f78c07..86f3020e73e2 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -2621,6 +2621,7 @@ public int hashCode() { * @since 21 */ @Pure + @StaticallyExecutable public int indexOf(int ch, int beginIndex, int endIndex) { checkBoundsBeginEnd(beginIndex, endIndex, length()); return isLatin1() ? StringLatin1.indexOf(value, ch, beginIndex, endIndex) @@ -2786,6 +2787,7 @@ public int indexOf(int ch, int beginIndex, int endIndex) { * @since 21 */ @Pure + @StaticallyExecutable public int indexOf(String str, int beginIndex, int endIndex) { if (str.length() == 1) { /* Simple optimization, can be omitted without behavioral impact */ diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java index b4da360e6aa3..ab92a305648d 100644 --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -25,6 +25,8 @@ package java.lang; +import org.checkerframework.common.value.qual.StaticallyExecutable; +import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectsOnly; import java.util.Arrays; @@ -187,6 +189,8 @@ public static int compareToCI_UTF16(byte[] value, byte[] other) { return len1 - len2; } + @Pure + @StaticallyExecutable public static int hashCode(byte[] value) { return switch (value.length) { case 0 -> 0; @@ -195,6 +199,8 @@ public static int hashCode(byte[] value) { }; } + @Pure + @StaticallyExecutable public static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) { if (!canEncode(ch)) { return -1; @@ -218,6 +224,8 @@ private static int indexOfChar(byte[] value, int ch, int fromIndex, int max) { return -1; } + @Pure + @StaticallyExecutable @IntrinsicCandidate public static int indexOf(byte[] value, byte[] str) { if (str.length == 0) { @@ -229,6 +237,8 @@ public static int indexOf(byte[] value, byte[] str) { return indexOf(value, value.length, str, str.length, 0); } + @Pure + @StaticallyExecutable @IntrinsicCandidate public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { byte first = str[0]; @@ -252,6 +262,8 @@ public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount return -1; } + @Pure + @StaticallyExecutable public static int lastIndexOf(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { int min = tgtCount - 1; @@ -280,6 +292,8 @@ public static int lastIndexOf(byte[] src, int srcCount, } } + @Pure + @StaticallyExecutable public static int lastIndexOf(final byte[] value, int ch, int fromIndex) { if (!canEncode(ch)) { return -1; @@ -583,6 +597,8 @@ public static String trim(byte[] value) { newString(value, st, len - st) : null; } + @Pure + @StaticallyExecutable public static int indexOfNonWhitespace(byte[] value) { int length = value.length; int left = 0; @@ -596,6 +612,8 @@ public static int indexOfNonWhitespace(byte[] value) { return left; } + @Pure + @StaticallyExecutable public static int lastIndexOfNonWhitespace(byte[] value) { int length = value.length; int right = length; diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 056045cb753a..6994da75bab8 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -25,6 +25,7 @@ package java.lang; +import org.checkerframework.common.value.qual.StaticallyExecutable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectsOnly; @@ -415,6 +416,8 @@ public static int compareToCI_Latin1(byte[] value, byte[] other) { return -StringLatin1.compareToCI_UTF16(other, value); } + @Pure + @StaticallyExecutable public static int hashCode(byte[] value) { return switch (value.length) { case 0 -> 0; @@ -423,6 +426,8 @@ public static int hashCode(byte[] value) { }; } + @Pure + @StaticallyExecutable public static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) { fromIndex = Math.max(fromIndex, 0); toIndex = Math.min(toIndex, value.length >> 1); @@ -438,6 +443,8 @@ public static int indexOf(byte[] value, int ch, int fromIndex, int toIndex) { } } + @Pure + @StaticallyExecutable @IntrinsicCandidate public static int indexOf(byte[] value, byte[] str) { if (str.length == 0) { @@ -449,6 +456,8 @@ public static int indexOf(byte[] value, byte[] str) { return indexOfUnsafe(value, length(value), str, length(str), 0); } + @Pure + @StaticallyExecutable @IntrinsicCandidate public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { checkBoundsBeginEnd(fromIndex, valueCount, value); @@ -487,6 +496,8 @@ private static int indexOfUnsafe(byte[] value, int valueCount, byte[] str, int s /** * Handles indexOf Latin1 substring in UTF16 string. */ + @Pure + @StaticallyExecutable @IntrinsicCandidate public static int indexOfLatin1(byte[] value, byte[] str) { if (str.length == 0) { @@ -498,6 +509,8 @@ public static int indexOfLatin1(byte[] value, byte[] str) { return indexOfLatin1Unsafe(value, length(value), str, str.length, 0); } + @Pure + @StaticallyExecutable @IntrinsicCandidate public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { checkBoundsBeginEnd(fromIndex, srcCount, src); @@ -505,6 +518,8 @@ public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCou return indexOfLatin1Unsafe(src, srcCount, tgt, tgtCount, fromIndex); } + @Pure + @StaticallyExecutable public static int indexOfLatin1Unsafe(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { assert fromIndex >= 0; assert tgtCount > 0; @@ -566,6 +581,8 @@ private static int indexOfSupplementary(byte[] value, int ch, int fromIndex, int } // srcCoder == UTF16 && tgtCoder == UTF16 + @Pure + @StaticallyExecutable public static int lastIndexOf(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { assert fromIndex >= 0; @@ -601,6 +618,8 @@ public static int lastIndexOf(byte[] src, int srcCount, } } + @Pure + @StaticallyExecutable public static int lastIndexOf(byte[] value, int ch, int fromIndex) { if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { // handle most cases here (ch is a BMP code point or a @@ -1017,6 +1036,8 @@ public static String trim(byte[] value) { null; } + @Pure + @StaticallyExecutable public static int indexOfNonWhitespace(byte[] value) { int length = value.length >> 1; int left = 0; @@ -1030,6 +1051,8 @@ public static int indexOfNonWhitespace(byte[] value) { return left; } + @Pure + @StaticallyExecutable public static int lastIndexOfNonWhitespace(byte[] value) { int length = value.length >>> 1; int right = length; @@ -1469,6 +1492,8 @@ public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int l } // srcCoder == UTF16 && tgtCoder == LATIN1 + @Pure + @StaticallyExecutable public static int lastIndexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { assert fromIndex >= 0; diff --git a/src/java.base/share/classes/java/lang/ThreadLocal.java b/src/java.base/share/classes/java/lang/ThreadLocal.java index f3fb82e38b76..3b374c08a434 100644 --- a/src/java.base/share/classes/java/lang/ThreadLocal.java +++ b/src/java.base/share/classes/java/lang/ThreadLocal.java @@ -27,6 +27,7 @@ import org.checkerframework.checker.interning.qual.UsesObjectEquals; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.framework.qual.AnnotatedFor; import org.checkerframework.framework.qual.CFComment; @@ -493,6 +494,7 @@ private ThreadLocalMap(ThreadLocalMap parentMap) { /** * Returns the number of elements in the map. */ + @Pure int size() { return size; } diff --git a/src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java b/src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java index 674838eca05b..901e9607c8ee 100644 --- a/src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java +++ b/src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java @@ -47,7 +47,9 @@ abstract class AbstractConstantGroup implements ConstantGroup { this.size = size; } - @Override public final int size() { + @Override + @Pure + public final int size() { return size; } diff --git a/src/java.base/share/classes/java/util/AbstractCollection.java b/src/java.base/share/classes/java/util/AbstractCollection.java index 588bcd68f2b3..0c02565aa4c6 100644 --- a/src/java.base/share/classes/java/util/AbstractCollection.java +++ b/src/java.base/share/classes/java/util/AbstractCollection.java @@ -29,6 +29,13 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; @@ -99,10 +106,10 @@ protected AbstractCollection() { * @return an iterator over the elements contained in this collection */ @SideEffectFree - public abstract @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty AbstractCollection this); + public abstract @MaybeModifiable @PolyGrowShrink @PolyNonEmpty Iterator iterator(@MaybeModifiable @PolyGrowShrink @PolyNonEmpty AbstractCollection this); @Pure - public abstract @NonNegative int size(@GuardSatisfied AbstractCollection this); + public abstract @NonNegative int size(@MaybeModifiable @GuardSatisfied AbstractCollection this); /** * {@inheritDoc} @@ -112,7 +119,7 @@ protected AbstractCollection() { */ @Pure @EnsuresNonEmptyIf(result = false, expression = "this") - public boolean isEmpty(@GuardSatisfied AbstractCollection this) { + public boolean isEmpty(@MaybeModifiable @GuardSatisfied AbstractCollection this) { return size() == 0; } @@ -128,7 +135,7 @@ public boolean isEmpty(@GuardSatisfied AbstractCollection this) { */ @Pure @EnsuresNonEmptyIf(result = true, expression = "this") - public boolean contains(@GuardSatisfied AbstractCollection this, @GuardSatisfied @UnknownSignedness Object o) { + public boolean contains(@MaybeModifiable @GuardSatisfied AbstractCollection this, @GuardSatisfied @UnknownSignedness Object o) { Iterator it = iterator(); if (o==null) { while (it.hasNext()) @@ -166,7 +173,7 @@ public boolean contains(@GuardSatisfied AbstractCollection this, @GuardSatisf * } */ @SideEffectFree - public @PolyNull @PolySigned Object[] toArray(AbstractCollection<@PolyNull @PolySigned E> this) { + public @PolyNull @PolySigned Object[] toArray(@MaybeModifiable AbstractCollection<@PolyNull @PolySigned E> this) { // Estimate size of array; be prepared to see more or fewer elements Object[] r = new Object[size()]; Iterator it = iterator(); @@ -207,7 +214,7 @@ public boolean contains(@GuardSatisfied AbstractCollection this, @GuardSatisf * @throws NullPointerException {@inheritDoc} */ @SuppressWarnings("unchecked") - public @Nullable T [] toArray(@PolyNull T [] a) { + public @Nullable T [] toArray(@MaybeModifiable AbstractCollection this, @PolyNull T [] a) { // Estimate size of array; be prepared to see more or fewer elements int size = size(); T[] r = a.length >= size ? a : @@ -280,7 +287,7 @@ private static T[] finishToArray(T[] r, Iterator it) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(@GuardSatisfied AbstractCollection this, E e) { + public boolean add(@IteratorPolyMod @Growable @GuardSatisfied AbstractCollection this, E e) { throw new UnsupportedOperationException(); } @@ -303,7 +310,7 @@ public boolean add(@GuardSatisfied AbstractCollection this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @CanShrink AbstractCollection this, @GuardSatisfied @UnknownSignedness Object o) { + public boolean remove(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink AbstractCollection this, @GuardSatisfied @UnknownSignedness Object o) { Iterator it = iterator(); if (o==null) { while (it.hasNext()) { @@ -340,7 +347,7 @@ public boolean remove(@GuardSatisfied @CanShrink AbstractCollection this, @Gu * @see #contains(Object) */ @Pure - public boolean containsAll(@GuardSatisfied AbstractCollection this, @GuardSatisfied Collection c) { + public boolean containsAll(@MaybeModifiable @GuardSatisfied AbstractCollection this, @GuardSatisfied Collection c) { for (Object e : c) if (!contains(e)) return false; @@ -368,7 +375,7 @@ public boolean containsAll(@GuardSatisfied AbstractCollection this, @GuardSat */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(@GuardSatisfied AbstractCollection this, Collection c) { + public boolean addAll(@Growable @GuardSatisfied AbstractCollection this, Collection c) { boolean modified = false; for (E e : c) if (add(e)) @@ -400,7 +407,7 @@ public boolean addAll(@GuardSatisfied AbstractCollection this, Collection this, Collection c) { + public boolean removeAll(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink AbstractCollection this, Collection c) { Objects.requireNonNull(c); boolean modified = false; Iterator it = iterator(); @@ -437,7 +444,7 @@ public boolean removeAll(@GuardSatisfied @CanShrink AbstractCollection this, */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean retainAll(@GuardSatisfied @CanShrink AbstractCollection this, Collection c) { + public boolean retainAll(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink AbstractCollection this, Collection c) { Objects.requireNonNull(c); boolean modified = false; Iterator it = iterator(); @@ -468,7 +475,7 @@ public boolean retainAll(@GuardSatisfied @CanShrink AbstractCollection this, */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink AbstractCollection this) { + public void clear(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink AbstractCollection this) { Iterator it = iterator(); while (it.hasNext()) { it.next(); @@ -490,7 +497,7 @@ public void clear(@GuardSatisfied @CanShrink AbstractCollection this) { * @return a string representation of this collection */ @SideEffectFree - public String toString(@GuardSatisfied AbstractCollection this) { + public String toString(@MaybeModifiable @GuardSatisfied AbstractCollection this) { Iterator it = iterator(); if (! it.hasNext()) return "[]"; diff --git a/src/java.base/share/classes/java/util/AbstractList.java b/src/java.base/share/classes/java/util/AbstractList.java index ef0db082c1ee..f6d173fce222 100644 --- a/src/java.base/share/classes/java/util/AbstractList.java +++ b/src/java.base/share/classes/java/util/AbstractList.java @@ -31,12 +31,20 @@ import org.checkerframework.checker.index.qual.IndexOrHigh; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.signedness.qual.UnknownSignedness; +import org.checkerframework.common.value.qual.StaticallyExecutable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; @@ -134,7 +142,7 @@ protected AbstractList() { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(@GuardSatisfied AbstractList this, E e) { + public boolean add(@IteratorPolyMod @Growable @GuardSatisfied AbstractList this, E e) { add(size(), e); return true; } @@ -145,7 +153,7 @@ public boolean add(@GuardSatisfied AbstractList this, E e) { * @throws IndexOutOfBoundsException {@inheritDoc} */ @Pure - public abstract E get(@GuardSatisfied AbstractList this, @IndexFor({"this"}) int index); + public abstract E get(@MaybeModifiable @GuardSatisfied AbstractList this, @IndexFor({"this"}) int index); /** * {@inheritDoc} @@ -163,7 +171,7 @@ public boolean add(@GuardSatisfied AbstractList this, E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E set(@GuardSatisfied AbstractList this, @IndexFor({"this"}) int index, E element) { + public E set(@IteratorPolyMod @Replaceable @GuardSatisfied AbstractList this, @IndexFor({"this"}) int index, E element) { throw new UnsupportedOperationException(); } @@ -183,7 +191,7 @@ public E set(@GuardSatisfied AbstractList this, @IndexFor({"this"}) int index @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void add(@GuardSatisfied AbstractList this, @IndexOrHigh({"this"}) int index, E element) { + public void add(@IteratorPolyMod @Growable @GuardSatisfied AbstractList this, @IndexOrHigh({"this"}) int index, E element) { throw new UnsupportedOperationException(); } @@ -199,7 +207,7 @@ public void add(@GuardSatisfied AbstractList this, @IndexOrHigh({"this"}) int */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@GuardSatisfied @CanShrink AbstractList this, @IndexFor({"this"}) int index) { + public E remove(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink AbstractList this, @IndexFor({"this"}) int index) { throw new UnsupportedOperationException(); } @@ -218,7 +226,8 @@ public E remove(@GuardSatisfied @CanShrink AbstractList this, @IndexFor({"thi * @throws NullPointerException {@inheritDoc} */ @Pure - public @GTENegativeOne int indexOf(@GuardSatisfied AbstractList this, @GuardSatisfied @UnknownSignedness Object o) { + @StaticallyExecutable + public @GTENegativeOne int indexOf(@MaybeModifiable @GuardSatisfied AbstractList this, @GuardSatisfied @UnknownSignedness Object o) { ListIterator it = listIterator(); if (o==null) { while (it.hasNext()) @@ -245,7 +254,8 @@ public E remove(@GuardSatisfied @CanShrink AbstractList this, @IndexFor({"thi * @throws NullPointerException {@inheritDoc} */ @Pure - public @GTENegativeOne int lastIndexOf(@GuardSatisfied AbstractList this, @GuardSatisfied @UnknownSignedness Object o) { + @StaticallyExecutable + public @GTENegativeOne int lastIndexOf(@MaybeModifiable @GuardSatisfied AbstractList this, @GuardSatisfied @UnknownSignedness Object o) { ListIterator it = listIterator(size()); if (o==null) { while (it.hasPrevious()) @@ -279,7 +289,7 @@ public E remove(@GuardSatisfied @CanShrink AbstractList this, @IndexFor({"thi */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink AbstractList this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink AbstractList this) { removeRange(0, size()); } @@ -305,7 +315,7 @@ public void clear(@GuardSatisfied @CanShrink AbstractList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(@GuardSatisfied AbstractList this, @IndexOrHigh({"this"}) int index, Collection c) { + public boolean addAll(@IteratorPolyMod @Growable @GuardSatisfied AbstractList this, @IndexOrHigh({"this"}) int index, Collection c) { rangeCheckForAdd(index); boolean modified = false; for (E e : c) { @@ -551,7 +561,7 @@ public void add(E e) { * {@code (fromIndex > toIndex)} */ @SideEffectFree - public @PolyGrowShrink List subList(@GuardSatisfied @PolyGrowShrink AbstractList this, @IndexOrHigh({"this"}) int fromIndex, @IndexOrHigh({"this"}) int toIndex) { + public @PolyGrowShrink @PolyModifiable List subList(@PolyModifiable @GuardSatisfied @PolyGrowShrink AbstractList this, @IndexOrHigh({"this"}) int fromIndex, @IndexOrHigh({"this"}) int toIndex) { subListRangeCheck(fromIndex, toIndex, size()); return (this instanceof RandomAccess ? new RandomAccessSubList<>(this, fromIndex, toIndex) : @@ -593,7 +603,7 @@ static void subListRangeCheck(int fromIndex, int toIndex, int size) { * @return {@code true} if the specified object is equal to this list */ @Pure - public boolean equals(@GuardSatisfied AbstractList this, @GuardSatisfied @Nullable Object o) { + public boolean equals(@MaybeModifiable @GuardSatisfied AbstractList this, @GuardSatisfied @Nullable Object o) { if (o == this) return true; if (!(o instanceof List)) @@ -621,7 +631,7 @@ public boolean equals(@GuardSatisfied AbstractList this, @GuardSatisfied @Nul * @return the hash code value for this list */ @Pure - public int hashCode(@GuardSatisfied AbstractList this) { + public int hashCode(@MaybeModifiable @GuardSatisfied AbstractList this) { int hashCode = 1; for (E e : this) hashCode = 31*hashCode + (e==null ? 0 : e.hashCode()); @@ -653,7 +663,7 @@ public int hashCode(@GuardSatisfied AbstractList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - protected void removeRange(@GuardSatisfied @CanShrink AbstractList this, @IndexOrHigh({"this"}) int fromIndex, @IndexOrHigh({"this"}) int toIndex) { + protected void removeRange(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink AbstractList this, @IndexOrHigh({"this"}) int fromIndex, @IndexOrHigh({"this"}) int toIndex) { ListIterator it = listIterator(fromIndex); for (int i=0, n=toIndex-fromIndex; i this) { + public @NonNegative int size(@MaybeModifiable @GuardSatisfied AbstractMap this) { return entrySet().size(); } @@ -118,7 +128,7 @@ protected AbstractMap() { */ @Pure @EnsuresNonEmptyIf(result = false, expression = "this") - public boolean isEmpty(@GuardSatisfied AbstractMap this) { + public boolean isEmpty(@MaybeModifiable @GuardSatisfied AbstractMap this) { return size() == 0; } @@ -136,7 +146,7 @@ public boolean isEmpty(@GuardSatisfied AbstractMap this) { * @throws NullPointerException {@inheritDoc} */ @Pure - public boolean containsValue(@GuardSatisfied AbstractMap this, @GuardSatisfied @UnknownSignedness Object value) { + public boolean containsValue(@MaybeModifiable @GuardSatisfied AbstractMap this, @GuardSatisfied @UnknownSignedness Object value) { Iterator> i = entrySet().iterator(); if (value==null) { while (i.hasNext()) { @@ -170,7 +180,7 @@ public boolean containsValue(@GuardSatisfied AbstractMap this, @GuardSatis */ @Pure @EnsuresKeyForIf(expression={"#1"}, result=true, map={"this"}) - public boolean containsKey(@GuardSatisfied AbstractMap this, @GuardSatisfied @UnknownSignedness Object key) { + public boolean containsKey(@MaybeModifiable @GuardSatisfied AbstractMap this, @GuardSatisfied @UnknownSignedness Object key) { Iterator> i = entrySet().iterator(); if (key==null) { while (i.hasNext()) { @@ -203,7 +213,7 @@ public boolean containsKey(@GuardSatisfied AbstractMap this, @GuardSatisfi * @throws NullPointerException {@inheritDoc} */ @Pure - public @Nullable V get(@GuardSatisfied AbstractMap this, @UnknownSignedness @GuardSatisfied Object key) { + public @Nullable V get(@MaybeModifiable @GuardSatisfied AbstractMap this, @UnknownSignedness @GuardSatisfied Object key) { Iterator> i = entrySet().iterator(); if (key==null) { while (i.hasNext()) { @@ -240,7 +250,7 @@ public boolean containsKey(@GuardSatisfied AbstractMap this, @GuardSatisfi @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(@GuardSatisfied AbstractMap this, K key, V value) { + public @Nullable V put(@Growable @Replaceable @GuardSatisfied AbstractMap this, K key, V value) { throw new UnsupportedOperationException(); } @@ -268,7 +278,7 @@ public boolean containsKey(@GuardSatisfied AbstractMap this, @GuardSatisfi */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V remove(@GuardSatisfied AbstractMap this, @GuardSatisfied @UnknownSignedness Object key) { + public @Nullable V remove(@Shrinkable @GuardSatisfied AbstractMap this, @GuardSatisfied @UnknownSignedness Object key) { Iterator> i = entrySet().iterator(); Entry correctEntry = null; if (key==null) { @@ -315,7 +325,7 @@ public boolean containsKey(@GuardSatisfied AbstractMap this, @GuardSatisfi */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putAll(@GuardSatisfied AbstractMap this, Map m) { + public void putAll(@Growable @Replaceable @GuardSatisfied AbstractMap this, Map m) { for (Map.Entry e : m.entrySet()) put(e.getKey(), e.getValue()); } @@ -334,7 +344,7 @@ public void putAll(@GuardSatisfied AbstractMap this, Map this) { + public void clear(@Shrinkable @GuardSatisfied AbstractMap this) { entrySet().clear(); } @@ -385,7 +395,7 @@ public void clear(@GuardSatisfied AbstractMap this) { * method will not all return the same set. */ @SideEffectFree - public Set<@KeyFor({"this"}) K> keySet(@GuardSatisfied AbstractMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@KeyFor({"this"}) K> keySet(@PolyShrinkable @GuardSatisfied AbstractMap this) { Set ks = keySet; if (ks == null) { ks = new AbstractSet() { @@ -459,7 +469,7 @@ public boolean contains(@UnknownSignedness Object k) { * method will not all return the same collection. */ @SideEffectFree - public Collection values(@GuardSatisfied AbstractMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable @GuardSatisfied AbstractMap this) { Collection vals = values; if (vals == null) { vals = new AbstractCollection() { @@ -517,7 +527,7 @@ public boolean contains(@UnknownSignedness Object v) { } @SideEffectFree - public abstract Set> entrySet(@GuardSatisfied AbstractMap this); + public abstract @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@PolyModifiable Entry<@KeyFor({"this"}) K,V>> entrySet(@PolyModifiable @GuardSatisfied AbstractMap this); // Comparison and hashing @@ -545,7 +555,7 @@ public boolean contains(@UnknownSignedness Object v) { * @return {@code true} if the specified object is equal to this map */ @Pure - public boolean equals(@GuardSatisfied AbstractMap this, @GuardSatisfied @Nullable Object o) { + public boolean equals(@MaybeModifiable @GuardSatisfied AbstractMap this, @GuardSatisfied @Nullable Object o) { if (o == this) return true; @@ -592,7 +602,7 @@ public boolean equals(@GuardSatisfied AbstractMap this, @GuardSatisfied @N * @see Set#equals(Object) */ @Pure - public int hashCode(@GuardSatisfied AbstractMap this) { + public int hashCode(@MaybeModifiable @GuardSatisfied AbstractMap this) { int h = 0; for (Entry entry : entrySet()) h += entry.hashCode(); @@ -612,7 +622,7 @@ public int hashCode(@GuardSatisfied AbstractMap this) { * @return a string representation of this map */ @SideEffectFree - public String toString(@GuardSatisfied AbstractMap this) { + public String toString(@MaybeModifiable @GuardSatisfied AbstractMap this) { Iterator> i = entrySet().iterator(); if (! i.hasNext()) return "{}"; @@ -681,7 +691,7 @@ private static boolean eq(Object o1, Object o2) { * * @since 1.6 */ - public static class SimpleEntry + public static @Modifiable class SimpleEntry implements Entry, java.io.Serializable { @java.io.Serial @@ -834,7 +844,7 @@ public String toString(AbstractMap.@GuardSatisfied SimpleEntry this) { * @param the type of the value * @since 1.6 */ - public static class SimpleImmutableEntry + public static @Unmodifiable class SimpleImmutableEntry implements Entry, java.io.Serializable { @java.io.Serial diff --git a/src/java.base/share/classes/java/util/AbstractQueue.java b/src/java.base/share/classes/java/util/AbstractQueue.java index 7d5dc34b6f80..ca5406a354de 100644 --- a/src/java.base/share/classes/java/util/AbstractQueue.java +++ b/src/java.base/share/classes/java/util/AbstractQueue.java @@ -37,9 +37,13 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.dataflow.qual.Pure; +import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; import org.checkerframework.framework.qual.AnnotatedFor; import org.checkerframework.framework.qual.DoesNotUnrefineReceiver; @@ -70,7 +74,7 @@ * @author Doug Lea * @param the type of elements held in this queue */ -@AnnotatedFor({"lock", "nullness"}) +@AnnotatedFor({"lock", "nullness", "modifiability"}) public abstract class AbstractQueue extends AbstractCollection implements Queue { @@ -78,7 +82,7 @@ public abstract class AbstractQueue /** * Constructor for use by subclasses. */ - protected AbstractQueue() { + protected @Modifiable AbstractQueue() { } /** @@ -104,7 +108,7 @@ protected AbstractQueue() { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(@GuardSatisfied AbstractQueue this, E e) { + public boolean add(@Growable @GuardSatisfied AbstractQueue this, E e) { if (offer(e)) return true; else @@ -124,7 +128,7 @@ public boolean add(@GuardSatisfied AbstractQueue this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@GuardSatisfied @NonEmpty @CanShrink AbstractQueue this) { + public E remove(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink AbstractQueue this) { E x = poll(); if (x != null) return x; @@ -161,7 +165,7 @@ public E element(@GuardSatisfied @NonEmpty AbstractQueue this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink AbstractQueue this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink AbstractQueue this) { while (poll() != null) ; } @@ -197,7 +201,7 @@ public void clear(@GuardSatisfied @CanShrink AbstractQueue this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(@GuardSatisfied AbstractQueue this, Collection c) { + public boolean addAll(@Growable @GuardSatisfied AbstractQueue this, Collection c) { if (c == null) throw new NullPointerException(); if (c == this) diff --git a/src/java.base/share/classes/java/util/AbstractSequentialList.java b/src/java.base/share/classes/java/util/AbstractSequentialList.java index 37f37de20bd3..b19b4af23a9f 100644 --- a/src/java.base/share/classes/java/util/AbstractSequentialList.java +++ b/src/java.base/share/classes/java/util/AbstractSequentialList.java @@ -28,6 +28,13 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; import org.checkerframework.dataflow.qual.Pure; @@ -100,7 +107,7 @@ protected AbstractSequentialList() { * @throws IndexOutOfBoundsException {@inheritDoc} */ @Pure - public E get(@GuardSatisfied AbstractSequentialList this, int index) { + public E get(@MaybeModifiable @GuardSatisfied AbstractSequentialList this, int index) { try { return listIterator(index).next(); } catch (NoSuchElementException exc) { @@ -130,7 +137,7 @@ public E get(@GuardSatisfied AbstractSequentialList this, int index) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E set(@GuardSatisfied AbstractSequentialList this, int index, E element) { + public E set(@IteratorPolyMod @Replaceable @GuardSatisfied AbstractSequentialList this, int index, E element) { try { ListIterator e = listIterator(index); E oldVal = e.next(); @@ -164,7 +171,7 @@ public E set(@GuardSatisfied AbstractSequentialList this, int index, E elemen @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void add(@GuardSatisfied AbstractSequentialList this, int index, E element) { + public void add(@IteratorPolyMod @Growable @GuardSatisfied AbstractSequentialList this, int index, E element) { try { listIterator(index).add(element); } catch (NoSuchElementException exc) { @@ -191,7 +198,7 @@ public void add(@GuardSatisfied AbstractSequentialList this, int index, E ele */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@GuardSatisfied @CanShrink AbstractSequentialList this, int index) { + public E remove(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink AbstractSequentialList this, int index) { try { ListIterator e = listIterator(index); E outCast = e.next(); @@ -236,7 +243,7 @@ public E remove(@GuardSatisfied @CanShrink AbstractSequentialList this, int i */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(@GuardSatisfied AbstractSequentialList this, int index, Collection c) { + public boolean addAll(@IteratorPolyMod @Growable @GuardSatisfied AbstractSequentialList this, int index, Collection c) { try { boolean modified = false; ListIterator e1 = listIterator(index); diff --git a/src/java.base/share/classes/java/util/AbstractSet.java b/src/java.base/share/classes/java/util/AbstractSet.java index e14b04cdc4d2..4f6a477fd927 100644 --- a/src/java.base/share/classes/java/util/AbstractSet.java +++ b/src/java.base/share/classes/java/util/AbstractSet.java @@ -26,6 +26,9 @@ package java.util; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.signedness.qual.UnknownSignedness; import org.checkerframework.dataflow.qual.Pure; @@ -94,7 +97,7 @@ protected AbstractSet() { * @return {@code true} if the specified object is equal to this set */ @Pure - public boolean equals(@GuardSatisfied AbstractSet this, @GuardSatisfied @Nullable Object o) { + public boolean equals(@MaybeModifiable @GuardSatisfied AbstractSet this, @GuardSatisfied @Nullable Object o) { if (o == this) return true; @@ -128,7 +131,7 @@ public boolean equals(@GuardSatisfied AbstractSet this, @GuardSatisfied @Null * @see Set#equals(Object) */ @Pure - public int hashCode(@GuardSatisfied AbstractSet this) { + public int hashCode(@MaybeModifiable @GuardSatisfied AbstractSet this) { int h = 0; Iterator i = iterator(); while (i.hasNext()) { @@ -178,7 +181,7 @@ public int hashCode(@GuardSatisfied AbstractSet this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeAll(@GuardSatisfied AbstractSet this, Collection c) { + public boolean removeAll(@IteratorPolyMod @Shrinkable @GuardSatisfied AbstractSet this, Collection c) { Objects.requireNonNull(c); boolean modified = false; diff --git a/src/java.base/share/classes/java/util/ArrayDeque.java b/src/java.base/share/classes/java/util/ArrayDeque.java index b531c6a65a5d..797384e7fb49 100644 --- a/src/java.base/share/classes/java/util/ArrayDeque.java +++ b/src/java.base/share/classes/java/util/ArrayDeque.java @@ -38,6 +38,12 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -105,7 +111,7 @@ * @param the type of elements held in this deque * @since 1.6 */ -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public class ArrayDeque extends AbstractCollection implements Deque, Cloneable, Serializable { @@ -198,7 +204,7 @@ private int newCapacity(int needed, int jump) { * Constructs an empty array deque with an initial capacity * sufficient to hold 16 elements. */ - public ArrayDeque() { + public @Modifiable @IteratorPolyMod ArrayDeque() { elements = new Object[16 + 1]; } @@ -208,7 +214,7 @@ public ArrayDeque() { * * @param numElements lower bound on initial capacity of the deque */ - public ArrayDeque(@NonNegative int numElements) { + public @Modifiable @IteratorPolyMod ArrayDeque(@NonNegative int numElements) { elements = new Object[(numElements < 1) ? 1 : (numElements == Integer.MAX_VALUE) ? Integer.MAX_VALUE : @@ -225,7 +231,7 @@ public ArrayDeque(@NonNegative int numElements) { * @param c the collection whose elements are to be placed into the deque * @throws NullPointerException if the specified collection is null */ - public @PolyNonEmpty ArrayDeque(@PolyNonEmpty Collection c) { + public @Modifiable @IteratorPolyMod @PolyNonEmpty ArrayDeque(@PolyNonEmpty Collection c) { this(c.size()); copyElements(c); } @@ -305,7 +311,7 @@ static final E nonNullElementAt(@PolyNull @PolySigned Object[] es, int i) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(@GuardSatisfied ArrayDeque this, E e) { + public void addFirst(@SeqGrowable @GuardSatisfied ArrayDeque this, E e) { if (e == null) throw new NullPointerException(); final Object[] es = elements; @@ -325,7 +331,7 @@ public void addFirst(@GuardSatisfied ArrayDeque this, E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(@GuardSatisfied ArrayDeque this, E e) { + public void addLast(@SeqGrowable @GuardSatisfied ArrayDeque this, E e) { if (e == null) throw new NullPointerException(); final Object[] es = elements; @@ -346,7 +352,7 @@ public void addLast(@GuardSatisfied ArrayDeque this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(Collection c) { + public boolean addAll(@Growable ArrayDeque this, Collection c) { final int s, needed; if ((needed = (s = size()) + c.size() + 1 - elements.length) > 0) grow(needed); @@ -367,7 +373,7 @@ private void copyElements(Collection c) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerFirst(E e) { + public boolean offerFirst(@SeqGrowable ArrayDeque this, E e) { addFirst(e); return true; } @@ -381,7 +387,7 @@ public boolean offerFirst(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerLast(E e) { + public boolean offerLast(@SeqGrowable ArrayDeque this, E e) { addLast(e); return true; } @@ -391,7 +397,7 @@ public boolean offerLast(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeFirst(@GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { + public E removeFirst(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { E e = pollFirst(); if (e == null) throw new NoSuchElementException(); @@ -403,7 +409,7 @@ public E removeFirst(@GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeLast(@GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { + public E removeLast(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { E e = pollLast(); if (e == null) throw new NoSuchElementException(); @@ -412,7 +418,7 @@ public E removeLast(@GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollFirst(@GuardSatisfied @CanShrink ArrayDeque this) { + public @Nullable E pollFirst(@Shrinkable @GuardSatisfied @CanShrink ArrayDeque this) { final Object[] es; final int h; E e = elementAt(es = elements, h = head); @@ -425,7 +431,7 @@ public E removeLast(@GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollLast(@GuardSatisfied @CanShrink ArrayDeque this) { + public @Nullable E pollLast(@Shrinkable @GuardSatisfied @CanShrink ArrayDeque this) { final Object[] es; final int t; E e = elementAt(es = elements, t = dec(tail, es.length)); @@ -484,7 +490,7 @@ public E getLast(@GuardSatisfied @NonEmpty ArrayDeque this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeFirstOccurrence(@GuardSatisfied @CanShrink ArrayDeque this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean removeFirstOccurrence(@Shrinkable @GuardSatisfied @CanShrink ArrayDeque this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { if (o != null) { final Object[] es = elements; for (int i = head, end = tail, to = (i <= end) ? end : es.length; @@ -514,7 +520,7 @@ public boolean removeFirstOccurrence(@GuardSatisfied @CanShrink ArrayDeque th */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeLastOccurrence(@GuardSatisfied @CanShrink ArrayDeque this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean removeLastOccurrence(@Shrinkable @GuardSatisfied @CanShrink ArrayDeque this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { if (o != null) { final Object[] es = elements; for (int i = tail, end = head, to = (i >= end) ? end : 0; @@ -544,7 +550,7 @@ public boolean removeLastOccurrence(@GuardSatisfied @CanShrink ArrayDeque thi @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(@GuardSatisfied ArrayDeque this, E e) { + public boolean add(@Growable @GuardSatisfied ArrayDeque this, E e) { addLast(e); return true; } @@ -560,7 +566,7 @@ public boolean add(@GuardSatisfied ArrayDeque this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(@GuardSatisfied ArrayDeque this, E e) { + public boolean offer(@Growable @GuardSatisfied ArrayDeque this, E e) { return offerLast(e); } @@ -577,7 +583,7 @@ public boolean offer(@GuardSatisfied ArrayDeque this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { + public E remove(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { return removeFirst(); } @@ -593,7 +599,7 @@ public E remove(@GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink ArrayDeque this) { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink ArrayDeque this) { return pollFirst(); } @@ -639,7 +645,7 @@ public E element(@GuardSatisfied @NonEmpty ArrayDeque this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void push(@GuardSatisfied ArrayDeque this, E e) { + public void push(@SeqGrowable @GuardSatisfied ArrayDeque this, E e) { addFirst(e); } @@ -655,7 +661,7 @@ public void push(@GuardSatisfied ArrayDeque this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E pop(@GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { + public E pop(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink ArrayDeque this) { return removeFirst(); } @@ -737,12 +743,12 @@ public boolean isEmpty(@GuardSatisfied ArrayDeque this) { * @return an iterator over the elements in this deque */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty ArrayDeque this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ArrayDeque this) { return new DeqIterator(); } @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyNonEmpty ArrayDeque this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ArrayDeque this) { return new DescendingIterator(); } @@ -979,7 +985,7 @@ public void forEach(Consumer action) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink ArrayDeque this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink ArrayDeque this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -999,7 +1005,7 @@ public boolean removeAll(@CanShrink ArrayDeque this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink ArrayDeque this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } @@ -1117,7 +1123,7 @@ public boolean contains(@GuardSatisfied ArrayDeque this, @GuardSatisfied @Nul */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @CanShrink ArrayDeque this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @GuardSatisfied @CanShrink ArrayDeque this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return removeFirstOccurrence(o); } @@ -1127,7 +1133,7 @@ public boolean remove(@GuardSatisfied @CanShrink ArrayDeque this, @GuardSatis */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink ArrayDeque this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink ArrayDeque this) { circularClear(elements, head, tail); head = tail = 0; } @@ -1241,7 +1247,7 @@ private T[] toArray(Class klazz) { * @return a copy of this deque */ @SideEffectFree - public ArrayDeque clone(@GuardSatisfied ArrayDeque this) { + public @Modifiable @IteratorPolyMod ArrayDeque clone(@GuardSatisfied ArrayDeque this) { try { @SuppressWarnings("unchecked") ArrayDeque result = (ArrayDeque) super.clone(); diff --git a/src/java.base/share/classes/java/util/ArrayList.java b/src/java.base/share/classes/java/util/ArrayList.java index 778066387da3..9e02fadd00a7 100644 --- a/src/java.base/share/classes/java/util/ArrayList.java +++ b/src/java.base/share/classes/java/util/ArrayList.java @@ -30,6 +30,13 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -40,6 +47,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; import org.checkerframework.checker.signedness.qual.PolySigned; import org.checkerframework.checker.signedness.qual.UnknownSignedness; +import org.checkerframework.common.value.qual.StaticallyExecutable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; @@ -129,7 +137,7 @@ * @since 1.2 */ @CFComment("lock/nullness: Permit null elements") -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable { @@ -176,7 +184,7 @@ public class ArrayList extends AbstractList * is negative */ @SideEffectFree - public ArrayList(@NonNegative int initialCapacity) { + public @Modifiable @IteratorPolyMod ArrayList(@NonNegative int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { @@ -191,7 +199,7 @@ public ArrayList(@NonNegative int initialCapacity) { * Constructs an empty list with an initial capacity of ten. */ @SideEffectFree - public ArrayList() { + public @Modifiable @IteratorPolyMod ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } @@ -204,7 +212,7 @@ public ArrayList() { * @throws NullPointerException if the specified collection is null */ @SideEffectFree - public @PolyNonEmpty ArrayList(@PolyNonEmpty Collection c) { + public @Modifiable @IteratorPolyMod @PolyNonEmpty ArrayList(@PolyNonEmpty Collection c) { Object[] a = c.toArray(); if ((size = a.length) != 0) { if (c.getClass() == ArrayList.class) { @@ -223,6 +231,7 @@ public ArrayList() { * list's current size. An application can use this operation to minimize * the storage of an {@code ArrayList} instance. */ + @SideEffectFree public void trimToSize(@GuardSatisfied ArrayList this) { modCount++; if (size < elementData.length) { @@ -239,6 +248,7 @@ public void trimToSize(@GuardSatisfied ArrayList this) { * * @param minCapacity the desired minimum capacity */ + @SideEffectFree public void ensureCapacity(@GuardSatisfied ArrayList this, int minCapacity) { if (minCapacity > elementData.length && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA @@ -315,6 +325,7 @@ public boolean contains(@GuardSatisfied ArrayList this, @GuardSatisfied @Null * or -1 if there is no such index. */ @Pure + @StaticallyExecutable public @GTENegativeOne int indexOf(@GuardSatisfied ArrayList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return indexOfRange(o, 0, size); } @@ -345,6 +356,7 @@ int indexOfRange(@GuardSatisfied @Nullable @UnknownSignedness Object o, int star * or -1 if there is no such index. */ @Pure + @StaticallyExecutable public @GTENegativeOne int lastIndexOf(@GuardSatisfied ArrayList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return lastIndexOfRange(o, 0, size); } @@ -374,7 +386,7 @@ int lastIndexOfRange(@GuardSatisfied @Nullable @UnknownSignedness Object o, int * @return a clone of this {@code ArrayList} instance */ @SideEffectFree - public Object clone(@GuardSatisfied ArrayList this) { + public @Modifiable @IteratorPolyMod Object clone(@GuardSatisfied ArrayList this) { try { ArrayList v = (ArrayList) super.clone(); v.elementData = Arrays.copyOf(elementData, size); @@ -511,7 +523,7 @@ public E getLast() { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E set(@GuardSatisfied ArrayList this, @NonNegative int index, E element) { + public E set(@Replaceable @GuardSatisfied ArrayList this, @NonNegative int index, E element) { Objects.checkIndex(index, size); E oldValue = elementData(index); elementData[index] = element; @@ -541,7 +553,7 @@ private void add(E e, Object[] elementData, int s) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(@GuardSatisfied ArrayList this, E e) { + public boolean add(@Growable @GuardSatisfied ArrayList this, E e) { modCount++; add(e, elementData, size); return true; @@ -559,7 +571,7 @@ public boolean add(@GuardSatisfied ArrayList this, E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void add(@GuardSatisfied ArrayList this, @NonNegative int index, E element) { + public void add(@Growable @GuardSatisfied ArrayList this, @NonNegative int index, E element) { rangeCheckForAdd(index); modCount++; final int s; @@ -581,7 +593,7 @@ public void add(@GuardSatisfied ArrayList this, @NonNegative int index, E ele @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E element) { + public void addFirst(@SeqGrowable ArrayList this, E element) { add(0, element); } @@ -593,7 +605,7 @@ public void addFirst(E element) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E element) { + public void addLast(@SeqGrowable ArrayList this, E element) { add(element); } @@ -608,7 +620,7 @@ public void addLast(E element) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@GuardSatisfied @CanShrink ArrayList this, @NonNegative int index) { + public E remove(@Shrinkable @GuardSatisfied @CanShrink ArrayList this, @NonNegative int index) { Objects.checkIndex(index, size); final Object[] es = elementData; @@ -626,7 +638,7 @@ public E remove(@GuardSatisfied @CanShrink ArrayList this, @NonNegative int i */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeFirst() { + public E removeFirst(@Shrinkable ArrayList this) { if (size == 0) { throw new NoSuchElementException(); } else { @@ -645,7 +657,7 @@ public E removeFirst() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeLast() { + public E removeLast(@Shrinkable ArrayList this) { int last = size - 1; if (last < 0) { throw new NoSuchElementException(); @@ -734,6 +746,7 @@ public int hashCode() { return hash; } + @Pure int hashCodeRange(int from, int to) { final Object[] es = elementData; if (to > es.length) { @@ -762,7 +775,7 @@ int hashCodeRange(int from, int to) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @CanShrink ArrayList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @GuardSatisfied @CanShrink ArrayList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { final Object[] es = elementData; final int size = this.size; int i = 0; @@ -800,7 +813,7 @@ private void fastRemove(Object[] es, int i) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink ArrayList this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink ArrayList this) { modCount++; final Object[] es = elementData; for (int to = size, i = size = 0; i < to; i++) @@ -822,7 +835,7 @@ public void clear(@GuardSatisfied @CanShrink ArrayList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(@GuardSatisfied ArrayList this, Collection c) { + public boolean addAll(@Growable @GuardSatisfied ArrayList this, Collection c) { Object[] a = c.toArray(); modCount++; int numNew = a.length; @@ -854,7 +867,7 @@ public boolean addAll(@GuardSatisfied ArrayList this, Collection */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(@GuardSatisfied ArrayList this, @NonNegative int index, Collection c) { + public boolean addAll(@Growable @GuardSatisfied ArrayList this, @NonNegative int index, Collection c) { rangeCheckForAdd(index); Object[] a = c.toArray(); @@ -890,7 +903,7 @@ public boolean addAll(@GuardSatisfied ArrayList this, @NonNegative int index, * toIndex > size() || * toIndex < fromIndex}) */ - protected void removeRange(@GuardSatisfied @CanShrink ArrayList this, int fromIndex, int toIndex) { + protected void removeRange(@Shrinkable @GuardSatisfied @CanShrink ArrayList this, int fromIndex, int toIndex) { if (fromIndex > toIndex) { throw new IndexOutOfBoundsException( outOfBoundsMsg(fromIndex, toIndex)); @@ -947,7 +960,7 @@ private static String outOfBoundsMsg(int fromIndex, int toIndex) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeAll(@CanShrink ArrayList this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink ArrayList this, Collection c) { return batchRemove(c, false, 0, size); } @@ -969,7 +982,7 @@ public boolean removeAll(@CanShrink ArrayList this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink ArrayList this, Collection c) { return batchRemove(c, true, 0, size); } @@ -1081,7 +1094,7 @@ private void readObject(java.io.ObjectInputStream s) * * @throws IndexOutOfBoundsException {@inheritDoc} */ - public @PolyGrowShrink ListIterator listIterator(@PolyGrowShrink ArrayList this, @NonNegative int index) { + public @PolyGrowShrink @PolyModifiable ListIterator listIterator(@PolyGrowShrink @PolyModifiable ArrayList this, @NonNegative int index) { rangeCheckForAdd(index); return new ListItr(index); } @@ -1094,7 +1107,7 @@ private void readObject(java.io.ObjectInputStream s) * * @see #listIterator(int) */ - public @PolyGrowShrink @PolyNonEmpty ListIterator listIterator(@PolyGrowShrink @PolyNonEmpty ArrayList this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty ListIterator listIterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ArrayList this) { return new ListItr(0); } @@ -1106,7 +1119,7 @@ private void readObject(java.io.ObjectInputStream s) * @return an iterator over the elements in this list in proper sequence */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty ArrayList this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ArrayList this) { return new Itr(); } @@ -1198,15 +1211,11 @@ public boolean hasPrevious() { return cursor != 0; } - @SideEffectsOnly("this") - @DoesNotUnrefineReceiver("modifiability") @Pure public int nextIndex() { return cursor; } - @SideEffectsOnly("this") - @DoesNotUnrefineReceiver("modifiability") @Pure public int previousIndex() { return cursor - 1; @@ -1288,10 +1297,8 @@ public void add(E e) { * @throws IndexOutOfBoundsException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} */ - @SideEffectsOnly("this") - @DoesNotUnrefineReceiver("modifiability") @SideEffectFree - public @PolyGrowShrink List subList(@GuardSatisfied @PolyGrowShrink ArrayList this, @NonNegative int fromIndex, @NonNegative int toIndex) { + public @PolyGrowShrink @PolyModifiable List subList(@PolyModifiable @GuardSatisfied @PolyGrowShrink ArrayList this, @NonNegative int fromIndex, @NonNegative int toIndex) { subListRangeCheck(fromIndex, toIndex, size); return new SubList<>(this, fromIndex, toIndex); } @@ -1474,6 +1481,7 @@ public int hashCode() { } @Pure + @StaticallyExecutable public int indexOf(@GuardSatisfied @Nullable @UnknownSignedness Object o) { int index = root.indexOfRange(o, offset, offset + size); checkForComodification(); @@ -1481,6 +1489,7 @@ public int indexOf(@GuardSatisfied @Nullable @UnknownSignedness Object o) { } @Pure + @StaticallyExecutable public int lastIndexOf(@GuardSatisfied @Nullable @UnknownSignedness Object o) { int index = root.lastIndexOfRange(o, offset, offset + size); checkForComodification(); @@ -1886,7 +1895,7 @@ private static boolean isClear(long[] bits, int i) { @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink ArrayList this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink ArrayList this, Predicate filter) { return removeIf(filter, 0, size); } @@ -1894,7 +1903,7 @@ public boolean removeIf(@CanShrink ArrayList this, Predicate filte * Removes all elements satisfying the given predicate, from index * i (inclusive) to index end (exclusive). */ - boolean removeIf(@CanShrink ArrayList this, Predicate filter, int i, final int end) { + boolean removeIf(@Shrinkable @CanShrink ArrayList this, Predicate filter, int i, final int end) { Objects.requireNonNull(filter); int expectedModCount = modCount; final Object[] es = elementData; @@ -1930,13 +1939,13 @@ boolean removeIf(@CanShrink ArrayList this, Predicate filter, int @SuppressWarnings({"unchecked"}) @Override @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(UnaryOperator operator) { + public void replaceAll(@Replaceable ArrayList this, UnaryOperator operator) { replaceAllRange(operator, 0, size); // TODO(8203662): remove increment of modCount from ... modCount++; } - private void replaceAllRange(UnaryOperator operator, int i, int end) { + private void replaceAllRange(@Replaceable ArrayList this, UnaryOperator operator, int i, int end) { Objects.requireNonNull(operator); final int expectedModCount = modCount; final Object[] es = elementData; @@ -1950,7 +1959,7 @@ private void replaceAllRange(UnaryOperator operator, int i, int end) { @SuppressWarnings("unchecked") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void sort(Comparator c) { + public void sort(@Replaceable ArrayList this, Comparator c) { final int expectedModCount = modCount; Arrays.sort((E[]) elementData, 0, size, c); if (modCount != expectedModCount) diff --git a/src/java.base/share/classes/java/util/Arrays.java b/src/java.base/share/classes/java/util/Arrays.java index 182019021720..f7956d45deab 100644 --- a/src/java.base/share/classes/java/util/Arrays.java +++ b/src/java.base/share/classes/java/util/Arrays.java @@ -31,6 +31,8 @@ import org.checkerframework.checker.index.qual.SearchIndexFor; import org.checkerframework.checker.interning.qual.PolyInterned; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; import org.checkerframework.checker.mustcall.qual.MustCallUnknown; import org.checkerframework.checker.mustcall.qual.PolyMustCall; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; @@ -42,6 +44,7 @@ import org.checkerframework.checker.signedness.qual.UnknownSignedness; import org.checkerframework.checker.signedness.qual.Unsigned; import org.checkerframework.common.value.qual.MinLen; +import org.checkerframework.common.value.qual.StaticallyExecutable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; @@ -3251,7 +3254,7 @@ public static boolean equals(T[] a, int aFromIndex, int aToIndex, * @param a the array to be filled * @param val the value to be stored in all elements of the array */ - @Pure + @SideEffectsOnly("#1") public static void fill(@PolySigned long[] a, @PolySigned long val) { for (int i = 0, len = a.length; i < len; i++) a[i] = val; @@ -4366,7 +4369,7 @@ private static boolean[] copyOfRangeBoolean(boolean[] original, int from, int to @SafeVarargs @SideEffectFree @SuppressWarnings("varargs") - public static @PolyNonEmpty List asList(T @PolyNonEmpty... a) { + public static @Replaceable @IteratorPolyMod @PolyNonEmpty List asList(T @PolyNonEmpty... a) { return new ArrayList<>(a); } @@ -4424,6 +4427,8 @@ public E set(int index, E element) { } @Override + @Pure + @StaticallyExecutable public int indexOf(Object o) { E[] a = this.a; if (o == null) { diff --git a/src/java.base/share/classes/java/util/Collection.java b/src/java.base/share/classes/java/util/Collection.java index f145fcab3f7c..52ea21d3941c 100644 --- a/src/java.base/share/classes/java/util/Collection.java +++ b/src/java.base/share/classes/java/util/Collection.java @@ -29,6 +29,13 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -275,7 +282,7 @@ */ @CFComment("lock/nullness: Subclasses of this interface/class may opt to prohibit null elements") -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public interface Collection extends Iterable { // Query Operations @@ -287,7 +294,7 @@ public interface Collection extends Iterable { * @return the number of elements in this collection */ @Pure - @NonNegative int size(@GuardSatisfied Collection this); + @NonNegative int size(@MaybeModifiable @GuardSatisfied Collection this); /** * Returns {@code true} if this collection contains no elements. @@ -296,7 +303,7 @@ public interface Collection extends Iterable { */ @Pure @EnsuresNonEmptyIf(result = false, expression = "this") - boolean isEmpty(@GuardSatisfied Collection this); + boolean isEmpty(@MaybeModifiable @GuardSatisfied Collection this); /** * Returns {@code true} if this collection contains the specified element. @@ -319,7 +326,7 @@ public interface Collection extends Iterable { "(though I think a nicer specification would be to return false in that case)"}) @Pure @EnsuresNonEmptyIf(result = true, expression = "this") - boolean contains(@GuardSatisfied Collection this, @GuardSatisfied @UnknownSignedness Object o); + boolean contains(@MaybeModifiable @GuardSatisfied Collection this, @GuardSatisfied @UnknownSignedness Object o); /** * Returns an iterator over the elements in this collection. There are no @@ -330,7 +337,7 @@ public interface Collection extends Iterable { * @return an {@code Iterator} over the elements in this collection */ @SideEffectFree - @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty Collection this); + @MaybeModifiable @PolyGrowShrink @PolyNonEmpty Iterator iterator(@MaybeModifiable @PolyGrowShrink @PolyNonEmpty Collection this); /** * Returns an array containing all of the elements in this collection. @@ -359,7 +366,7 @@ public interface Collection extends Iterable { "methods, because the most useful type for toArray is not expressible", "in the surface syntax that the nullness annotations support."}) @SideEffectFree - @PolyNull @PolySigned Object[] toArray(Collection<@PolyNull @PolySigned E> this); + @PolyNull @PolySigned Object[] toArray(@MaybeModifiable Collection<@PolyNull @PolySigned E> this); /** * Returns an array containing all of the elements in this collection; @@ -412,7 +419,7 @@ public interface Collection extends Iterable { * runtime component type} of the specified array * @throws NullPointerException if the specified array is null */ - @Nullable T [] toArray(@PolyNull T[] a); + @Nullable T[] toArray(@MaybeModifiable Collection this, @PolyNull T[] a); /** * Returns an array containing all of the elements in this collection, @@ -449,7 +456,8 @@ public interface Collection extends Iterable { * @throws NullPointerException if the generator function is null * @since 11 */ - default T[] toArray(IntFunction generator) { + @SideEffectFree + default T[] toArray(@MaybeModifiable Collection this, IntFunction generator) { return toArray(generator.apply(0)); } @@ -491,8 +499,7 @@ default T[] toArray(IntFunction generator) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean add(@GuardSatisfied Collection this, E e); - + boolean add(@IteratorPolyMod @Growable @GuardSatisfied Collection this, E e); /** * Removes a single instance of the specified element from this * collection, if it is present (optional operation). More formally, @@ -515,8 +522,7 @@ default T[] toArray(IntFunction generator) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean remove(@GuardSatisfied @CanShrink Collection this, @UnknownSignedness Object o); - + boolean remove(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink Collection this, @UnknownSignedness Object o); // Bulk Operations @@ -539,7 +545,7 @@ default T[] toArray(IntFunction generator) { * @see #contains(Object) */ @Pure - boolean containsAll(@GuardSatisfied Collection this, @GuardSatisfied Collection c); + boolean containsAll(@MaybeModifiable @GuardSatisfied Collection this, @GuardSatisfied Collection c); /** * Adds all of the elements in the specified collection to this collection @@ -569,8 +575,7 @@ default T[] toArray(IntFunction generator) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean addAll(@GuardSatisfied Collection this, Collection c); - + boolean addAll(@Growable @GuardSatisfied Collection this, Collection c); /** * Removes all of this collection's elements that are also contained in the * specified collection (optional operation). After this call returns, @@ -596,8 +601,7 @@ default T[] toArray(IntFunction generator) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean removeAll(@GuardSatisfied @CanShrink Collection this, Collection c); - + boolean removeAll(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink Collection this, Collection c); /** * Removes all of the elements of this collection that satisfy the given * predicate. Errors or runtime exceptions thrown during iteration or by @@ -622,7 +626,7 @@ default T[] toArray(IntFunction generator) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default boolean removeIf(@CanShrink Collection this, Predicate filter) { + default boolean removeIf(@IteratorPolyMod @Shrinkable @CanShrink Collection this, Predicate filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator each = iterator(); @@ -659,8 +663,7 @@ default boolean removeIf(@CanShrink Collection this, Predicate fil */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean retainAll(@GuardSatisfied @CanShrink Collection this, Collection c); - + boolean retainAll(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink Collection this, Collection c); /** * Removes all of the elements from this collection (optional operation). * The collection will be empty after this method returns. @@ -670,8 +673,7 @@ default boolean removeIf(@CanShrink Collection this, Predicate fil */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void clear(@GuardSatisfied @CanShrink Collection this); - + void clear(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink Collection this); // Comparison and hashing @@ -709,7 +711,7 @@ default boolean removeIf(@CanShrink Collection this, Predicate fil * @see List#equals(Object) */ @Pure - boolean equals(@GuardSatisfied Collection this, @GuardSatisfied @Nullable @UnknownSignedness Object o); + boolean equals(@MaybeModifiable @GuardSatisfied Collection this, @GuardSatisfied @Nullable @UnknownSignedness Object o); /** * Returns the hash code value for this collection. While the @@ -727,7 +729,7 @@ default boolean removeIf(@CanShrink Collection this, Predicate fil * @see Object#equals(Object) */ @Pure - int hashCode(@GuardSatisfied Collection this); + int hashCode(@MaybeModifiable @GuardSatisfied Collection this); /** * Creates a {@link Spliterator} over the elements in this collection. @@ -781,7 +783,7 @@ default boolean removeIf(@CanShrink Collection this, Predicate fil */ @SideEffectFree @Override - default Spliterator spliterator() { + default Spliterator spliterator(@MaybeModifiable Collection this) { return Spliterators.spliterator(this, 0); } @@ -801,7 +803,7 @@ default Spliterator spliterator() { * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - default @PolyNonEmpty Stream stream(@PolyNonEmpty Collection this) { + default @PolyNonEmpty Stream stream(@MaybeModifiable @PolyNonEmpty Collection this) { return StreamSupport.stream(spliterator(), false); } @@ -823,7 +825,7 @@ default Spliterator spliterator() { * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - default Stream parallelStream() { + default Stream parallelStream(@MaybeModifiable Collection this) { return StreamSupport.stream(spliterator(), true); } } diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java index b62d99fe5b40..d2481186965e 100644 --- a/src/java.base/share/classes/java/util/Collections.java +++ b/src/java.base/share/classes/java/util/Collections.java @@ -29,6 +29,16 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyIteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -168,7 +178,7 @@ private Collections() { * @see List#sort(Comparator) */ @SideEffectsOnly("#1") - public static > void sort(List list) { + public static > void sort(@IteratorPolyMod @Replaceable List list) { list.sort(null); } @@ -203,7 +213,7 @@ public static > void sort(List list) { */ @SuppressWarnings({"unchecked", "rawtypes"}) @SideEffectsOnly("#1") - public static void sort(List list, @Nullable Comparator c) { + public static void sort(@IteratorPolyMod @Replaceable List list, @Nullable Comparator c) { list.sort(c); } @@ -240,7 +250,7 @@ public static void sort(List list, @Nullable Comparator c) { * with the elements of the list. */ public static - int binarySearch(List> list, T key) { + int binarySearch(@MaybeModifiable List> list, T key) { if (list instanceof RandomAccess || list.size()> list, T key) { } private static - int indexedBinarySearch(List> list, T key) { + int indexedBinarySearch(@MaybeModifiable List> list, T key) { int low = 0; int high = list.size()-1; @@ -268,7 +278,7 @@ else if (cmp > 0) } private static - int iteratorBinarySearch(List> list, T key) + int iteratorBinarySearch(@MaybeModifiable List> list, T key) { int low = 0; int high = list.size()-1; @@ -345,7 +355,7 @@ private static T get(ListIterator i, int index) { * elements of the list using this comparator. */ @SuppressWarnings("unchecked") - public static int binarySearch(List list, T key, @Nullable Comparator c) { + public static int binarySearch(@MaybeModifiable List list, T key, @Nullable Comparator c) { if (c==null) return binarySearch((List>) list, key); @@ -355,7 +365,7 @@ public static int binarySearch(List list, T key, @Nullable Comp return Collections.iteratorBinarySearch(list, key, c); } - private static int indexedBinarySearch(List l, T key, Comparator c) { + private static int indexedBinarySearch(@MaybeModifiable List l, T key, Comparator c) { int low = 0; int high = l.size()-1; @@ -374,7 +384,7 @@ else if (cmp > 0) return -(low + 1); // key not found } - private static int iteratorBinarySearch(List l, T key, Comparator c) { + private static int iteratorBinarySearch(@MaybeModifiable List l, T key, Comparator c) { int low = 0; int high = l.size()-1; ListIterator i = l.listIterator(); @@ -410,7 +420,7 @@ else if (cmp > 0) * @see List#reversed List.reversed */ @SuppressWarnings({"rawtypes", "unchecked"}) - public static void reverse(@GuardSatisfied List list) { + public static void reverse(@IteratorPolyMod @Replaceable @GuardSatisfied List list) { int size = list.size(); if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) { for (int i=0, mid=size>>1, j=size-1; i list) { * @throws UnsupportedOperationException if the specified list or * its list-iterator does not support the {@code set} operation. */ - public static void shuffle(@GuardSatisfied List list) { + public static void shuffle(@IteratorPolyMod @Replaceable @GuardSatisfied List list) { Random rnd = r; if (rnd == null) r = rnd = new Random(); // harmless race. @@ -480,7 +490,7 @@ public static void shuffle(@GuardSatisfied List list) { * @throws UnsupportedOperationException if the specified list or its * list-iterator does not support the {@code set} operation. */ - public static void shuffle(List list, Random rnd) { + public static void shuffle(@IteratorPolyMod @Replaceable List list, Random rnd) { shuffle(list, (RandomGenerator) rnd); } @@ -509,7 +519,7 @@ public static void shuffle(List list, Random rnd) { * @since 21 */ @SuppressWarnings({"rawtypes", "unchecked"}) - public static void shuffle(@GuardSatisfied List list, RandomGenerator rnd) { + public static void shuffle(@IteratorPolyMod @Replaceable @GuardSatisfied List list, RandomGenerator rnd) { int size = list.size(); if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) { for (int i=size; i>1; i--) @@ -547,7 +557,7 @@ public static void shuffle(@GuardSatisfied List list, RandomGenerator rnd) { * @since 1.4 */ @SuppressWarnings({"rawtypes", "unchecked"}) - public static void swap(@GuardSatisfied List list, int i, int j) { + public static void swap(@Replaceable @GuardSatisfied List list, int i, int j) { // instead of using a raw type here, it's possible to capture // the wildcard but it will require a call to a supplementary // private method @@ -576,7 +586,7 @@ private static void swap(Object[] arr, int i, int j) { * @throws UnsupportedOperationException if the specified list or its * list-iterator does not support the {@code set} operation. */ - public static void fill(@GuardSatisfied List list, T obj) { + public static void fill(@IteratorPolyMod @Replaceable @GuardSatisfied List list, T obj) { int size = list.size(); if (size < FILL_THRESHOLD || list instanceof RandomAccess) { @@ -609,7 +619,7 @@ public static void fill(@GuardSatisfied List list, T obj) { * @throws UnsupportedOperationException if the destination list's * list-iterator does not support the {@code set} operation. */ - public static void copy(List dest, List src) { + public static void copy(@IteratorPolyMod @Replaceable List dest, @MaybeModifiable List src) { int srcSize = src.size(); if (srcSize > dest.size()) throw new IndexOutOfBoundsException("Source does not fit in dest"); @@ -652,7 +662,7 @@ public static void copy(List dest, List src) { */ @Pure @StaticallyExecutable - public static > T min(Collection coll) { + public static > T min(@MaybeModifiable Collection coll) { Iterator i = coll.iterator(); T candidate = i.next(); @@ -690,7 +700,7 @@ public static > T min(Collection T min(Collection coll, @Nullable Comparator comp) { + public static T min(@MaybeModifiable Collection coll, @Nullable Comparator comp) { if (comp==null) return (T)min((Collection>) coll); @@ -729,7 +739,7 @@ public static T min(Collection coll, @Nullable Comparator> T max(Collection coll) { + public static > T max(@MaybeModifiable Collection coll) { Iterator i = coll.iterator(); T candidate = i.next(); @@ -767,7 +777,7 @@ public static > T max(Collection T max(Collection coll, @Nullable Comparator comp) { + public static T max(@MaybeModifiable Collection coll, @Nullable Comparator comp) { if (comp==null) return (T)max((Collection>) coll); @@ -837,7 +847,7 @@ public static T max(Collection coll, @Nullable Comparator list, int distance) { + public static void rotate(@IteratorPolyMod @Replaceable @GuardSatisfied List list, int distance) { if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD) rotate1(list, distance); else @@ -903,7 +913,7 @@ private static void rotate2(List list, int distance) { */ @SideEffectsOnly("#1") @DoesNotUnrefineReceiver("modifiability") - public static boolean replaceAll(List list, @Nullable T oldVal, T newVal) { + public static boolean replaceAll(@IteratorPolyMod @Replaceable List list, @Nullable T oldVal, T newVal) { boolean result = false; int size = list.size(); if (size < REPLACEALL_THRESHOLD || list instanceof RandomAccess) { @@ -964,7 +974,8 @@ public static boolean replaceAll(List list, @Nullable T oldVal, T newVal) * @since 1.4 */ @Pure - public static @GTENegativeOne int indexOfSubList(@GuardSatisfied List source, @GuardSatisfied List target) { + @StaticallyExecutable + public static @GTENegativeOne int indexOfSubList(@MaybeModifiable @GuardSatisfied List source, @GuardSatisfied List target) { int sourceSize = source.size(); int targetSize = target.size(); int maxCandidate = sourceSize - targetSize; @@ -1018,7 +1029,8 @@ public static boolean replaceAll(List list, @Nullable T oldVal, T newVal) * @since 1.4 */ @Pure - public static @GTENegativeOne int lastIndexOfSubList(@GuardSatisfied List source, @GuardSatisfied List target) { + @StaticallyExecutable + public static @GTENegativeOne int lastIndexOfSubList(@MaybeModifiable @GuardSatisfied List source, @GuardSatisfied List target) { int sourceSize = source.size(); int targetSize = target.size(); int maxCandidate = sourceSize - targetSize; @@ -1082,7 +1094,7 @@ public static boolean replaceAll(List list, @Nullable T oldVal, T newVal) */ @SuppressWarnings("unchecked") @SideEffectFree - public static @PolyGrowShrink @PolyNonEmpty Collection unmodifiableCollection(@PolyGrowShrink @PolyNonEmpty Collection c) { + public static @IteratorPolyMod @Unmodifiable @PolyGrowShrink @PolyNonEmpty Collection unmodifiableCollection(@MaybeModifiable @PolyGrowShrink @PolyNonEmpty Collection c) { if (c.getClass() == UnmodifiableCollection.class) { return (Collection) c; } @@ -1116,7 +1128,9 @@ static class UnmodifiableCollection implements Collection, Serializable { @SideEffectFree public @PolyNull @PolySigned Object[] toArray(Collections.UnmodifiableCollection<@PolyNull @PolySigned E> this) {return c.toArray();} public @Nullable T[] toArray(@PolyNull T[] a) {return c.toArray(a);} + @SideEffectFree public T[] toArray(IntFunction f) {return c.toArray(f);} + @SideEffectFree public String toString() {return c.toString();} @SideEffectFree @@ -1240,7 +1254,7 @@ public Stream parallelStream() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static SequencedCollection unmodifiableSequencedCollection(SequencedCollection c) { + public static @Unmodifiable SequencedCollection unmodifiableSequencedCollection(SequencedCollection c) { if (c.getClass() == UnmodifiableSequencedCollection.class) { return (SequencedCollection) c; } @@ -1327,7 +1341,7 @@ public E removeLast() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static @PolyNonEmpty Set unmodifiableSet(@PolyNonEmpty Set s) { + public static @IteratorPolyMod @Unmodifiable @PolyNonEmpty Set unmodifiableSet(@MaybeModifiable @PolyNonEmpty Set s) { // Not checking for subclasses because of heap pollution and information leakage. if (s.getClass() == UnmodifiableSet.class) { return (Set) s; @@ -1368,7 +1382,7 @@ static class UnmodifiableSet extends UnmodifiableCollection */ @SuppressWarnings("unchecked") @SideEffectFree - public static SequencedSet unmodifiableSequencedSet(SequencedSet s) { + public static @Unmodifiable SequencedSet unmodifiableSequencedSet(SequencedSet s) { // Not checking for subclasses because of heap pollution and information leakage. if (s.getClass() == UnmodifiableSequencedSet.class) { return (SequencedSet) s; @@ -1423,7 +1437,7 @@ public SequencedSet reversed() { * @return an unmodifiable view of the specified sorted set. */ @SideEffectFree - public static @PolyNonEmpty SortedSet unmodifiableSortedSet(@PolyNonEmpty SortedSet s) { + public static @Unmodifiable @PolyNonEmpty SortedSet unmodifiableSortedSet(@MaybeModifiable @PolyNonEmpty SortedSet s) { // Not checking for subclasses because of heap pollution and information leakage. if (s.getClass() == UnmodifiableSortedSet.class) { return s; @@ -1463,7 +1477,9 @@ public SortedSet tailSet(E fromElement) { return new UnmodifiableSortedSet<>(ss.tailSet(fromElement)); } + @Pure public E first() {return ss.first();} + @Pure public E last() {return ss.last();} } @@ -1486,7 +1502,7 @@ public SortedSet tailSet(E fromElement) { * @since 1.8 */ @SideEffectFree - public static @PolyNonEmpty NavigableSet unmodifiableNavigableSet(@PolyNonEmpty NavigableSet s) { + public static @Unmodifiable @PolyNonEmpty NavigableSet unmodifiableNavigableSet(@MaybeModifiable @PolyNonEmpty NavigableSet s) { if (s.getClass() == UnmodifiableNavigableSet.class) { return s; } @@ -1537,9 +1553,13 @@ public EmptyNavigableSet() { UnmodifiableNavigableSet(NavigableSet s) {super(s); ns = s;} + @Pure public @Nullable E lower(E e) { return ns.lower(e); } + @Pure public @Nullable E floor(E e) { return ns.floor(e); } + @Pure public @Nullable E ceiling(E e) { return ns.ceiling(e); } + @Pure public @Nullable E higher(E e) { return ns.higher(e); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") @@ -1552,6 +1572,7 @@ public EmptyNavigableSet() { public NavigableSet descendingSet() { return new UnmodifiableNavigableSet<>(ns.descendingSet()); } @SideEffectFree + @DoesNotUnrefineReceiver("modifiability") public Iterator descendingIterator() { return descendingSet().iterator(); } @@ -1595,7 +1616,7 @@ public NavigableSet tailSet(E fromElement, boolean inclusive) { */ @SuppressWarnings("unchecked") @SideEffectFree - public static @PolyGrowShrink @PolyNonEmpty List unmodifiableList(@PolyGrowShrink @PolyNonEmpty List list) { + public static @Unmodifiable @PolyGrowShrink @PolyNonEmpty List unmodifiableList(@MaybeModifiable @PolyGrowShrink @PolyNonEmpty List list) { if (list.getClass() == UnmodifiableList.class || list.getClass() == UnmodifiableRandomAccessList.class) { return (List) list; } @@ -1645,8 +1666,10 @@ public E remove(int index) { throw new UnsupportedOperationException(); } @Pure + @StaticallyExecutable public int indexOf(Object o) {return list.indexOf(o);} @Pure + @StaticallyExecutable public int lastIndexOf(Object o) {return list.lastIndexOf(o);} @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") @@ -1785,7 +1808,7 @@ private Object writeReplace() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static @PolyNonEmpty Map unmodifiableMap(@PolyNonEmpty Map m) { + public static @Unmodifiable @PolyNonEmpty Map unmodifiableMap(@MaybeModifiable @PolyNonEmpty Map m) { // Not checking for subclasses because of heap pollution and information leakage. if (m.getClass() == UnmodifiableMap.class) { return (Map) m; @@ -2074,6 +2097,7 @@ public void forEachRemaining(Consumer> action) { } @SuppressWarnings("unchecked") + @SideEffectFree public Object[] toArray() { Object[] a = c.toArray(); for (int i=0; i SequencedMap unmodifiableSequencedMap(SequencedMap m) { + public static @Unmodifiable SequencedMap unmodifiableSequencedMap(SequencedMap m) { // Not checking for subclasses because of heap pollution and information leakage. if (m.getClass() == UnmodifiableSequencedMap.class) { return (SequencedMap) m; @@ -2269,7 +2293,7 @@ public SequencedMap reversed() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static @PolyNonEmpty SortedMap unmodifiableSortedMap(@PolyNonEmpty SortedMap m) { + public static @Unmodifiable @PolyNonEmpty SortedMap unmodifiableSortedMap(@MaybeModifiable @PolyNonEmpty SortedMap m) { // Not checking for subclasses because of heap pollution and information leakage. if (m.getClass() == UnmodifiableSortedMap.class) { return (SortedMap) m; @@ -2301,7 +2325,9 @@ public SortedMap headMap(K toKey) @SideEffectFree public SortedMap tailMap(K fromKey) { return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); } + @Pure public K firstKey() { return sm.firstKey(); } + @Pure public K lastKey() { return sm.lastKey(); } } @@ -2326,7 +2352,7 @@ public SortedMap tailMap(K fromKey) */ @SuppressWarnings("unchecked") @SideEffectFree - public static @PolyNonEmpty NavigableMap unmodifiableNavigableMap(@PolyNonEmpty NavigableMap m) { + public static @Unmodifiable @PolyNonEmpty NavigableMap unmodifiableNavigableMap(@MaybeModifiable @PolyNonEmpty NavigableMap m) { if (m.getClass() == UnmodifiableNavigableMap.class) { return (NavigableMap) m; } @@ -2381,9 +2407,13 @@ public NavigableSet navigableKeySet() UnmodifiableNavigableMap(NavigableMap m) {super(m); nm = m;} + @Pure public @Nullable K lowerKey(K key) { return nm.lowerKey(key); } + @Pure public @Nullable K floorKey(K key) { return nm.floorKey(key); } + @Pure public @Nullable K ceilingKey(K key) { return nm.ceilingKey(key); } + @Pure public @Nullable K higherKey(K key) { return nm.higherKey(key); } @SuppressWarnings("unchecked") @@ -2502,11 +2532,11 @@ public NavigableMap tailMap(K fromKey, boolean inclusive) * @param c the collection to be "wrapped" in a synchronized collection. * @return a synchronized view of the specified collection. */ - public static @PolyGrowShrink @PolyNonEmpty Collection synchronizedCollection(@PolyGrowShrink @PolyNonEmpty Collection c) { + public static @PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty Collection synchronizedCollection(@PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty Collection c) { return new SynchronizedCollection<>(c); } - static @PolyGrowShrink @PolyNonEmpty Collection synchronizedCollection(@PolyGrowShrink @PolyNonEmpty Collection c, Object mutex) { + static @PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty Collection synchronizedCollection(@PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty Collection c, Object mutex) { return new SynchronizedCollection<>(c, mutex); } @@ -2553,6 +2583,7 @@ public boolean contains(@UnknownSignedness Object o) { public @Nullable T[] toArray(@PolyNull T[] a) { synchronized (mutex) {return c.toArray(a);} } + @SideEffectFree public T[] toArray(IntFunction f) { synchronized (mutex) {return c.toArray(f);} } @@ -2658,11 +2689,11 @@ private void writeObject(ObjectOutputStream s) throws IOException { * @param s the set to be "wrapped" in a synchronized set. * @return a synchronized view of the specified set. */ - public static Set synchronizedSet(Set s) { + public static @PolyIteratorPolyMod @PolyModifiable Set synchronizedSet(@PolyIteratorPolyMod @PolyModifiable Set s) { return new SynchronizedSet<>(s); } - static Set synchronizedSet(Set s, Object mutex) { + static @PolyIteratorPolyMod @PolyModifiable Set synchronizedSet(@PolyIteratorPolyMod @PolyModifiable Set s, Object mutex) { return new SynchronizedSet<>(s, mutex); } @@ -2733,7 +2764,7 @@ public int hashCode() { * @param s the sorted set to be "wrapped" in a synchronized sorted set. * @return a synchronized view of the specified sorted set. */ - public static SortedSet synchronizedSortedSet(SortedSet s) { + public static @PolyModifiable SortedSet synchronizedSortedSet(@PolyModifiable SortedSet s) { return new SynchronizedSortedSet<>(s); } @@ -2781,9 +2812,11 @@ public SortedSet tailSet(E fromElement) { } } + @Pure public E first() { synchronized (mutex) {return ss.first();} } + @Pure public E last() { synchronized (mutex) {return ss.last();} } @@ -2830,7 +2863,7 @@ public E last() { * @return a synchronized view of the specified navigable set * @since 1.8 */ - public static NavigableSet synchronizedNavigableSet(NavigableSet s) { + public static @PolyModifiable NavigableSet synchronizedNavigableSet(@PolyModifiable NavigableSet s) { return new SynchronizedNavigableSet<>(s); } @@ -2940,13 +2973,13 @@ public NavigableSet tailSet(E fromElement, boolean inclusive) { * @param list the list to be "wrapped" in a synchronized list. * @return a synchronized view of the specified list. */ - public static @PolyGrowShrink @PolyNonEmpty List synchronizedList(@PolyGrowShrink @PolyNonEmpty List list) { + public static @PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty List synchronizedList(@PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty List list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list)); } - static @PolyGrowShrink @PolyNonEmpty List synchronizedList(@PolyGrowShrink @PolyNonEmpty List list, Object mutex) { + static @PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty List synchronizedList(@PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty List list, Object mutex) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list, mutex) : new SynchronizedList<>(list, mutex)); @@ -3006,10 +3039,12 @@ public E remove(int index) { } @Pure + @StaticallyExecutable public int indexOf(Object o) { synchronized (mutex) {return list.indexOf(o);} } @Pure + @StaticallyExecutable public int lastIndexOf(Object o) { synchronized (mutex) {return list.lastIndexOf(o);} } @@ -3134,7 +3169,7 @@ private Object writeReplace() { * @param m the map to be "wrapped" in a synchronized map. * @return a synchronized view of the specified map. */ - public static Map synchronizedMap(Map m) { + public static @PolyModifiable Map synchronizedMap(@PolyModifiable Map m) { return new SynchronizedMap<>(m); } @@ -3210,7 +3245,7 @@ public void clear() { private transient Set> entrySet; private transient Collection values; - public Set keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set keySet(@PolyShrinkable SynchronizedMap this) { synchronized (mutex) { if (keySet==null) keySet = new SynchronizedSet<>(m.keySet(), mutex); @@ -3219,7 +3254,7 @@ public Set keySet() { } @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable SynchronizedMap this) { synchronized (mutex) { if (entrySet==null) entrySet = new SynchronizedSet<>(m.entrySet(), mutex); @@ -3227,7 +3262,7 @@ public Set> entrySet() { } } - public Collection values() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable SynchronizedMap this) { synchronized (mutex) { if (values==null) values = new SynchronizedCollection<>(m.values(), mutex); @@ -3365,7 +3400,7 @@ private void writeObject(ObjectOutputStream s) throws IOException { * @param m the sorted map to be "wrapped" in a synchronized sorted map. * @return a synchronized view of the specified sorted map. */ - public static SortedMap synchronizedSortedMap(SortedMap m) { + public static @PolyModifiable SortedMap synchronizedSortedMap(@PolyModifiable SortedMap m) { return new SynchronizedSortedMap<>(m); } @@ -3416,9 +3451,11 @@ public SortedMap tailMap(K fromKey) { } } + @Pure public K firstKey() { synchronized (mutex) {return sm.firstKey();} } + @Pure public K lastKey() { synchronized (mutex) {return sm.lastKey();} } @@ -3471,7 +3508,7 @@ public K lastKey() { * @return a synchronized view of the specified navigable map. * @since 1.8 */ - public static NavigableMap synchronizedNavigableMap(NavigableMap m) { + public static @PolyModifiable NavigableMap synchronizedNavigableMap(@PolyModifiable NavigableMap m) { return new SynchronizedNavigableMap<>(m); } @@ -3501,18 +3538,22 @@ static class SynchronizedNavigableMap public @Nullable Entry lowerEntry(K key) { synchronized (mutex) { return nm.lowerEntry(key); } } + @Pure public @Nullable K lowerKey(K key) { synchronized (mutex) { return nm.lowerKey(key); } } public @Nullable Entry floorEntry(K key) { synchronized (mutex) { return nm.floorEntry(key); } } + @Pure public @Nullable K floorKey(K key) { synchronized (mutex) { return nm.floorKey(key); } } public @Nullable Entry ceilingEntry(K key) { synchronized (mutex) { return nm.ceilingEntry(key); } } + @Pure public @Nullable K ceilingKey(K key) { synchronized (mutex) { return nm.ceilingKey(key); } } public @Nullable Entry higherEntry(K key) { synchronized (mutex) { return nm.higherEntry(key); } } + @Pure public @Nullable K higherKey(K key) { synchronized (mutex) { return nm.higherKey(key); } } public @Nullable Entry firstEntry() @@ -3536,19 +3577,19 @@ public NavigableMap descendingMap() { } } - public NavigableSet keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet keySet(@PolyShrinkable SynchronizedNavigableMap this) { return navigableKeySet(); } @SideEffectFree - public NavigableSet navigableKeySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet navigableKeySet(@PolyShrinkable SynchronizedNavigableMap this) { synchronized (mutex) { return new SynchronizedNavigableSet<>(nm.navigableKeySet(), mutex); } } @SideEffectFree - public NavigableSet descendingKeySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet descendingKeySet(@PolyShrinkable SynchronizedNavigableMap this) { synchronized (mutex) { return new SynchronizedNavigableSet<>(nm.descendingKeySet(), mutex); } @@ -3663,7 +3704,7 @@ public NavigableMap tailMap(K fromKey, boolean inclusive) { * @return a dynamically typesafe view of the specified collection * @since 1.5 */ - public static @PolyGrowShrink @PolyNonEmpty Collection checkedCollection(@PolyGrowShrink @PolyNonEmpty Collection c, + public static @PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty Collection checkedCollection(@PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty Collection c, Class type) { return new CheckedCollection<>(c, type); } @@ -3713,6 +3754,7 @@ private String badElementMsg(Object o) { @SideEffectFree public @PolyNull @PolySigned Object[] toArray(Collections.CheckedCollection<@PolyNull @PolySigned E> this) { return c.toArray(); } public @Nullable T[] toArray(@PolyNull T[] a) { return c.toArray(a); } + @SideEffectFree public T[] toArray(IntFunction f) { return c.toArray(f); } public String toString() { return c.toString(); } @SideEffectsOnly("this") @@ -3849,7 +3891,7 @@ public boolean removeIf(Predicate filter) { * @return a dynamically typesafe view of the specified queue * @since 1.8 */ - public static @PolyGrowShrink @PolyNonEmpty Queue checkedQueue(@PolyGrowShrink @PolyNonEmpty Queue queue, Class type) { + public static @PolyGrowShrink @PolyModifiable @PolyNonEmpty Queue checkedQueue(@PolyGrowShrink @PolyModifiable @PolyNonEmpty Queue queue, Class type) { return new CheckedQueue<>(queue, type); } @@ -3916,7 +3958,7 @@ static class CheckedQueue * @return a dynamically typesafe view of the specified set * @since 1.5 */ - public static Set checkedSet(Set s, Class type) { + public static @PolyIteratorPolyMod @PolyModifiable Set checkedSet(@PolyIteratorPolyMod @PolyModifiable Set s, Class type) { return new CheckedSet<>(s, type); } @@ -3965,7 +4007,7 @@ static class CheckedSet extends CheckedCollection * @return a dynamically typesafe view of the specified sorted set * @since 1.5 */ - public static SortedSet checkedSortedSet(SortedSet s, + public static @PolyModifiable SortedSet checkedSortedSet(@PolyModifiable SortedSet s, Class type) { return new CheckedSortedSet<>(s, type); } @@ -3989,7 +4031,9 @@ static class CheckedSortedSet extends CheckedSet @Pure public @Nullable Comparator comparator() { return ss.comparator(); } + @Pure public E first() { return ss.first(); } + @Pure public E last() { return ss.last(); } public SortedSet subSet(E fromElement, E toElement) { @@ -4031,7 +4075,7 @@ public SortedSet tailSet(E fromElement) { * @return a dynamically typesafe view of the specified navigable set * @since 1.8 */ - public static NavigableSet checkedNavigableSet(NavigableSet s, + public static @PolyModifiable NavigableSet checkedNavigableSet(@PolyModifiable NavigableSet s, Class type) { return new CheckedNavigableSet<>(s, type); } @@ -4053,9 +4097,13 @@ static class CheckedNavigableSet extends CheckedSortedSet ns = s; } + @Pure public @Nullable E lower(E e) { return ns.lower(e); } + @Pure public @Nullable E floor(E e) { return ns.floor(e); } + @Pure public @Nullable E ceiling(E e) { return ns.ceiling(e); } + @Pure public @Nullable E higher(E e) { return ns.higher(e); } public @Nullable E pollFirst() { return ns.pollFirst(); } public @Nullable E pollLast() {return ns.pollLast(); } @@ -4116,7 +4164,7 @@ public NavigableSet tailSet(E fromElement, boolean inclusive) { * @return a dynamically typesafe view of the specified list * @since 1.5 */ - public static @PolyGrowShrink @PolyNonEmpty List checkedList(@PolyGrowShrink @PolyNonEmpty List list, Class type) { + public static @PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty List checkedList(@PolyGrowShrink @PolyIteratorPolyMod @PolyModifiable @PolyNonEmpty List list, Class type) { return (list instanceof RandomAccess ? new CheckedRandomAccessList<>(list, type) : new CheckedList<>(list, type)); @@ -4144,13 +4192,16 @@ static class CheckedList @Pure public int hashCode() { return list.hashCode(); } @Pure + @StaticallyExecutable public E get(int index) { return list.get(index); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") public E remove(int index) { return list.remove(index); } @Pure + @StaticallyExecutable public int indexOf(Object o) { return list.indexOf(o); } @Pure + @StaticallyExecutable public int lastIndexOf(Object o) { return list.lastIndexOf(o); } @EnsuresNonEmpty("this") @@ -4295,7 +4346,7 @@ public List subList(int fromIndex, int toIndex) { * @return a dynamically typesafe view of the specified map * @since 1.5 */ - public static Map checkedMap(Map m, + public static @PolyModifiable Map checkedMap(@PolyModifiable Map m, Class keyType, Class valueType) { return new CheckedMap<>(m, keyType, valueType); @@ -4370,8 +4421,8 @@ private String badValueMsg(Object value) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") public void clear() { m.clear(); } - public Set keySet() { return m.keySet(); } - public Collection values() { return m.values(); } + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set keySet(@PolyShrinkable CheckedMap this) { return m.keySet(); } + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable CheckedMap this) { return m.values(); } @Pure public boolean equals(@Nullable Object o) { return o == this || m.equals(o); } @Pure @@ -4412,7 +4463,7 @@ public void putAll(Map t) { private transient Set> entrySet; @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable CheckedMap this) { if (entrySet==null) entrySet = new CheckedEntrySet<>(m.entrySet(), valueType); return entrySet; @@ -4567,6 +4618,7 @@ public void forEachRemaining(Consumer> action) { } @SuppressWarnings("unchecked") + @SideEffectFree public Object[] toArray() { Object[] source = s.toArray(); @@ -4759,7 +4811,7 @@ public boolean equals(@Nullable Object o) { * @return a dynamically typesafe view of the specified map * @since 1.5 */ - public static SortedMap checkedSortedMap(SortedMap m, + public static @PolyModifiable SortedMap checkedSortedMap(@PolyModifiable SortedMap m, Class keyType, Class valueType) { return new CheckedSortedMap<>(m, keyType, valueType); @@ -4785,7 +4837,9 @@ static class CheckedSortedMap extends CheckedMap @Pure public @Nullable Comparator comparator() { return sm.comparator(); } + @Pure public K firstKey() { return sm.firstKey(); } + @Pure public K lastKey() { return sm.lastKey(); } @SideEffectFree @@ -4839,7 +4893,7 @@ public SortedMap tailMap(K fromKey) { * @return a dynamically typesafe view of the specified map * @since 1.8 */ - public static NavigableMap checkedNavigableMap(NavigableMap m, + public static @PolyModifiable NavigableMap checkedNavigableMap(@PolyModifiable NavigableMap m, Class keyType, Class valueType) { return new CheckedNavigableMap<>(m, keyType, valueType); @@ -4865,7 +4919,9 @@ static class CheckedNavigableMap extends CheckedSortedMap @Pure public @Nullable Comparator comparator() { return nm.comparator(); } + @Pure public K firstKey() { return nm.firstKey(); } + @Pure public K lastKey() { return nm.lastKey(); } public @Nullable Entry lowerEntry(K key) { @@ -4875,6 +4931,7 @@ static class CheckedNavigableMap extends CheckedSortedMap : null; } + @Pure public @Nullable K lowerKey(K key) { return nm.lowerKey(key); } public @Nullable Entry floorEntry(K key) { @@ -4884,6 +4941,7 @@ static class CheckedNavigableMap extends CheckedSortedMap : null; } + @Pure public @Nullable K floorKey(K key) { return nm.floorKey(key); } public @Nullable Entry ceilingEntry(K key) { @@ -4893,6 +4951,7 @@ static class CheckedNavigableMap extends CheckedSortedMap : null; } + @Pure public @Nullable K ceilingKey(K key) { return nm.ceilingKey(key); } public @Nullable Entry higherEntry(K key) { @@ -4902,6 +4961,7 @@ static class CheckedNavigableMap extends CheckedSortedMap : null; } + @Pure public @Nullable K higherKey(K key) { return nm.higherKey(key); } public @Nullable Entry firstEntry() { @@ -4941,17 +5001,17 @@ public NavigableMap descendingMap() { return checkedNavigableMap(nm.descendingMap(), keyType, valueType); } - public NavigableSet keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet keySet(@PolyShrinkable CheckedNavigableMap this) { return navigableKeySet(); } @SideEffectFree - public NavigableSet navigableKeySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet navigableKeySet(@PolyShrinkable CheckedNavigableMap this) { return checkedNavigableSet(nm.navigableKeySet(), keyType); } @SideEffectFree - public NavigableSet descendingKeySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet descendingKeySet(@PolyShrinkable CheckedNavigableMap this) { return checkedNavigableSet(nm.descendingKeySet(), keyType); } @@ -5013,7 +5073,7 @@ public NavigableMap tailMap(K fromKey, boolean inclusive) { */ @SuppressWarnings("unchecked") @SideEffectFree - public static Iterator emptyIterator() { + public static @Unmodifiable Iterator emptyIterator() { return (Iterator) EmptyIterator.EMPTY_ITERATOR; } @@ -5064,7 +5124,7 @@ public void forEachRemaining(Consumer action) { */ @SuppressWarnings("unchecked") @SideEffectFree - public static ListIterator emptyListIterator() { + public static @Unmodifiable ListIterator emptyListIterator() { return (ListIterator) EmptyListIterator.EMPTY_ITERATOR; } @@ -5130,7 +5190,7 @@ private static class EmptyEnumeration implements Enumeration { * @see #emptySet() */ @SuppressWarnings("rawtypes") - public static final Set EMPTY_SET = new EmptySet<>(); + public static final @Unmodifiable Set EMPTY_SET = new EmptySet<>(); /** * Returns an empty set (immutable). This set is serializable. @@ -5153,7 +5213,7 @@ private static class EmptyEnumeration implements Enumeration { */ @SuppressWarnings("unchecked") @SideEffectFree - public static final Set emptySet() { + public static final @Unmodifiable Set emptySet() { return (Set) EMPTY_SET; } @@ -5236,7 +5296,7 @@ public int hashCode() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static SortedSet emptySortedSet() { + public static @Unmodifiable SortedSet emptySortedSet() { return (SortedSet) UnmodifiableNavigableSet.EMPTY_NAVIGABLE_SET; } @@ -5258,7 +5318,7 @@ public static SortedSet emptySortedSet() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static NavigableSet emptyNavigableSet() { + public static @Unmodifiable NavigableSet emptyNavigableSet() { return (NavigableSet) UnmodifiableNavigableSet.EMPTY_NAVIGABLE_SET; } @@ -5268,7 +5328,7 @@ public static NavigableSet emptyNavigableSet() { * @see #emptyList() */ @SuppressWarnings("rawtypes") - public static final List EMPTY_LIST = new EmptyList<>(); + public static final @Unmodifiable List EMPTY_LIST = new EmptyList<>(); /** * Returns an empty list (immutable). This list is serializable. @@ -5292,7 +5352,7 @@ public static NavigableSet emptyNavigableSet() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static final List emptyList() { + public static final @Unmodifiable List emptyList() { return (List) EMPTY_LIST; } @@ -5384,7 +5444,7 @@ private Object readResolve() { * @since 1.3 */ @SuppressWarnings("rawtypes") - public static final Map EMPTY_MAP = new EmptyMap<>(); + public static final @Unmodifiable Map EMPTY_MAP = new EmptyMap<>(); /** * Returns an empty map (immutable). This map is serializable. @@ -5406,7 +5466,7 @@ private Object readResolve() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static final Map emptyMap() { + public static final @Unmodifiable Map emptyMap() { return (Map) EMPTY_MAP; } @@ -5428,7 +5488,7 @@ public static final Map emptyMap() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static final SortedMap emptySortedMap() { + public static final @Unmodifiable SortedMap emptySortedMap() { return (SortedMap) UnmodifiableNavigableMap.EMPTY_NAVIGABLE_MAP; } @@ -5450,7 +5510,7 @@ public static final SortedMap emptySortedMap() { */ @SuppressWarnings("unchecked") @SideEffectFree - public static final NavigableMap emptyNavigableMap() { + public static final @Unmodifiable NavigableMap emptyNavigableMap() { return (NavigableMap) UnmodifiableNavigableMap.EMPTY_NAVIGABLE_MAP; } @@ -5581,11 +5641,11 @@ private Object readResolve() { * @param o the sole object to be stored in the returned set. * @return an immutable set containing only the specified object. */ - public static Set singleton(T o) { + public static @Unmodifiable Set singleton(T o) { return new SingletonSet<>(o); } - static Iterator singletonIterator(final E e) { + static @Unmodifiable Iterator singletonIterator(final E e) { return new Iterator<>() { private boolean hasNext = true; @Pure @@ -5723,7 +5783,7 @@ public int hashCode() { * @return an immutable list containing only the specified object. * @since 1.3 */ - public static @ArrayLen(1) List singletonList(T o) { + public static @Unmodifiable @ArrayLen(1) List singletonList(T o) { return new SingletonList<>(o); } @@ -5806,7 +5866,7 @@ public int hashCode() { * mapping. * @since 1.3 */ - public static Map singletonMap(K key, V value) { + public static @Unmodifiable Map singletonMap(K key, V value) { return new SingletonMap<>(key, value); } @@ -5967,7 +6027,7 @@ public int hashCode() { * @see List#addAll(Collection) * @see List#addAll(int, Collection) */ - public static List nCopies(@NonNegative int n, T o) { + public static @Unmodifiable List nCopies(@NonNegative int n, T o) { if (n < 0) throw new IllegalArgumentException("List length = " + n); return new CopiesList<>(n, o); @@ -6005,11 +6065,13 @@ public boolean contains(@UnknownSignedness Object obj) { } @Pure + @StaticallyExecutable public int indexOf(Object o) { return contains(o) ? 0 : -1; } @Pure + @StaticallyExecutable public int lastIndexOf(Object o) { return contains(o) ? n - 1 : -1; } @@ -6031,6 +6093,7 @@ public void forEach(Consumer action) { } } + @SideEffectFree public @PolyNull @PolySigned Object[] toArray(Collections.CopiesList<@PolyNull @PolySigned E> this) { final Object[] a = new Object[n]; if (element != null) @@ -6332,7 +6395,7 @@ public T nextElement(/*@NonEmpty Enumeration this*/) { * @see Enumeration * @see ArrayList */ - public static ArrayList list(Enumeration e) { + public static @Modifiable @IteratorPolyMod ArrayList list(Enumeration e) { ArrayList l = new ArrayList<>(); while (e.hasMoreElements()) l.add(e.nextElement()); @@ -6361,7 +6424,7 @@ static boolean eq(Object o1, Object o2) { * @throws NullPointerException if {@code c} is null * @since 1.5 */ - public static @NonNegative int frequency(Collection c, @Nullable Object o) { + public static @NonNegative int frequency(@MaybeModifiable Collection c, @Nullable Object o) { int result = 0; if (o == null) { for (Object e : c) @@ -6413,7 +6476,7 @@ static boolean eq(Object o1, Object o2) { * (optional) * @since 1.5 */ - public static boolean disjoint(Collection c1, Collection c2) { + public static boolean disjoint(@MaybeModifiable Collection c1, @MaybeModifiable Collection c2) { // The collection to be used for contains(). Preference is given to // the collection who's contains() has lower O() complexity. Collection contains = c2; @@ -6493,7 +6556,7 @@ public static boolean disjoint(Collection c1, Collection c2) { */ @SafeVarargs @SideEffectsOnly("#1") - public static boolean addAll(@GuardSatisfied Collection c, T... elements) { + public static boolean addAll(@Growable @GuardSatisfied Collection c, T... elements) { boolean result = false; for (T element : elements) result |= c.add(element); @@ -6532,7 +6595,7 @@ public static boolean addAll(@GuardSatisfied Collection c, T... e * @since 1.6 */ @SideEffectFree - public static Set newSetFromMap(Map map) { + public static @PolyModifiable Set newSetFromMap(@PolyModifiable Map map) { if (! map.isEmpty()) // implicit null check throw new IllegalArgumentException("Map is non-empty"); return new SetFromMap<>(map); @@ -6726,7 +6789,7 @@ private SequencedMap map() { * @return the queue * @since 1.6 */ - public static @PolyGrowShrink @PolyNonEmpty Queue asLifoQueue(@PolyGrowShrink @PolyNonEmpty Deque deque) { + public static @PolyGrowShrink @PolyModifiable @PolyNonEmpty Queue asLifoQueue(@PolyGrowShrink @PolyModifiable @PolyNonEmpty Deque deque) { return new AsLIFOQueue<>(Objects.requireNonNull(deque)); } @@ -6766,6 +6829,7 @@ static class AsLIFOQueue extends AbstractQueue @SideEffectFree public @PolyNull @PolySigned Object[] toArray(Collections.AsLIFOQueue<@PolyNull @PolySigned E> this) { return q.toArray(); } public @Nullable T[] toArray(@PolyNull T[] a) { return q.toArray(a); } + @SideEffectFree public T[] toArray(IntFunction f) { return q.toArray(f); } public String toString() { return q.toString(); } @Pure diff --git a/src/java.base/share/classes/java/util/Deque.java b/src/java.base/share/classes/java/util/Deque.java index 86641c7d78f2..3130d363c6be 100644 --- a/src/java.base/share/classes/java/util/Deque.java +++ b/src/java.base/share/classes/java/util/Deque.java @@ -39,6 +39,10 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -219,7 +223,7 @@ * @param the type of elements held in this deque */ @CFComment({"lock/nullness: Subclasses of this interface/class may opt to prohibit null elements"}) -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public interface Deque extends Queue, SequencedCollection { /** * Inserts the specified element at the front of this deque if it is @@ -241,8 +245,7 @@ public interface Deque extends Queue, SequencedCollection { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void addFirst(@GuardSatisfied Deque this, E e); - + void addFirst(@SeqGrowable @GuardSatisfied Deque this, E e); /** * Inserts the specified element at the end of this deque if it is * possible to do so immediately without violating capacity restrictions, @@ -265,8 +268,7 @@ public interface Deque extends Queue, SequencedCollection { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void addLast(@GuardSatisfied Deque this, E e); - + void addLast(@SeqGrowable @GuardSatisfied Deque this, E e); /** * Inserts the specified element at the front of this deque unless it would * violate capacity restrictions. When using a capacity-restricted deque, @@ -285,8 +287,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offerFirst(E e); - + boolean offerFirst(@SeqGrowable Deque this, E e); /** * Inserts the specified element at the end of this deque unless it would * violate capacity restrictions. When using a capacity-restricted deque, @@ -305,8 +306,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offerLast(E e); - + boolean offerLast(@SeqGrowable Deque this, E e); /** * Retrieves and removes the first element of this deque. This method * differs from {@link #pollFirst pollFirst} only in that it throws an @@ -317,8 +317,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E removeFirst(@GuardSatisfied @NonEmpty @CanShrink Deque this); - + E removeFirst(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink Deque this); /** * Retrieves and removes the last element of this deque. This method * differs from {@link #pollLast pollLast} only in that it throws an @@ -329,8 +328,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E removeLast(@GuardSatisfied @NonEmpty @CanShrink Deque this); - + E removeLast(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink Deque this); /** * Retrieves and removes the first element of this deque, * or returns {@code null} if this deque is empty. @@ -339,8 +337,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable E pollFirst(@GuardSatisfied @CanShrink Deque this); - + @Nullable E pollFirst(@Shrinkable @GuardSatisfied @CanShrink Deque this); /** * Retrieves and removes the last element of this deque, * or returns {@code null} if this deque is empty. @@ -349,8 +346,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable E pollLast(@GuardSatisfied @CanShrink Deque this); - + @Nullable E pollLast(@Shrinkable @GuardSatisfied @CanShrink Deque this); /** * Retrieves, but does not remove, the first element of this deque. * @@ -413,8 +409,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean removeFirstOccurrence(@GuardSatisfied @CanShrink Deque this, Object o); - + boolean removeFirstOccurrence(@Shrinkable @GuardSatisfied @CanShrink Deque this, Object o); /** * Removes the last occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. @@ -434,8 +429,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean removeLastOccurrence(@GuardSatisfied @CanShrink Deque this, Object o); - + boolean removeLastOccurrence(@Shrinkable @GuardSatisfied @CanShrink Deque this, Object o); // *** Queue methods *** /** @@ -463,8 +457,7 @@ public interface Deque extends Queue, SequencedCollection { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean add(@GuardSatisfied Deque this, E e); - + boolean add(@Growable @GuardSatisfied Deque this, E e); /** * Inserts the specified element into the queue represented by this deque * (in other words, at the tail of this deque) if it is possible to do so @@ -488,8 +481,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offer(E e); - + boolean offer(@Growable Deque this, E e); /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque). @@ -503,8 +495,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E remove(@GuardSatisfied @NonEmpty @CanShrink Deque this); - + E remove(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink Deque this); /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque), or returns @@ -517,8 +508,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable E poll(@GuardSatisfied @CanShrink Deque this); - + @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink Deque this); /** * Retrieves, but does not remove, the head of the queue represented by * this deque (in other words, the first element of this deque). @@ -572,8 +562,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean addAll(Collection c); - + boolean addAll(@Growable Deque this, Collection c); // *** Stack methods *** /** @@ -596,8 +585,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void push(@GuardSatisfied Deque this, E e); - + void push(@SeqGrowable @GuardSatisfied Deque this, E e); /** * Pops an element from the stack represented by this deque. In other * words, removes and returns the first element of this deque. @@ -610,8 +598,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E pop(@GuardSatisfied @NonEmpty @CanShrink Deque this); - + E pop(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink Deque this); // *** Collection methods *** @@ -636,8 +623,7 @@ public interface Deque extends Queue, SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean remove(@GuardSatisfied @CanShrink Deque this, @UnknownSignedness Object o); - + boolean remove(@Shrinkable @GuardSatisfied @CanShrink Deque this, @UnknownSignedness Object o); /** * Returns {@code true} if this deque contains the specified element. * More formally, returns {@code true} if and only if this deque contains diff --git a/src/java.base/share/classes/java/util/EnumMap.java b/src/java.base/share/classes/java/util/EnumMap.java index 16ef64062e50..0c29a347e7f7 100644 --- a/src/java.base/share/classes/java/util/EnumMap.java +++ b/src/java.base/share/classes/java/util/EnumMap.java @@ -28,6 +28,14 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.initialization.qual.UnknownInitialization; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nullness.qual.EnsuresKeyFor; @@ -97,7 +105,7 @@ * @see EnumSet * @since 1.5 */ -@AnnotatedFor({"nullness", "index"}) +@AnnotatedFor({"nullness", "index", "modifiability"}) public class EnumMap, V> extends AbstractMap implements java.io.Serializable, Cloneable { @@ -155,7 +163,7 @@ private Object maskNull(@Nullable Object value) { * @param keyType the class object of the key type for this enum map * @throws NullPointerException if {@code keyType} is null */ - public EnumMap(Class keyType) { + public @Modifiable EnumMap(Class keyType) { this.keyType = keyType; keyUniverse = getKeyUniverse(keyType); vals = new Object[keyUniverse.length]; @@ -168,7 +176,7 @@ public EnumMap(Class keyType) { * @param m the enum map from which to initialize this enum map * @throws NullPointerException if {@code m} is null */ - public EnumMap(EnumMap m) { + public @Modifiable EnumMap(EnumMap m) { keyType = m.keyType; keyUniverse = m.keyUniverse; vals = m.vals.clone(); @@ -187,7 +195,7 @@ public EnumMap(EnumMap m) { * {@code EnumMap} instance and contains no mappings * @throws NullPointerException if {@code m} is null */ - public EnumMap(Map m) { + public @Modifiable EnumMap(Map m) { if (m instanceof EnumMap) { EnumMap em = (EnumMap) m; keyType = em.keyType; @@ -293,7 +301,7 @@ private boolean containsMapping(@GuardSatisfied @UnknownSignedness Object key, @ @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(K key, V value) { + public @Nullable V put(@Growable @Replaceable EnumMap this, K key, V value) { typeCheck(key); int index = key.ordinal(); @@ -315,7 +323,7 @@ private boolean containsMapping(@GuardSatisfied @UnknownSignedness Object key, @ */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V remove(@GuardSatisfied @UnknownSignedness Object key) { + public @Nullable V remove(@Shrinkable EnumMap this, @GuardSatisfied @UnknownSignedness Object key) { if (!isValidKey(key)) return null; int index = ((Enum)key).ordinal(); @@ -369,7 +377,7 @@ private boolean isValidKey(@GuardSatisfied @UnknownSignedness Object key) { @RequiresNonNull({"keyUniverse", "vals"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putAll(@UnknownInitialization EnumMap this, Map m) { + public void putAll(@Growable @Replaceable @UnknownInitialization EnumMap this, Map m) { if (m instanceof EnumMap em) { if (em.keyType != keyType) { if (em.isEmpty()) @@ -395,7 +403,7 @@ public void putAll(@UnknownInitialization EnumMap this, Map this) { Arrays.fill(vals, null); size = 0; } @@ -419,7 +427,7 @@ public void clear() { * @return a set view of the keys contained in this enum map */ @SideEffectFree - public Set keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set keySet(@PolyShrinkable EnumMap this) { Set ks = keySet; if (ks == null) { ks = new KeySet(); @@ -444,14 +452,14 @@ public boolean contains(@Nullable @UnknownSignedness Object o) { } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable KeySet this, @Nullable @UnknownSignedness Object o) { int oldSize = size; EnumMap.this.remove(o); return size != oldSize; } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear() { + public void clear(@Shrinkable KeySet this) { EnumMap.this.clear(); } } @@ -467,7 +475,7 @@ public void clear() { * @return a collection view of the values contained in this map */ @SideEffectFree - public Collection values() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable EnumMap this) { Collection vs = values; if (vs == null) { vs = new Values(); @@ -492,7 +500,7 @@ public boolean contains(@Nullable @UnknownSignedness Object o) { } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable Values this, @Nullable @UnknownSignedness Object o) { o = maskNull(o); for (int i = 0; i < vals.length; i++) { @@ -506,7 +514,7 @@ public boolean remove(@Nullable @UnknownSignedness Object o) { } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear() { + public void clear(@Shrinkable Values this) { EnumMap.this.clear(); } } @@ -521,7 +529,7 @@ public void clear() { * @return a set view of the mappings contained in this enum map */ @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable EnumMap this) { Set> es = entrySet; if (es != null) return es; @@ -543,7 +551,7 @@ public boolean contains(@Nullable @UnknownSignedness Object o) { } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable EntrySet this, @Nullable @UnknownSignedness Object o) { return o instanceof Map.Entry entry && removeMapping(entry.getKey(), entry.getValue()); } @@ -553,7 +561,7 @@ public boolean remove(@Nullable @UnknownSignedness Object o) { } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear() { + public void clear(@Shrinkable EntrySet this) { EnumMap.this.clear(); } @SideEffectFree @@ -606,7 +614,7 @@ public boolean hasNext() { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void remove() { + public void remove(@Shrinkable EnumMapIterator this) { checkLastReturnedIndex(); if (vals[lastReturnedIndex] != null) { @@ -830,7 +838,7 @@ private int entryHashCode(int index) { */ @SuppressWarnings("unchecked") @SideEffectFree - public EnumMap clone() { + public @Modifiable EnumMap clone() { EnumMap result = null; try { result = (EnumMap) super.clone(); diff --git a/src/java.base/share/classes/java/util/EnumSet.java b/src/java.base/share/classes/java/util/EnumSet.java index aa52dbefd7c9..916e1c9aef37 100644 --- a/src/java.base/share/classes/java/util/EnumSet.java +++ b/src/java.base/share/classes/java/util/EnumSet.java @@ -25,6 +25,10 @@ package java.util; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; @@ -84,7 +88,7 @@ * @since 1.5 * @see EnumMap */ -@AnnotatedFor({"nullness", "index"}) +@AnnotatedFor({"nullness", "index", "modifiability"}) public abstract sealed class EnumSet> extends AbstractSet implements Cloneable, java.io.Serializable permits JumboEnumSet, RegularEnumSet { @@ -102,7 +106,7 @@ public abstract sealed class EnumSet> extends AbstractSet */ final transient Enum[] universe; - EnumSet(ClasselementType, Enum[] universe) { + @Modifiable @IteratorPolyMod EnumSet(ClasselementType, Enum[] universe) { this.elementType = elementType; this.universe = universe; } @@ -116,7 +120,7 @@ public abstract sealed class EnumSet> extends AbstractSet * @return An empty enum set of the specified type. * @throws NullPointerException if {@code elementType} is null */ - public static > EnumSet noneOf(Class elementType) { + public static > @Modifiable @IteratorPolyMod EnumSet noneOf(Class elementType) { Enum[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); @@ -137,7 +141,7 @@ public static > EnumSet noneOf(Class elementType) { * @return An enum set containing all the elements in the specified type. * @throws NullPointerException if {@code elementType} is null */ - public static > EnumSet allOf(Class elementType) { + public static > @Modifiable @IteratorPolyMod EnumSet allOf(Class elementType) { EnumSet result = noneOf(elementType); result.addAll(); return result; @@ -149,8 +153,7 @@ public static > EnumSet allOf(Class elementType) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - abstract void addAll(); - + abstract void addAll(@Growable EnumSet this); /** * Creates an enum set with the same element type as the specified enum * set, initially containing the same elements (if any). @@ -161,7 +164,7 @@ public static > EnumSet allOf(Class elementType) { * @throws NullPointerException if {@code s} is null */ @SideEffectFree - public static > EnumSet copyOf(EnumSet s) { + public static > @Modifiable @IteratorPolyMod EnumSet copyOf(EnumSet s) { return s.clone(); } @@ -180,7 +183,7 @@ public static > EnumSet copyOf(EnumSet s) { * @throws NullPointerException if {@code c} is null */ @SideEffectFree - public static > EnumSet copyOf(Collection c) { + public static > @Modifiable @IteratorPolyMod EnumSet copyOf(Collection c) { if (c instanceof EnumSet) { return ((EnumSet)c).clone(); } else { @@ -205,7 +208,7 @@ public static > EnumSet copyOf(Collection c) { * @return The complement of the specified set in this set * @throws NullPointerException if {@code s} is null */ - public static > EnumSet complementOf(EnumSet s) { + public static > @Modifiable @IteratorPolyMod EnumSet complementOf(EnumSet s) { EnumSet result = copyOf(s); result.complement(); return result; @@ -226,7 +229,7 @@ public static > EnumSet complementOf(EnumSet s) { * @return an enum set initially containing the specified element */ @SideEffectFree - public static > EnumSet of(E e) { + public static > @Modifiable @IteratorPolyMod EnumSet of(E e) { EnumSet result = noneOf(e.getDeclaringClass()); result.add(e); return result; @@ -248,7 +251,7 @@ public static > EnumSet of(E e) { * @return an enum set initially containing the specified elements */ @SideEffectFree - public static > EnumSet of(E e1, E e2) { + public static > @Modifiable @IteratorPolyMod EnumSet of(E e1, E e2) { EnumSet result = noneOf(e1.getDeclaringClass()); result.add(e1); result.add(e2); @@ -272,7 +275,7 @@ public static > EnumSet of(E e1, E e2) { * @return an enum set initially containing the specified elements */ @SideEffectFree - public static > EnumSet of(E e1, E e2, E e3) { + public static > @Modifiable @IteratorPolyMod EnumSet of(E e1, E e2, E e3) { EnumSet result = noneOf(e1.getDeclaringClass()); result.add(e1); result.add(e2); @@ -298,7 +301,7 @@ public static > EnumSet of(E e1, E e2, E e3) { * @return an enum set initially containing the specified elements */ @SideEffectFree - public static > EnumSet of(E e1, E e2, E e3, E e4) { + public static > @Modifiable @IteratorPolyMod EnumSet of(E e1, E e2, E e3, E e4) { EnumSet result = noneOf(e1.getDeclaringClass()); result.add(e1); result.add(e2); @@ -325,7 +328,7 @@ public static > EnumSet of(E e1, E e2, E e3, E e4) { * @throws NullPointerException if any parameters are null * @return an enum set initially containing the specified elements */ - public static > EnumSet of(E e1, E e2, E e3, E e4, + public static > @Modifiable @IteratorPolyMod EnumSet of(E e1, E e2, E e3, E e4, E e5) { EnumSet result = noneOf(e1.getDeclaringClass()); @@ -353,7 +356,7 @@ public static > EnumSet of(E e1, E e2, E e3, E e4, */ @SafeVarargs @SideEffectFree - public static > EnumSet of(E first, E... rest) { + public static > @Modifiable @IteratorPolyMod EnumSet of(E first, E... rest) { EnumSet result = noneOf(first.getDeclaringClass()); result.add(first); for (E e : rest) @@ -375,7 +378,7 @@ public static > EnumSet of(E first, E... rest) { * @return an enum set initially containing all of the elements in the * range defined by the two specified endpoints */ - public static > EnumSet range(E from, E to) { + public static > @Modifiable @IteratorPolyMod EnumSet range(E from, E to) { if (from.compareTo(to) > 0) throw new IllegalArgumentException(from + " > " + to); EnumSet result = noneOf(from.getDeclaringClass()); @@ -389,18 +392,15 @@ public static > EnumSet range(E from, E to) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - abstract void addRange(E from, E to); - + abstract void addRange(@Growable EnumSet this, E from, E to); /** * Returns a copy of this set. * * @return a copy of this set */ @SuppressWarnings("unchecked") - @SideEffectsOnly("this") - @DoesNotUnrefineReceiver("modifiability") @SideEffectFree - public EnumSet clone() { + public @Modifiable @IteratorPolyMod EnumSet clone() { try { return (EnumSet) super.clone(); } catch(CloneNotSupportedException e) { @@ -413,8 +413,7 @@ public EnumSet clone() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - abstract void complement(); - + abstract void complement(@Growable @Shrinkable EnumSet this); /** * Throws an exception if e is not of the correct type for this enum set. */ diff --git a/src/java.base/share/classes/java/util/Enumeration.java b/src/java.base/share/classes/java/util/Enumeration.java index 570fd15fb70a..1aa0ac6c376a 100644 --- a/src/java.base/share/classes/java/util/Enumeration.java +++ b/src/java.base/share/classes/java/util/Enumeration.java @@ -26,6 +26,7 @@ package java.util; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.dataflow.qual.Pure; @@ -128,7 +129,7 @@ public interface Enumeration { * * @since 9 */ - default Iterator asIterator() { + default @Unmodifiable Iterator asIterator() { return new Iterator<>() { @Pure @EnsuresNonEmptyIf(result = true, expression = "this") diff --git a/src/java.base/share/classes/java/util/HashMap.java b/src/java.base/share/classes/java/util/HashMap.java index dcd5ec880cd6..9c4d780125f6 100644 --- a/src/java.base/share/classes/java/util/HashMap.java +++ b/src/java.base/share/classes/java/util/HashMap.java @@ -27,6 +27,16 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; @@ -155,7 +165,7 @@ * @see Hashtable * @since 1.2 */ -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public class HashMap extends AbstractMap implements Map, Cloneable, Serializable { @@ -469,7 +479,7 @@ static final int tableSizeFor(int cap) { * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ - public HashMap(@NonNegative int initialCapacity, float loadFactor) { + public @Modifiable HashMap(@NonNegative int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); @@ -493,7 +503,7 @@ public HashMap(@NonNegative int initialCapacity, float loadFactor) { * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */ - public HashMap(@NonNegative int initialCapacity) { + public @Modifiable HashMap(@NonNegative int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } @@ -501,7 +511,7 @@ public HashMap(@NonNegative int initialCapacity) { * Constructs an empty {@code HashMap} with the default initial capacity * (16) and the default load factor (0.75). */ - public HashMap() { + public @Modifiable HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } @@ -514,7 +524,7 @@ public HashMap() { * @param m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ - public @PolyNonEmpty HashMap(@PolyNonEmpty Map m) { + public @Modifiable @PolyNonEmpty HashMap(@PolyNonEmpty Map m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); } @@ -650,7 +660,7 @@ public boolean containsKey(@GuardSatisfied HashMap this, @GuardSatisfied @ @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(@GuardSatisfied HashMap this, K key, V value) { + public @Nullable V put(@Growable @Replaceable @GuardSatisfied HashMap this, K key, V value) { return putVal(hash(key), key, value, false, true); } @@ -826,7 +836,7 @@ else if ((e = tab[index = (n - 1) & hash]) != null) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putAll(@GuardSatisfied HashMap this, Map m) { + public void putAll(@Growable @Replaceable @GuardSatisfied HashMap this, Map m) { putMapEntries(m, true); } @@ -841,7 +851,7 @@ public void putAll(@GuardSatisfied HashMap this, Map this, @GuardSatisfied @Nullable @UnknownSignedness Object key) { + public @Nullable V remove(@Shrinkable @GuardSatisfied HashMap this, @GuardSatisfied @Nullable @UnknownSignedness Object key) { Node e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; @@ -904,7 +914,7 @@ else if (node == p) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied HashMap this) { + public void clear(@Shrinkable @GuardSatisfied HashMap this) { Node[] tab; modCount++; if ((tab = table) != null && size > 0) { @@ -953,7 +963,7 @@ public boolean containsValue(@GuardSatisfied HashMap this, @GuardSatisfied * @return a set view of the keys contained in this map */ @SideEffectFree - public Set<@KeyFor({"this"}) K> keySet(@GuardSatisfied HashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@KeyFor({"this"}) K> keySet(@PolyShrinkable @GuardSatisfied HashMap this) { Set ks = keySet; if (ks == null) { ks = new KeySet(); @@ -1030,14 +1040,14 @@ T[] valuesToArray(T[] a) { return a; } - final class KeySet extends AbstractSet { + final @Shrinkable class KeySet extends AbstractSet { @Pure public final @NonNegative int size() { return size; } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") public final void clear() { HashMap.this.clear(); } @SideEffectFree - public final Iterator iterator() { return new KeyIterator(); } + public final @Modifiable Iterator iterator() { return new KeyIterator(); } @Pure @EnsuresNonEmptyIf(result = true, expression = "this") public final boolean contains(@Nullable @UnknownSignedness Object o) { return containsKey(o); } @@ -1093,7 +1103,7 @@ public final void forEach(Consumer action) { * @return a view of the values contained in this map */ @SideEffectFree - public Collection values(@GuardSatisfied HashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable @GuardSatisfied HashMap this) { Collection vs = values; if (vs == null) { vs = new Values(); @@ -1102,14 +1112,14 @@ public Collection values(@GuardSatisfied HashMap this) { return vs; } - final class Values extends AbstractCollection { + final @Shrinkable class Values extends AbstractCollection { @Pure public final @NonNegative int size() { return size; } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") public final void clear() { HashMap.this.clear(); } @SideEffectFree - public final Iterator iterator() { return new ValueIterator(); } + public final @Modifiable Iterator iterator() { return new ValueIterator(); } @Pure @EnsuresNonEmptyIf(result = true, expression = "this") public final boolean contains(@Nullable @UnknownSignedness Object o) { return containsValue(o); } @@ -1160,19 +1170,19 @@ public final void forEach(Consumer action) { * @return a set view of the mappings contained in this map */ @SideEffectFree - public Set> entrySet(@GuardSatisfied HashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable @GuardSatisfied HashMap this) { Set> es; return (es = entrySet) == null ? (entrySet = new EntrySet()) : es; } - final class EntrySet extends AbstractSet> { + final @Shrinkable class EntrySet extends AbstractSet> { @Pure public final @NonNegative int size() { return size; } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") public final void clear() { HashMap.this.clear(); } @SideEffectFree - public final Iterator> iterator() { + public final @Modifiable Iterator> iterator() { return new EntryIterator(); } @Pure @@ -1219,7 +1229,9 @@ public final void forEach(Consumer> action) { @Override @Pure - public V getOrDefault(@GuardSatisfied @Nullable @UnknownSignedness Object key, V defaultValue) { + public @PolyModifiable V getOrDefault(HashMap this, + @GuardSatisfied @Nullable @UnknownSignedness Object key, + @PolyModifiable V defaultValue) { Node e; return (e = getNode(key)) == null ? defaultValue : e.value; } @@ -1228,21 +1240,21 @@ public V getOrDefault(@GuardSatisfied @Nullable @UnknownSignedness Object key, V @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(@Growable HashMap this, K key, V value) { return putVal(hash(key), key, value, true, true); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @Nullable @UnknownSignedness Object key, @GuardSatisfied @Nullable @UnknownSignedness Object value) { + public boolean remove(@Shrinkable HashMap this, @GuardSatisfied @Nullable @UnknownSignedness Object key, @GuardSatisfied @Nullable @UnknownSignedness Object value) { return removeNode(hash(key), key, value, true, true) != null; } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean replace(K key, V oldValue, V newValue) { + public boolean replace(@Replaceable HashMap this, K key, V oldValue, V newValue) { Node e; V v; if ((e = getNode(key)) != null && ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) { @@ -1256,7 +1268,7 @@ public boolean replace(K key, V oldValue, V newValue) { @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V replace(K key, V value) { + public @Nullable V replace(@Replaceable HashMap this, K key, V value) { Node e; if ((e = getNode(key)) != null) { V oldValue = e.value; @@ -1279,7 +1291,7 @@ public boolean replace(K key, V oldValue, V newValue) { */ @Override @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V computeIfAbsent(K key, + public @PolyNull V computeIfAbsent(@Growable HashMap this, K key, Function mappingFunction) { if (mappingFunction == null) throw new NullPointerException(); @@ -1346,7 +1358,7 @@ else if (t != null) */ @Override @DoesNotUnrefineReceiver("modifiability") - public @Nullable V computeIfPresent(K key, + public @Nullable V computeIfPresent(@Shrinkable @Replaceable HashMap this, K key, BiFunction remappingFunction) { if (remappingFunction == null) throw new NullPointerException(); @@ -1381,7 +1393,7 @@ else if (t != null) */ @Override @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V compute(K key, + public @PolyNull V compute(@Modifiable HashMap this, K key, BiFunction remappingFunction) { if (remappingFunction == null) throw new NullPointerException(); @@ -1447,7 +1459,7 @@ else if (v != null) { */ @Override @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V merge(K key, @NonNull V value, + public @PolyNull V merge(@Modifiable HashMap this, K key, @NonNull V value, BiFunction remappingFunction) { if (value == null || remappingFunction == null) throw new NullPointerException(); @@ -1526,7 +1538,7 @@ public void forEach(BiConsumer action) { @Override @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(BiFunction function) { + public void replaceAll(@Replaceable HashMap this, BiFunction function) { Node[] tab; if (function == null) throw new NullPointerException(); @@ -1554,7 +1566,7 @@ public void replaceAll(BiFunction function) { @SideEffectFree @SuppressWarnings("unchecked") @Override - public Object clone(@GuardSatisfied HashMap this) { + public @Modifiable Object clone(@GuardSatisfied HashMap this) { HashMap result; try { result = (HashMap)super.clone(); @@ -1671,7 +1683,7 @@ static void putLoadFactor(HashMap map, float lf) { /* ------------------------------------------------------------ */ // iterators - abstract class HashIterator { + abstract @Modifiable class HashIterator { Node next; // next entry to return Node current; // current entry int expectedModCount; // for fast-fail @@ -1721,21 +1733,21 @@ public final void remove() { } } - final class KeyIterator extends HashIterator + final @Modifiable class KeyIterator extends HashIterator implements Iterator { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") public final K next(@NonEmpty KeyIterator this) { return nextNode().key; } } - final class ValueIterator extends HashIterator + final @Modifiable class ValueIterator extends HashIterator implements Iterator { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") public final V next(@NonEmpty ValueIterator this) { return nextNode().value; } } - final class EntryIterator extends HashIterator + final @Modifiable class EntryIterator extends HashIterator implements Iterator> { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") diff --git a/src/java.base/share/classes/java/util/HashSet.java b/src/java.base/share/classes/java/util/HashSet.java index 3f8233e35de0..a0cc9cc33604 100644 --- a/src/java.base/share/classes/java/util/HashSet.java +++ b/src/java.base/share/classes/java/util/HashSet.java @@ -28,6 +28,12 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -103,7 +109,7 @@ * @since 1.2 */ -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public class HashSet extends AbstractSet implements Set, Cloneable, java.io.Serializable @@ -120,7 +126,7 @@ public class HashSet * Constructs a new, empty set; the backing {@code HashMap} instance has * default initial capacity (16) and load factor (0.75). */ - public HashSet() { + public @Modifiable @IteratorPolyMod HashSet() { map = new HashMap<>(); } @@ -133,7 +139,7 @@ public HashSet() { * @param c the collection whose elements are to be placed into this set * @throws NullPointerException if the specified collection is null */ - public @PolyNonEmpty HashSet(@PolyNonEmpty Collection c) { + public @Modifiable @IteratorPolyMod @PolyNonEmpty HashSet(@PolyNonEmpty Collection c) { map = HashMap.newHashMap(Math.max(c.size(), 12)); addAll(c); } @@ -151,7 +157,7 @@ public HashSet() { * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive */ - public HashSet(@NonNegative int initialCapacity, float loadFactor) { + public @Modifiable @IteratorPolyMod HashSet(@NonNegative int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } @@ -167,7 +173,7 @@ public HashSet(@NonNegative int initialCapacity, float loadFactor) { * @throws IllegalArgumentException if the initial capacity is less * than zero */ - public HashSet(@NonNegative int initialCapacity) { + public @Modifiable @IteratorPolyMod HashSet(@NonNegative int initialCapacity) { map = new HashMap<>(initialCapacity); } @@ -184,7 +190,7 @@ public HashSet(@NonNegative int initialCapacity) { * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive */ - HashSet(int initialCapacity, float loadFactor, boolean dummy) { + @Modifiable @IteratorPolyMod HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); } @@ -196,7 +202,7 @@ public HashSet(@NonNegative int initialCapacity) { * @see ConcurrentModificationException */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty HashSet this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty HashSet this) { return map.keySet().iterator(); } @@ -251,7 +257,7 @@ public boolean contains(@GuardSatisfied HashSet this, @GuardSatisfied @Nullab @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(@GuardSatisfied HashSet this, E e) { + public boolean add(@Growable @GuardSatisfied HashSet this, E e) { return map.put(e, PRESENT)==null; } @@ -269,7 +275,7 @@ public boolean add(@GuardSatisfied HashSet this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied HashSet this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @GuardSatisfied HashSet this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return map.remove(o)==PRESENT; } @@ -279,7 +285,7 @@ public boolean remove(@GuardSatisfied HashSet this, @GuardSatisfied @Nullable */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied HashSet this) { + public void clear(@Shrinkable @GuardSatisfied HashSet this) { map.clear(); } @@ -291,7 +297,7 @@ public void clear(@GuardSatisfied HashSet this) { */ @SideEffectFree @SuppressWarnings("unchecked") - public Object clone(@GuardSatisfied HashSet this) { + public @Modifiable @IteratorPolyMod Object clone(@GuardSatisfied HashSet this) { try { HashSet newSet = (HashSet) super.clone(); newSet.map = (HashMap) map.clone(); @@ -399,6 +405,7 @@ private void readObject(java.io.ObjectInputStream s) * @since 1.8 */ @SideEffectFree + @DoesNotUnrefineReceiver("modifiability") public Spliterator spliterator() { return new HashMap.KeySpliterator<>(map, 0, -1, 0, 0); } diff --git a/src/java.base/share/classes/java/util/Hashtable.java b/src/java.base/share/classes/java/util/Hashtable.java index 71d1c0364373..d8652dd908d3 100644 --- a/src/java.base/share/classes/java/util/Hashtable.java +++ b/src/java.base/share/classes/java/util/Hashtable.java @@ -27,6 +27,14 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -152,7 +160,7 @@ * @since 1.0 */ @CFComment({"lock: This collection can only contain nonnull values"}) -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public class Hashtable extends Dictionary implements Map, Cloneable, java.io.Serializable { @@ -204,7 +212,7 @@ public class Hashtable * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive. */ - public Hashtable(@NonNegative int initialCapacity, float loadFactor) { + public @Modifiable Hashtable(@NonNegative int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); @@ -226,7 +234,7 @@ public Hashtable(@NonNegative int initialCapacity, float loadFactor) { * @throws IllegalArgumentException if the initial capacity is less * than zero. */ - public Hashtable(@NonNegative int initialCapacity) { + public @Modifiable Hashtable(@NonNegative int initialCapacity) { this(initialCapacity, 0.75f); } @@ -234,7 +242,7 @@ public Hashtable(@NonNegative int initialCapacity) { * Constructs a new, empty hashtable with a default initial capacity (11) * and load factor (0.75). */ - public Hashtable() { + public @Modifiable Hashtable() { this(11, 0.75f); } @@ -247,7 +255,7 @@ public Hashtable() { * @throws NullPointerException if the specified map is null. * @since 1.2 */ - public Hashtable(Map t) { + public @Modifiable Hashtable(Map t) { this(Math.max(2*t.size(), 11), 0.75f); putAll(t); } @@ -258,7 +266,7 @@ public Hashtable(Map t) { * * @param dummy a dummy parameter */ - Hashtable(Void dummy) {} + @Modifiable Hashtable(Void dummy) {} /** * Returns the number of keys in this hashtable. @@ -505,7 +513,7 @@ private void addEntry(int hash, K key, V value, int index) { @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized @Nullable V put(@GuardSatisfied Hashtable this, K key, V value) { + public synchronized @Nullable V put(@Growable @Replaceable @GuardSatisfied Hashtable this, K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); @@ -540,7 +548,7 @@ private void addEntry(int hash, K key, V value, int index) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized @Nullable V remove(@GuardSatisfied Hashtable this, @GuardSatisfied @UnknownSignedness Object key) { + public synchronized @Nullable V remove(@Shrinkable @GuardSatisfied Hashtable this, @GuardSatisfied @UnknownSignedness Object key) { Entry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @@ -574,7 +582,7 @@ private void addEntry(int hash, K key, V value, int index) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized void putAll(@GuardSatisfied Hashtable this, Map t) { + public synchronized void putAll(@Growable @Replaceable @GuardSatisfied Hashtable this, Map t) { for (Map.Entry e : t.entrySet()) put(e.getKey(), e.getValue()); } @@ -584,7 +592,7 @@ public synchronized void putAll(@GuardSatisfied Hashtable this, Map this) { + public synchronized void clear(@Shrinkable @GuardSatisfied Hashtable this) { Entry tab[] = table; for (int index = tab.length; --index >= 0; ) tab[index] = null; @@ -600,7 +608,7 @@ public synchronized void clear(@GuardSatisfied Hashtable this) { * @return a clone of the hashtable */ @SideEffectFree - public synchronized Object clone(@GuardSatisfied Hashtable this) { + public synchronized @Modifiable Object clone(@GuardSatisfied Hashtable this) { Hashtable t = cloneHashtable(); t.table = new Entry[table.length]; for (int i = table.length ; i-- > 0 ; ) { @@ -702,7 +710,7 @@ private Iterator getIterator(int type) { * @since 1.2 */ @SideEffectFree - public Set<@KeyFor({"this"}) K> keySet(@GuardSatisfied Hashtable this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@KeyFor({"this"}) K> keySet(@PolyShrinkable @GuardSatisfied Hashtable this) { if (keySet == null) keySet = Collections.synchronizedSet(new KeySet(), this); return keySet; @@ -751,7 +759,7 @@ public void clear() { * @since 1.2 */ @SideEffectFree - public Set> entrySet(@GuardSatisfied Hashtable this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable @GuardSatisfied Hashtable this) { if (entrySet==null) entrySet = Collections.synchronizedSet(new EntrySet(), this); return entrySet; @@ -842,7 +850,7 @@ public void clear() { * @since 1.2 */ @SideEffectFree - public Collection values(@GuardSatisfied Hashtable this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable @GuardSatisfied Hashtable this) { if (values==null) values = Collections.synchronizedCollection(new ValueCollection(), this); @@ -949,7 +957,9 @@ public synchronized int hashCode(@GuardSatisfied Hashtable this) { @Override @Pure - public synchronized V getOrDefault(@GuardSatisfied @UnknownSignedness Object key, V defaultValue) { + public synchronized @PolyModifiable V getOrDefault(Hashtable this, + @GuardSatisfied @UnknownSignedness Object key, + @PolyModifiable V defaultValue) { V result = get(key); return (null == result) ? defaultValue : result; } @@ -978,7 +988,7 @@ public synchronized void forEach(BiConsumer action) { @SuppressWarnings("unchecked") @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized void replaceAll(BiFunction function) { + public synchronized void replaceAll(@Replaceable Hashtable this, BiFunction function) { Objects.requireNonNull(function); // explicit check required in case // table is empty. final int expectedModCount = modCount; @@ -1001,7 +1011,7 @@ public synchronized void replaceAll(BiFunction this, K key, V value) { Objects.requireNonNull(value); // Makes sure the key is not already in the hashtable. @@ -1027,7 +1037,7 @@ public synchronized void replaceAll(BiFunction this, @GuardSatisfied @UnknownSignedness Object key, @GuardSatisfied @UnknownSignedness Object value) { Objects.requireNonNull(value); Entry tab[] = table; @@ -1054,7 +1064,7 @@ public synchronized boolean remove(@GuardSatisfied @UnknownSignedness Object key @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized boolean replace(K key, V oldValue, V newValue) { + public synchronized boolean replace(@Replaceable Hashtable this, K key, V oldValue, V newValue) { Objects.requireNonNull(oldValue); Objects.requireNonNull(newValue); Entry tab[] = table; @@ -1078,7 +1088,7 @@ public synchronized boolean replace(K key, V oldValue, V newValue) { @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized @Nullable V replace(K key, V value) { + public synchronized @Nullable V replace(@Replaceable Hashtable this, K key, V value) { Objects.requireNonNull(value); Entry tab[] = table; int hash = key.hashCode(); @@ -1107,7 +1117,7 @@ public synchronized boolean replace(K key, V oldValue, V newValue) { */ @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized @PolyNull V computeIfAbsent(K key, Function mappingFunction) { + public synchronized @PolyNull V computeIfAbsent(@Growable Hashtable this, K key, Function mappingFunction) { Objects.requireNonNull(mappingFunction); Entry tab[] = table; @@ -1144,7 +1154,7 @@ public synchronized boolean replace(K key, V oldValue, V newValue) { */ @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized @Nullable V computeIfPresent(K key, BiFunction remappingFunction) { + public synchronized @Nullable V computeIfPresent(@Shrinkable @Replaceable Hashtable this, K key, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); Entry tab[] = table; @@ -1187,7 +1197,7 @@ public synchronized boolean replace(K key, V oldValue, V newValue) { */ @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized @PolyNull V compute(K key, BiFunction remappingFunction) { + public synchronized @PolyNull V compute(@Modifiable Hashtable this, K key, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); Entry tab[] = table; @@ -1239,7 +1249,7 @@ public synchronized boolean replace(K key, V oldValue, V newValue) { */ @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized @PolyNull V merge(K key, @NonNull V value, BiFunction remappingFunction) { + public synchronized @PolyNull V merge(@Modifiable Hashtable this, K key, @NonNull V value, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); Entry tab[] = table; diff --git a/src/java.base/share/classes/java/util/IdentityHashMap.java b/src/java.base/share/classes/java/util/IdentityHashMap.java index 8f667b861a5c..48cd25d628a8 100644 --- a/src/java.base/share/classes/java/util/IdentityHashMap.java +++ b/src/java.base/share/classes/java/util/IdentityHashMap.java @@ -27,6 +27,14 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nullness.qual.EnsuresKeyFor; @@ -165,7 +173,7 @@ */ @CFComment({"lock/nullness: This collection can only contain null values"}) -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public class IdentityHashMap extends AbstractMap implements Map, java.io.Serializable, Cloneable @@ -237,7 +245,7 @@ static final Object unmaskNull(Object key) { * Constructs a new, empty identity hash map with a default expected * maximum size (21). */ - public IdentityHashMap() { + public @Modifiable IdentityHashMap() { init(DEFAULT_CAPACITY); } @@ -251,7 +259,7 @@ public IdentityHashMap() { * @throws IllegalArgumentException if {@code expectedMaxSize} is negative */ @SideEffectFree - public IdentityHashMap(@NonNegative int expectedMaxSize) { + public @Modifiable IdentityHashMap(@NonNegative int expectedMaxSize) { if (expectedMaxSize < 0) throw new IllegalArgumentException("expectedMaxSize is negative: " + expectedMaxSize); @@ -293,7 +301,7 @@ private void init(int initCapacity) { * @param m the map whose mappings are to be placed into this map * @throws NullPointerException if the specified map is null */ - public IdentityHashMap(Map m) { + public @Modifiable IdentityHashMap(Map m) { // Allow for a bit of growth this((int) ((1 + m.size()) * 1.1)); putAll(m); @@ -461,7 +469,7 @@ private boolean containsMapping(@UnknownSignedness @GuardSatisfied @Nullable Obj @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(@GuardSatisfied IdentityHashMap this, K key, V value) { + public @Nullable V put(@Growable @Replaceable @GuardSatisfied IdentityHashMap this, K key, V value) { final Object k = maskNull(key); retryAfterResize: for (;;) { @@ -544,7 +552,7 @@ private boolean resize(int newCapacity) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putAll(@GuardSatisfied IdentityHashMap this, Map m) { + public void putAll(@Growable @Replaceable @GuardSatisfied IdentityHashMap this, Map m) { int n = m.size(); if (n == 0) return; @@ -568,7 +576,7 @@ public void putAll(@GuardSatisfied IdentityHashMap this, Map this, @GuardSatisfied @Nullable @UnknownSignedness Object key) { + public @Nullable V remove(@Shrinkable @GuardSatisfied IdentityHashMap this, @GuardSatisfied @Nullable @UnknownSignedness Object key) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; @@ -666,7 +674,7 @@ private void closeDeletion(int d) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied IdentityHashMap this) { + public void clear(@Shrinkable @GuardSatisfied IdentityHashMap this) { modCount++; Object[] tab = table; for (int i = 0; i < tab.length; i++) @@ -759,7 +767,7 @@ public int hashCode(@GuardSatisfied IdentityHashMap this) { * @return a shallow copy of this map */ @SideEffectFree - public Object clone(@GuardSatisfied IdentityHashMap this) { + public @Modifiable Object clone(@GuardSatisfied IdentityHashMap this) { try { IdentityHashMap m = (IdentityHashMap) super.clone(); m.entrySet = null; @@ -1042,7 +1050,7 @@ private void checkIndexForEntryUse() { * @see System#identityHashCode(Object) */ @SideEffectFree - public Set<@KeyFor({"this"}) K> keySet(@GuardSatisfied IdentityHashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@KeyFor({"this"}) K> keySet(@PolyShrinkable @GuardSatisfied IdentityHashMap this) { Set ks = keySet; if (ks == null) { ks = new KeySet(); @@ -1162,7 +1170,7 @@ public Spliterator spliterator() { * {@code containsAll} methods. */ @SideEffectFree - public Collection values(@GuardSatisfied IdentityHashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable @GuardSatisfied IdentityHashMap this) { Collection vs = values; if (vs == null) { vs = new Values(); @@ -1280,7 +1288,7 @@ public Spliterator spliterator() { * @return a set view of the identity-mappings contained in this map */ @SideEffectFree - public Set> entrySet(@GuardSatisfied IdentityHashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable @GuardSatisfied IdentityHashMap this) { Set> es = entrySet; if (es != null) return es; @@ -1290,7 +1298,7 @@ public Spliterator spliterator() { private class EntrySet extends AbstractSet> { @SideEffectFree - public Iterator> iterator() { + public @Modifiable Iterator> iterator() { return new EntryIterator(); } @Pure @@ -1481,7 +1489,7 @@ public void forEach(BiConsumer action) { @SuppressWarnings("unchecked") @Override @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(BiFunction function) { + public void replaceAll(@Replaceable IdentityHashMap this, BiFunction function) { Objects.requireNonNull(function); int expectedModCount = modCount; @@ -1510,7 +1518,7 @@ public void replaceAll(BiFunction function) { @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(Object key, Object value) { + public boolean remove(@Shrinkable IdentityHashMap this, Object key, Object value) { return removeMapping(key, value); } @@ -1526,7 +1534,7 @@ public boolean remove(Object key, Object value) { @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean replace(K key, V oldValue, V newValue) { + public boolean replace(@Replaceable IdentityHashMap this, K key, V oldValue, V newValue) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; diff --git a/src/java.base/share/classes/java/util/ImmutableCollections.java b/src/java.base/share/classes/java/util/ImmutableCollections.java index 4b1f660e037a..c98bfb79bf72 100644 --- a/src/java.base/share/classes/java/util/ImmutableCollections.java +++ b/src/java.base/share/classes/java/util/ImmutableCollections.java @@ -32,6 +32,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.PolyNull; import org.checkerframework.checker.signedness.qual.UnknownSignedness; +import org.checkerframework.common.value.qual.StaticallyExecutable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; @@ -528,6 +529,7 @@ private boolean allowNulls() { @Override @Pure + @StaticallyExecutable public int indexOf(Object o) { if (!allowNulls() && o == null) { throw new NullPointerException(); @@ -542,6 +544,7 @@ public int indexOf(Object o) { @Override @Pure + @StaticallyExecutable public int lastIndexOf(Object o) { if (!allowNulls() && o == null) { throw new NullPointerException(); @@ -555,6 +558,7 @@ public int lastIndexOf(Object o) { } @Override + @SideEffectFree public Object[] toArray() { Object[] array = new Object[size]; for (int i = 0; i < size; i++) { @@ -628,6 +632,7 @@ public E get(int index) { @Override @Pure + @StaticallyExecutable public int indexOf(Object o) { Objects.requireNonNull(o); if (o.equals(e0)) { @@ -641,6 +646,7 @@ public int indexOf(Object o) { @Override @Pure + @StaticallyExecutable public int lastIndexOf(Object o) { Objects.requireNonNull(o); if (e1 != EMPTY && o.equals(e1)) { @@ -667,6 +673,7 @@ private Object writeReplace() { } @Override + @SideEffectFree public Object[] toArray() { if (e1 == EMPTY) { return new Object[] { e0 }; @@ -738,6 +745,7 @@ private Object writeReplace() { } @Override + @SideEffectFree public Object[] toArray() { return Arrays.copyOf(elements, elements.length); } @@ -759,6 +767,7 @@ public Object[] toArray() { @Override @Pure + @StaticallyExecutable public int indexOf(Object o) { if (!allowNulls && o == null) { throw new NullPointerException(); @@ -774,6 +783,7 @@ public int indexOf(Object o) { @Override @Pure + @StaticallyExecutable public int lastIndexOf(Object o) { if (!allowNulls && o == null) { throw new NullPointerException(); @@ -917,6 +927,7 @@ private Object writeReplace() { } @Override + @SideEffectFree public Object[] toArray() { if (e1 == EMPTY) { return new Object[] { e0 }; @@ -1106,6 +1117,7 @@ private Object writeReplace() { } @Override + @SideEffectFree public Object[] toArray() { Object[] array = new Object[size]; Iterator it = iterator(); diff --git a/src/java.base/share/classes/java/util/Iterator.java b/src/java.base/share/classes/java/util/Iterator.java index 04e99e4cedc4..004538e40cd7 100644 --- a/src/java.base/share/classes/java/util/Iterator.java +++ b/src/java.base/share/classes/java/util/Iterator.java @@ -27,6 +27,7 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.dataflow.qual.Pure; @@ -69,7 +70,7 @@ @CFComment({"nullness: This @Covariant annotation is sound, but it would not be sound on", "ListIterator (a subclass of Iterator), which supports a set operation." }) -@AnnotatedFor({"lock", "nullness"}) +@AnnotatedFor({"lock", "nullness", "modifiability"}) @Covariant({0}) public interface Iterator { /** @@ -120,7 +121,7 @@ public interface Iterator { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default void remove(@GuardSatisfied @CanShrink Iterator this) { + default void remove(@Shrinkable @GuardSatisfied @CanShrink Iterator this) { throw new UnsupportedOperationException("remove"); } diff --git a/src/java.base/share/classes/java/util/LinkedHashMap.java b/src/java.base/share/classes/java/util/LinkedHashMap.java index 102d153cbb97..b5d8abf8574c 100644 --- a/src/java.base/share/classes/java/util/LinkedHashMap.java +++ b/src/java.base/share/classes/java/util/LinkedHashMap.java @@ -27,6 +27,16 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.SeqUngrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; @@ -190,7 +200,7 @@ * @see Hashtable * @since 1.4 */ -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public class LinkedHashMap extends HashMap implements SequencedMap @@ -409,7 +419,7 @@ void afterNodeAccess(Node e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putFirst(K k, V v) { + public @Nullable V putFirst(@SeqGrowable LinkedHashMap this, K k, V v) { try { putMode = PUT_FIRST; return this.put(k, v); @@ -428,7 +438,7 @@ void afterNodeAccess(Node e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putLast(K k, V v) { + public @Nullable V putLast(@SeqGrowable LinkedHashMap this, K k, V v) { try { putMode = PUT_LAST; return this.put(k, v); @@ -457,7 +467,7 @@ void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException { * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ - public LinkedHashMap(@NonNegative int initialCapacity, float loadFactor) { + public @Modifiable LinkedHashMap(@NonNegative int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; } @@ -473,7 +483,7 @@ public LinkedHashMap(@NonNegative int initialCapacity, float loadFactor) { * @param initialCapacity the initial capacity * @throws IllegalArgumentException if the initial capacity is negative */ - public LinkedHashMap(@NonNegative int initialCapacity) { + public @Modifiable LinkedHashMap(@NonNegative int initialCapacity) { super(initialCapacity); accessOrder = false; } @@ -482,7 +492,7 @@ public LinkedHashMap(@NonNegative int initialCapacity) { * Constructs an empty insertion-ordered {@code LinkedHashMap} instance * with the default initial capacity (16) and load factor (0.75). */ - public LinkedHashMap() { + public @Modifiable LinkedHashMap() { super(); accessOrder = false; } @@ -496,7 +506,7 @@ public LinkedHashMap() { * @param m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ - public @PolyNonEmpty LinkedHashMap(@PolyNonEmpty Map m) { + public @Modifiable @PolyNonEmpty LinkedHashMap(@PolyNonEmpty Map m) { super(); accessOrder = false; putMapEntries(m, false); @@ -513,7 +523,7 @@ public LinkedHashMap() { * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ - public LinkedHashMap(@NonNegative int initialCapacity, + public @Modifiable LinkedHashMap(@NonNegative int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); @@ -570,7 +580,9 @@ public boolean containsValue(@GuardSatisfied LinkedHashMap this, @GuardSat */ @CFComment("`getOrDefault()` is not strictly pure: if `accessOrder==true`, it changes the access order") @Pure - public V getOrDefault(@Nullable Object key, V defaultValue) { + public @PolyModifiable V getOrDefault(LinkedHashMap this, + @Nullable Object key, + @PolyModifiable V defaultValue) { Node e; if ((e = getNode(key)) == null) return defaultValue; @@ -584,7 +596,7 @@ public V getOrDefault(@Nullable Object key, V defaultValue) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied LinkedHashMap this) { + public void clear(@Shrinkable @GuardSatisfied LinkedHashMap this) { super.clear(); head = tail = null; } @@ -654,7 +666,7 @@ protected boolean removeEldestEntry(@GuardSatisfied LinkedHashMap this, Ma * @return a set view of the keys contained in this map */ @SideEffectFree - public Set<@KeyFor({"this"}) K> keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@KeyFor({"this"}) K> keySet(@PolyShrinkable LinkedHashMap this) { return sequencedKeySet(); } @@ -667,7 +679,7 @@ protected boolean removeEldestEntry(@GuardSatisfied LinkedHashMap this, Ma * @return {@inheritDoc} * @since 21 */ - public SequencedSet sequencedKeySet() { + public @PolyShrinkable @Ungrowable SequencedSet sequencedKeySet(@PolyShrinkable LinkedHashMap this) { Set ks = keySet; if (ks == null) { SequencedSet sks = new LinkedKeySet(false); @@ -721,6 +733,7 @@ final T[] valuesToArray(T[] a, boolean reversed) { return a; } + @SeqUngrowable final class LinkedKeySet extends AbstractSet implements SequencedSet { final boolean reversed; LinkedKeySet(boolean reversed) { this.reversed = reversed; } @@ -730,7 +743,7 @@ final class LinkedKeySet extends AbstractSet implements SequencedSet { @DoesNotUnrefineReceiver("modifiability") public final void clear() { LinkedHashMap.this.clear(); } @SideEffectFree - public final Iterator iterator() { + public final @Modifiable Iterator iterator() { return new LinkedKeyIterator(reversed); } @Pure @@ -822,7 +835,7 @@ public SequencedSet reversed() { * @return a view of the values contained in this map */ @SideEffectFree - public Collection values() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable LinkedHashMap this) { return sequencedValues(); } @@ -835,7 +848,7 @@ public Collection values() { * @return {@inheritDoc} * @since 21 */ - public SequencedCollection sequencedValues() { + public @PolyShrinkable @Ungrowable SequencedCollection sequencedValues(@PolyShrinkable LinkedHashMap this) { Collection vs = values; if (vs == null) { SequencedCollection svs = new LinkedValues(false); @@ -848,6 +861,7 @@ public SequencedCollection sequencedValues() { } } + @SeqUngrowable final class LinkedValues extends AbstractCollection implements SequencedCollection { final boolean reversed; LinkedValues(boolean reversed) { this.reversed = reversed; } @@ -944,7 +958,7 @@ public SequencedCollection reversed() { * @return a set view of the mappings contained in this map */ @SideEffectFree - public Set> entrySet(@GuardSatisfied LinkedHashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable @GuardSatisfied LinkedHashMap this) { return sequencedEntrySet(); } @@ -957,7 +971,7 @@ public SequencedCollection reversed() { * @return {@inheritDoc} * @since 21 */ - public SequencedSet> sequencedEntrySet() { + public @PolyShrinkable @Ungrowable SequencedSet> sequencedEntrySet(@PolyModifiable LinkedHashMap this) { Set> es = entrySet; if (es == null) { SequencedSet> ses = new LinkedEntrySet(false); @@ -970,6 +984,7 @@ public SequencedSet> sequencedEntrySet() { } } + @SeqUngrowable final class LinkedEntrySet extends AbstractSet> implements SequencedSet> { final boolean reversed; @@ -1073,7 +1088,7 @@ public void forEach(BiConsumer action) { } @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(BiFunction function) { + public void replaceAll(@Replaceable LinkedHashMap this, BiFunction function) { if (function == null) throw new NullPointerException(); int mc = modCount; @@ -1085,7 +1100,7 @@ public void replaceAll(BiFunction function) { // Iterators - abstract class LinkedHashIterator { + abstract @Modifiable class LinkedHashIterator { LinkedHashMap.Entry next; LinkedHashMap.Entry current; int expectedModCount; @@ -1130,7 +1145,7 @@ public final void remove() { } } - final class LinkedKeyIterator extends LinkedHashIterator + final @Modifiable class LinkedKeyIterator extends LinkedHashIterator implements Iterator { LinkedKeyIterator(boolean reversed) { super(reversed); } @SideEffectsOnly("this") @@ -1138,7 +1153,7 @@ final class LinkedKeyIterator extends LinkedHashIterator public final K next(@NonEmpty LinkedKeyIterator this) { return nextNode().getKey(); } } - final class LinkedValueIterator extends LinkedHashIterator + final @Modifiable class LinkedValueIterator extends LinkedHashIterator implements Iterator { LinkedValueIterator(boolean reversed) { super(reversed); } @SideEffectsOnly("this") @@ -1146,7 +1161,7 @@ final class LinkedValueIterator extends LinkedHashIterator public final V next(@NonEmpty LinkedValueIterator this) { return nextNode().value; } } - final class LinkedEntryIterator extends LinkedHashIterator + final @Modifiable class LinkedEntryIterator extends LinkedHashIterator implements Iterator> { LinkedEntryIterator(boolean reversed) { super(reversed); } @SideEffectsOnly("this") @@ -1167,7 +1182,7 @@ final class LinkedEntryIterator extends LinkedHashIterator * @throws IllegalArgumentException if numMappings is negative * @since 19 */ - public static LinkedHashMap newLinkedHashMap(int numMappings) { + public static @Modifiable LinkedHashMap newLinkedHashMap(int numMappings) { if (numMappings < 0) { throw new IllegalArgumentException("Negative number of mappings: " + numMappings); } @@ -1242,46 +1257,48 @@ public boolean containsValue(Object value) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(K key, V value) { + public @Nullable V put(@Growable @Replaceable ReversedLinkedHashMapView this, K key, V value) { return base.put(key, value); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V remove(@Nullable Object key) { + public @Nullable V remove(@Shrinkable ReversedLinkedHashMapView this, @Nullable Object key) { return base.remove(key); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putAll(Map m) { + public void putAll(@Growable @Replaceable ReversedLinkedHashMapView this, Map m) { base.putAll(m); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear() { + public void clear(@Shrinkable ReversedLinkedHashMapView this) { base.clear(); } @SideEffectFree - public Set keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set keySet(@PolyShrinkable ReversedLinkedHashMapView this) { return base.sequencedKeySet().reversed(); } @SideEffectFree - public Collection values() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable ReversedLinkedHashMapView this) { return base.sequencedValues().reversed(); } @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable ReversedLinkedHashMapView this) { return base.sequencedEntrySet().reversed(); } @CFComment("`getOrDefault()` is not strictly pure: if `accessOrder==true`, it changes the access order") @Pure - public V getOrDefault(@Nullable Object key, V defaultValue) { + public @PolyModifiable V getOrDefault(ReversedLinkedHashMapView this, + @Nullable Object key, + @PolyModifiable V defaultValue) { return base.getOrDefault(key, defaultValue); } @@ -1297,7 +1314,7 @@ public void forEach(BiConsumer action) { } @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(BiFunction function) { + public void replaceAll(@Replaceable ReversedLinkedHashMapView this, BiFunction function) { if (function == null) throw new NullPointerException(); int mc = base.modCount; @@ -1309,45 +1326,45 @@ public void replaceAll(BiFunction function) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(@Growable ReversedLinkedHashMapView this, K key, V value) { return base.putIfAbsent(key, value); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(Object key, Object value) { + public boolean remove(@Shrinkable ReversedLinkedHashMapView this, Object key, Object value) { return base.remove(key, value); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean replace(K key, V oldValue, V newValue) { + public boolean replace(@Replaceable ReversedLinkedHashMapView this, K key, V oldValue, V newValue) { return base.replace(key, oldValue, newValue); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V replace(K key, V value) { + public @Nullable V replace(@Replaceable ReversedLinkedHashMapView this, K key, V value) { return base.replace(key, value); } @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V computeIfAbsent(K key, Function mappingFunction) { + public @PolyNull V computeIfAbsent(@Growable ReversedLinkedHashMapView this, K key, Function mappingFunction) { return base.computeIfAbsent(key, mappingFunction); } @DoesNotUnrefineReceiver("modifiability") - public @Nullable V computeIfPresent(K key, BiFunction remappingFunction) { + public @Nullable V computeIfPresent(@Shrinkable @Replaceable ReversedLinkedHashMapView this, K key, BiFunction remappingFunction) { return base.computeIfPresent(key, remappingFunction); } @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V compute(K key, BiFunction remappingFunction) { + public @PolyNull V compute(@Modifiable ReversedLinkedHashMapView this, K key, BiFunction remappingFunction) { return base.compute(key, remappingFunction); } @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V merge(K key, @NonNull V value, BiFunction remappingFunction) { + public @PolyNull V merge(@Modifiable ReversedLinkedHashMapView this, K key, @NonNull V value, BiFunction remappingFunction) { return base.merge(key, value, remappingFunction); } @@ -1358,35 +1375,37 @@ public SequencedMap reversed() { return base; } + @Pure public @Nullable Entry firstEntry() { return base.lastEntry(); } + @Pure public @Nullable Entry lastEntry() { return base.firstEntry(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry(@Shrinkable ReversedLinkedHashMapView this) { return base.pollLastEntry(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable Entry pollLastEntry() { + public @Nullable Entry pollLastEntry(@Shrinkable ReversedLinkedHashMapView this) { return base.pollFirstEntry(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putFirst(K k, V v) { + public @Nullable V putFirst(@SeqGrowable ReversedLinkedHashMapView this, K k, V v) { return base.putLast(k, v); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putLast(K k, V v) { + public @Nullable V putLast(@SeqGrowable ReversedLinkedHashMapView this, K k, V v) { return base.putFirst(k, v); } } diff --git a/src/java.base/share/classes/java/util/LinkedHashSet.java b/src/java.base/share/classes/java/util/LinkedHashSet.java index b8730d9183ae..2f47876c76a3 100644 --- a/src/java.base/share/classes/java/util/LinkedHashSet.java +++ b/src/java.base/share/classes/java/util/LinkedHashSet.java @@ -25,6 +25,13 @@ package java.util; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.dataflow.qual.Deterministic; import org.checkerframework.dataflow.qual.Pure; @@ -129,7 +136,7 @@ */ @CFComment({"lock/nullness: This class permits null elements"}) -@AnnotatedFor({"lock", "nullness"}) +@AnnotatedFor({"lock", "nullness", "modifiability"}) public class LinkedHashSet extends HashSet implements SequencedSet, Cloneable, java.io.Serializable { @@ -150,7 +157,7 @@ public class LinkedHashSet * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive */ - public LinkedHashSet(int initialCapacity, float loadFactor) { + public @Modifiable @IteratorPolyMod LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } @@ -166,7 +173,7 @@ public LinkedHashSet(int initialCapacity, float loadFactor) { * @throws IllegalArgumentException if the initial capacity is less * than zero */ - public LinkedHashSet(int initialCapacity) { + public @Modifiable @IteratorPolyMod LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true); } @@ -175,7 +182,7 @@ public LinkedHashSet(int initialCapacity) { * capacity (16) and load factor (0.75). */ @SideEffectFree - public LinkedHashSet() { + public @Modifiable @IteratorPolyMod LinkedHashSet() { super(16, .75f, true); } @@ -189,7 +196,7 @@ public LinkedHashSet() { * this set * @throws NullPointerException if the specified collection is null */ - public LinkedHashSet(Collection c) { + public @Modifiable @IteratorPolyMod LinkedHashSet(Collection c) { super(HashMap.calculateHashMapCapacity(Math.max(c.size(), 12)), .75f, true); addAll(c); } @@ -215,6 +222,7 @@ public LinkedHashSet(Collection c) { */ @Override @SideEffectFree + @DoesNotUnrefineReceiver("modifiability") public Spliterator spliterator() { return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED); } @@ -231,7 +239,7 @@ public Spliterator spliterator() { * @throws IllegalArgumentException if numElements is negative * @since 19 */ - public static LinkedHashSet newLinkedHashSet(int numElements) { + public static @Modifiable LinkedHashSet newLinkedHashSet(int numElements) { if (numElements < 0) { throw new IllegalArgumentException("Negative number of elements: " + numElements); } @@ -255,7 +263,7 @@ LinkedHashMap map() { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E e) { + public void addFirst(@SeqGrowable LinkedHashSet this, E e) { map().putFirst(e, PRESENT); } @@ -270,7 +278,7 @@ public void addFirst(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E e) { + public void addLast(@SeqGrowable LinkedHashSet this, E e) { map().putLast(e, PRESENT); } @@ -306,7 +314,7 @@ public E getLast() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeFirst() { + public E removeFirst(@Shrinkable LinkedHashSet this) { return map().sequencedKeySet().removeFirst(); } @@ -318,7 +326,7 @@ public E removeFirst() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeLast() { + public E removeLast(@Shrinkable LinkedHashSet this) { return map().sequencedKeySet().removeLast(); } @@ -339,15 +347,15 @@ class ReverseLinkedHashSetView extends AbstractSet implements SequencedSet public int size() { return LinkedHashSet.this.size(); } @SideEffectFree public Iterator iterator() { return map().sequencedKeySet().reversed().iterator(); } - public boolean add(E e) { return LinkedHashSet.this.add(e); } - public void addFirst(E e) { LinkedHashSet.this.addLast(e); } - public void addLast(E e) { LinkedHashSet.this.addFirst(e); } + public boolean add(@Growable ReverseLinkedHashSetView this, E e) { return LinkedHashSet.this.add(e); } + public void addFirst(@SeqGrowable ReverseLinkedHashSetView this, E e) { LinkedHashSet.this.addLast(e); } + public void addLast(@SeqGrowable ReverseLinkedHashSetView this, E e) { LinkedHashSet.this.addFirst(e); } @Pure public E getFirst() { return LinkedHashSet.this.getLast(); } @Pure public E getLast() { return LinkedHashSet.this.getFirst(); } - public E removeFirst() { return LinkedHashSet.this.removeLast(); } - public E removeLast() { return LinkedHashSet.this.removeFirst(); } + public E removeFirst(@Shrinkable ReverseLinkedHashSetView this) { return LinkedHashSet.this.removeLast(); } + public E removeLast(@Shrinkable ReverseLinkedHashSetView this) { return LinkedHashSet.this.removeFirst(); } @SideEffectFree public SequencedSet reversed() { return LinkedHashSet.this; } @Pure diff --git a/src/java.base/share/classes/java/util/LinkedList.java b/src/java.base/share/classes/java/util/LinkedList.java index cdad9d058291..3d31fddaff3e 100644 --- a/src/java.base/share/classes/java/util/LinkedList.java +++ b/src/java.base/share/classes/java/util/LinkedList.java @@ -31,6 +31,13 @@ import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; import org.checkerframework.checker.lock.qual.ReleasesNoLocks; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -39,6 +46,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; import org.checkerframework.checker.signedness.qual.PolySigned; import org.checkerframework.checker.signedness.qual.UnknownSignedness; +import org.checkerframework.common.value.qual.StaticallyExecutable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; @@ -109,7 +117,7 @@ */ @CFComment({"lock/nullness: This class permits null elements"}) -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public class LinkedList extends AbstractSequentialList implements List, Deque, Cloneable, java.io.Serializable @@ -137,7 +145,7 @@ void dataStructureInvariants() { /** * Constructs an empty list. */ - public LinkedList() { + public @Modifiable @IteratorPolyMod LinkedList() { } /** @@ -148,7 +156,7 @@ public LinkedList() { * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ - public @PolyNonEmpty LinkedList(@PolyNonEmpty Collection c) { + public @Modifiable @IteratorPolyMod @PolyNonEmpty LinkedList(@PolyNonEmpty Collection c) { this(); addAll(c); } @@ -304,7 +312,7 @@ public E getLast(@GuardSatisfied @NonEmpty LinkedList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeFirst(@GuardSatisfied @NonEmpty @CanShrink LinkedList this) { + public E removeFirst(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink LinkedList this) { final Node f = first; if (f == null) throw new NoSuchElementException(); @@ -319,7 +327,7 @@ public E removeFirst(@GuardSatisfied @NonEmpty @CanShrink LinkedList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeLast(@GuardSatisfied @NonEmpty @CanShrink LinkedList this) { + public E removeLast(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink LinkedList this) { final Node l = last; if (l == null) throw new NoSuchElementException(); @@ -334,7 +342,7 @@ public E removeLast(@GuardSatisfied @NonEmpty @CanShrink LinkedList this) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(@GuardSatisfied LinkedList this, E e) { + public void addFirst(@SeqGrowable @GuardSatisfied LinkedList this, E e) { linkFirst(e); } @@ -348,7 +356,7 @@ public void addFirst(@GuardSatisfied LinkedList this, E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(@GuardSatisfied LinkedList this, E e) { + public void addLast(@SeqGrowable @GuardSatisfied LinkedList this, E e) { linkLast(e); } @@ -389,7 +397,7 @@ public boolean contains(@GuardSatisfied LinkedList this, @GuardSatisfied @Nul @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(@GuardSatisfied LinkedList this, E e) { + public boolean add(@Growable @GuardSatisfied LinkedList this, E e) { linkLast(e); return true; } @@ -410,7 +418,7 @@ public boolean add(@GuardSatisfied LinkedList this, E e) { @ReleasesNoLocks @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @CanShrink LinkedList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @GuardSatisfied @CanShrink LinkedList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { if (o == null) { for (Node x = first; x != null; x = x.next) { if (x.item == null) { @@ -443,7 +451,7 @@ public boolean remove(@GuardSatisfied @CanShrink LinkedList this, @GuardSatis */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(@GuardSatisfied LinkedList this, Collection c) { + public boolean addAll(@Growable @GuardSatisfied LinkedList this, Collection c) { return addAll(size, c); } @@ -464,7 +472,7 @@ public boolean addAll(@GuardSatisfied LinkedList this, Collection this, @NonNegative int index, Collection c) { + public boolean addAll(@Growable @GuardSatisfied LinkedList this, @NonNegative int index, Collection c) { checkPositionIndex(index); Object[] a = c.toArray(); @@ -509,7 +517,7 @@ public boolean addAll(@GuardSatisfied LinkedList this, @NonNegative int index */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink LinkedList this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink LinkedList this) { // Clearing all of the links between nodes is "unnecessary", but: // - helps a generational GC if the discarded nodes inhabit // more than one generation @@ -554,7 +562,7 @@ public E get(@GuardSatisfied LinkedList this, @NonNegative int index) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E set(@GuardSatisfied LinkedList this, @NonNegative int index, E element) { + public E set(@Replaceable @GuardSatisfied LinkedList this, @NonNegative int index, E element) { checkElementIndex(index); Node x = node(index); E oldVal = x.item; @@ -574,7 +582,7 @@ public E set(@GuardSatisfied LinkedList this, @NonNegative int index, E eleme @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void add(@GuardSatisfied LinkedList this, @NonNegative int index, E element) { + public void add(@Growable @GuardSatisfied LinkedList this, @NonNegative int index, E element) { checkPositionIndex(index); if (index == size) @@ -594,7 +602,7 @@ public void add(@GuardSatisfied LinkedList this, @NonNegative int index, E el */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@GuardSatisfied @CanShrink LinkedList this, @NonNegative int index) { + public E remove(@Shrinkable @GuardSatisfied @CanShrink LinkedList this, @NonNegative int index) { checkElementIndex(index); return unlink(node(index)); } @@ -666,6 +674,7 @@ Node node(@NonNegative int index) { * this list, or -1 if this list does not contain the element */ @Pure + @StaticallyExecutable public @GTENegativeOne int indexOf(@GuardSatisfied LinkedList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { int index = 0; if (o == null) { @@ -696,6 +705,7 @@ Node node(@NonNegative int index) { * this list, or -1 if this list does not contain the element */ @Pure + @StaticallyExecutable public @GTENegativeOne int lastIndexOf(@GuardSatisfied LinkedList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { int index = size; if (o == null) { @@ -748,7 +758,7 @@ public E element(@GuardSatisfied @NonEmpty LinkedList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink LinkedList this) { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink LinkedList this) { final Node f = first; return (f == null) ? null : unlinkFirst(f); } @@ -762,7 +772,7 @@ public E element(@GuardSatisfied @NonEmpty LinkedList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@GuardSatisfied @NonEmpty @CanShrink LinkedList this) { + public E remove(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink LinkedList this) { return removeFirst(); } @@ -775,7 +785,7 @@ public E remove(@GuardSatisfied @NonEmpty @CanShrink LinkedList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable LinkedList this, E e) { return add(e); } @@ -789,7 +799,7 @@ public boolean offer(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerFirst(E e) { + public boolean offerFirst(@SeqGrowable LinkedList this, E e) { addFirst(e); return true; } @@ -803,7 +813,7 @@ public boolean offerFirst(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerLast(E e) { + public boolean offerLast(@SeqGrowable LinkedList this, E e) { addLast(e); return true; } @@ -846,7 +856,7 @@ public boolean offerLast(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollFirst(@GuardSatisfied @CanShrink LinkedList this) { + public @Nullable E pollFirst(@Shrinkable @GuardSatisfied @CanShrink LinkedList this) { final Node f = first; return (f == null) ? null : unlinkFirst(f); } @@ -861,7 +871,7 @@ public boolean offerLast(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollLast(@GuardSatisfied @CanShrink LinkedList this) { + public @Nullable E pollLast(@Shrinkable @GuardSatisfied @CanShrink LinkedList this) { final Node l = last; return (l == null) ? null : unlinkLast(l); } @@ -877,7 +887,7 @@ public boolean offerLast(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void push(@GuardSatisfied LinkedList this, E e) { + public void push(@SeqGrowable @GuardSatisfied LinkedList this, E e) { addFirst(e); } @@ -894,7 +904,7 @@ public void push(@GuardSatisfied LinkedList this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E pop(@GuardSatisfied @NonEmpty @CanShrink LinkedList this) { + public E pop(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink LinkedList this) { return removeFirst(); } @@ -909,7 +919,7 @@ public E pop(@GuardSatisfied @NonEmpty @CanShrink LinkedList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeFirstOccurrence(@GuardSatisfied @CanShrink LinkedList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean removeFirstOccurrence(@Shrinkable @GuardSatisfied @CanShrink LinkedList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return remove(o); } @@ -924,7 +934,7 @@ public boolean removeFirstOccurrence(@GuardSatisfied @CanShrink LinkedList th */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeLastOccurrence(@GuardSatisfied @CanShrink LinkedList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean removeLastOccurrence(@Shrinkable @GuardSatisfied @CanShrink LinkedList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { if (o == null) { for (Node x = last; x != null; x = x.prev) { if (x.item == null) { @@ -964,7 +974,7 @@ public boolean removeLastOccurrence(@GuardSatisfied @CanShrink LinkedList thi * @throws IndexOutOfBoundsException {@inheritDoc} * @see List#listIterator(int) */ - public @PolyGrowShrink @PolyNonEmpty ListIterator listIterator(@PolyGrowShrink @PolyNonEmpty LinkedList this, @NonNegative int index) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty ListIterator listIterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty LinkedList this, @NonNegative int index) { checkPositionIndex(index); return new ListItr(index); } @@ -1099,7 +1109,7 @@ private static class Node { * @since 1.6 */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyNonEmpty LinkedList this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty LinkedList this) { return new DescendingIterator(); } @@ -1141,7 +1151,7 @@ private LinkedList superClone() { * @return a shallow copy of this {@code LinkedList} instance */ @SideEffectFree - public Object clone(@GuardSatisfied LinkedList this) { + public @Modifiable @IteratorPolyMod Object clone(@GuardSatisfied LinkedList this) { LinkedList clone = superClone(); // Put clone into "virgin" state @@ -1457,6 +1467,7 @@ public boolean removeIf(Predicate filter) { return rlist.removeIf(filter); } + @SideEffectFree public T[] toArray(IntFunction generator) { return rlist.toArray(generator); } @@ -1549,7 +1560,7 @@ public E pop() { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void push(E e) { + public void push(@SeqGrowable ReverseOrderLinkedListView this, E e) { rdeque.push(e); } @@ -1577,13 +1588,13 @@ public void push(E e) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerLast(E e) { + public boolean offerLast(@SeqGrowable ReverseOrderLinkedListView this, E e) { return rdeque.offerLast(e); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerFirst(E e) { + public boolean offerFirst(@SeqGrowable ReverseOrderLinkedListView this, E e) { return rdeque.offerFirst(e); } @@ -1616,11 +1627,13 @@ public E element() { } @Pure + @StaticallyExecutable public int lastIndexOf(@Nullable Object o) { return rlist.lastIndexOf(o); } @Pure + @StaticallyExecutable public int indexOf(@Nullable Object o) { return rlist.indexOf(o); } @@ -1691,13 +1704,13 @@ public boolean contains(@Nullable Object o) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E e) { + public void addLast(@SeqGrowable ReverseOrderLinkedListView this, E e) { rdeque.addLast(e); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E e) { + public void addFirst(@SeqGrowable ReverseOrderLinkedListView this, E e) { rdeque.addFirst(e); } diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java index 9272b2821dc9..4f0903691ef3 100644 --- a/src/java.base/share/classes/java/util/List.java +++ b/src/java.base/share/classes/java/util/List.java @@ -33,6 +33,15 @@ import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; import org.checkerframework.checker.lock.qual.ReleasesNoLocks; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -162,7 +171,7 @@ */ @CFComment({"lock/nullness: Subclasses of this interface/class may opt to prohibit null elements"}) -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public interface List extends SequencedCollection { // Query Operations @@ -174,7 +183,7 @@ public interface List extends SequencedCollection { * @return the number of elements in this list */ @Pure - @NonNegative int size(@GuardSatisfied List this); + @NonNegative int size(@MaybeModifiable @GuardSatisfied List this); /** * Returns {@code true} if this list contains no elements. @@ -183,7 +192,7 @@ public interface List extends SequencedCollection { */ @Pure @EnsuresNonEmptyIf(result = false, expression = "this") - boolean isEmpty(@GuardSatisfied List this); + boolean isEmpty(@MaybeModifiable @GuardSatisfied List this); /** * Returns {@code true} if this list contains the specified element. @@ -202,7 +211,7 @@ public interface List extends SequencedCollection { */ @Pure @EnsuresNonEmptyIf(result = true, expression = "this") - boolean contains(@GuardSatisfied List this, @UnknownSignedness Object o); + boolean contains(@MaybeModifiable @GuardSatisfied List this, @UnknownSignedness Object o); /** * Returns an iterator over the elements in this list in proper sequence. @@ -229,7 +238,7 @@ public interface List extends SequencedCollection { * @see Arrays#asList(Object[]) */ @SideEffectFree - @PolyNull @PolySigned Object[] toArray(List<@PolyNull @PolySigned E> this); + @PolyNull @PolySigned Object[] toArray(@MaybeModifiable List<@PolyNull @PolySigned E> this); /** * Returns an array containing all of the elements in this list in @@ -270,7 +279,7 @@ public interface List extends SequencedCollection { * this list * @throws NullPointerException if the specified array is null */ - @Nullable T[] toArray(@PolyNull T[] a); + @Nullable T[] toArray(@MaybeModifiable List this, @PolyNull T[] a); // Modification Operations @@ -301,7 +310,7 @@ public interface List extends SequencedCollection { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean add(@GuardSatisfied List this, E e); + boolean add(@IteratorPolyMod @Growable @GuardSatisfied List this, E e); /** * Removes the first occurrence of the specified element from this list, @@ -326,8 +335,7 @@ public interface List extends SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean remove(@GuardSatisfied @CanShrink List this, @UnknownSignedness Object o); - + boolean remove(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink List this, @UnknownSignedness Object o); // Bulk Modification Operations @@ -350,7 +358,7 @@ public interface List extends SequencedCollection { * @see #contains(Object) */ @Pure - boolean containsAll(@GuardSatisfied List this, Collection c); + boolean containsAll(@MaybeModifiable @GuardSatisfied List this, Collection c); /** * Appends all of the elements in the specified collection to the end of @@ -376,7 +384,7 @@ public interface List extends SequencedCollection { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") @EnsuresNonEmptyIf(result = true, expression = "this") - boolean addAll(@GuardSatisfied List this, Collection c); + boolean addAll(@Growable @GuardSatisfied List this, Collection c); /** * Inserts all of the elements in the specified collection into this @@ -408,7 +416,7 @@ public interface List extends SequencedCollection { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") @EnsuresNonEmptyIf(result = true, expression = "this") - boolean addAll(@GuardSatisfied List this, @IndexOrHigh({"this"}) int index, Collection c); + boolean addAll(@IteratorPolyMod @Growable @GuardSatisfied List this, @IndexOrHigh({"this"}) int index, Collection c); /** * Removes from this list all of its elements that are contained in the @@ -430,7 +438,7 @@ public interface List extends SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean removeAll(@GuardSatisfied @CanShrink List this, Collection c); + boolean removeAll(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink List this, Collection c); /** * Retains only the elements in this list that are contained in the @@ -454,7 +462,7 @@ public interface List extends SequencedCollection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean retainAll(@GuardSatisfied @CanShrink List this, Collection c); + boolean retainAll(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink List this, Collection c); /** * Replaces each element of this list with the result of applying the @@ -486,7 +494,7 @@ public interface List extends SequencedCollection { * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - default void replaceAll(UnaryOperator operator) { + default void replaceAll(@IteratorPolyMod @Replaceable List this, UnaryOperator operator) { Objects.requireNonNull(operator); final ListIterator li = this.listIterator(); while (li.hasNext()) { @@ -556,7 +564,7 @@ default void replaceAll(UnaryOperator operator) { @SuppressWarnings({"unchecked", "rawtypes"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default void sort(Comparator c) { + default void sort(@IteratorPolyMod @Replaceable List this, Comparator c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator i = this.listIterator(); @@ -575,7 +583,7 @@ default void sort(Comparator c) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void clear(@GuardSatisfied @CanShrink List this); + void clear(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink List this); // Comparison and hashing @@ -595,7 +603,7 @@ default void sort(Comparator c) { * @return {@code true} if the specified object is equal to this list */ @Pure - boolean equals(@GuardSatisfied List this, @Nullable Object o); + boolean equals(@MaybeModifiable @GuardSatisfied List this, @Nullable Object o); /** * Returns the hash code value for this list. The hash code of a list @@ -615,7 +623,7 @@ default void sort(Comparator c) { * @see #equals(Object) */ @Pure - int hashCode(@GuardSatisfied List this); + int hashCode(@MaybeModifiable @GuardSatisfied List this); // Positional Access Operations @@ -629,7 +637,7 @@ default void sort(Comparator c) { * ({@code index < 0 || index >= size()}) */ @Pure - E get(@GuardSatisfied List this, @IndexFor({"this"}) int index); + E get(@MaybeModifiable @GuardSatisfied List this, @IndexFor({"this"}) int index); /** * Replaces the element at the specified position in this list with the @@ -652,7 +660,7 @@ default void sort(Comparator c) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E set(@GuardSatisfied List this, @IndexFor({"this"}) int index, E element); + E set(@IteratorPolyMod @Replaceable @GuardSatisfied List this, @IndexFor({"this"}) int index, E element); /** * Inserts the specified element at the specified position in this list @@ -677,8 +685,7 @@ default void sort(Comparator c) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void add(@GuardSatisfied List this, @IndexOrHigh({"this"}) int index, E element); - + void add(@IteratorPolyMod @Growable @GuardSatisfied List this, @IndexOrHigh({"this"}) int index, E element); /** * Removes the element at the specified position in this list (optional * operation). Shifts any subsequent elements to the left (subtracts one @@ -695,7 +702,7 @@ default void sort(Comparator c) { @ReleasesNoLocks @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E remove(@GuardSatisfied @CanShrink List this, @IndexFor({"this"}) int index); + E remove(@IteratorPolyMod @Shrinkable @GuardSatisfied @CanShrink List this, @IndexFor({"this"}) int index); // Search Operations @@ -718,7 +725,7 @@ default void sort(Comparator c) { * (optional) */ @Pure - @GTENegativeOne int indexOf(@GuardSatisfied List this, @GuardSatisfied @UnknownSignedness Object o); + @GTENegativeOne int indexOf(@MaybeModifiable @GuardSatisfied List this, @GuardSatisfied @UnknownSignedness Object o); /** * Returns the index of the last occurrence of the specified element @@ -738,7 +745,7 @@ default void sort(Comparator c) { * (optional) */ @Pure - @GTENegativeOne int lastIndexOf(@GuardSatisfied List this, @GuardSatisfied @UnknownSignedness Object o); + @GTENegativeOne int lastIndexOf(@MaybeModifiable @GuardSatisfied List this, @GuardSatisfied @UnknownSignedness Object o); // List Iterators @@ -806,7 +813,7 @@ default void sort(Comparator c) { * fromIndex > toIndex}) */ @SideEffectFree - @PolyGrowShrink List subList(@GuardSatisfied @PolyGrowShrink List this, @IndexOrHigh({"this"}) int fromIndex, @IndexOrHigh({"this"}) int toIndex); + @PolyGrowShrink @PolyModifiable List subList(@PolyModifiable @GuardSatisfied @PolyGrowShrink List this, @IndexOrHigh({"this"}) int fromIndex, @IndexOrHigh({"this"}) int toIndex); /** * Creates a {@link Spliterator} over the elements in this list. @@ -866,7 +873,7 @@ default Spliterator spliterator() { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default void addFirst(E e) { + default void addFirst(@SeqGrowable List this, E e) { this.add(0, e); } @@ -883,7 +890,7 @@ default void addFirst(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default void addLast(E e) { + default void addLast(@SeqGrowable List this, E e) { this.add(e); } @@ -940,7 +947,7 @@ default E getLast() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default E removeFirst() { + default E removeFirst(@Shrinkable List this) { if (this.isEmpty()) { throw new NoSuchElementException(); } else { @@ -961,7 +968,7 @@ default E removeFirst() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default E removeLast() { + default E removeLast(@Shrinkable List this) { if (this.isEmpty()) { throw new NoSuchElementException(); } else { @@ -998,7 +1005,7 @@ default List reversed() { */ @SideEffectFree @SuppressWarnings("unchecked") - static List of() { + static @Unmodifiable List of() { return (List) ImmutableCollections.EMPTY_LIST; } @@ -1015,7 +1022,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1) { + static @Unmodifiable @NonEmpty List of(E e1) { return new ImmutableCollections.List12<>(e1); } @@ -1033,7 +1040,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1, E e2) { + static @Unmodifiable @NonEmpty List of(E e1, E e2) { return new ImmutableCollections.List12<>(e1, e2); } @@ -1052,7 +1059,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1, E e2, E e3) { + static @Unmodifiable @NonEmpty List of(E e1, E e2, E e3) { return ImmutableCollections.listFromTrustedArray(e1, e2, e3); } @@ -1072,7 +1079,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1, E e2, E e3, E e4) { + static @Unmodifiable @NonEmpty List of(E e1, E e2, E e3, E e4) { return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4); } @@ -1093,7 +1100,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1, E e2, E e3, E e4, E e5) { + static @Unmodifiable @NonEmpty List of(E e1, E e2, E e3, E e4, E e5) { return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5); } @@ -1115,7 +1122,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6) { + static @Unmodifiable @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6) { return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5, e6); } @@ -1139,7 +1146,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + static @Unmodifiable @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5, e6, e7); } @@ -1164,7 +1171,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + static @Unmodifiable @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5, e6, e7, e8); } @@ -1190,7 +1197,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + static @Unmodifiable @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5, e6, e7, e8, e9); } @@ -1217,7 +1224,7 @@ static List of() { * @since 9 */ @SideEffectFree - static @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + static @Unmodifiable @NonEmpty List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); } @@ -1250,7 +1257,7 @@ static List of() { @SideEffectFree @SafeVarargs @SuppressWarnings("varargs") - static @PolyNonEmpty List of(E @PolyNonEmpty... elements) { + static @Unmodifiable @PolyNonEmpty List of(E @PolyNonEmpty... elements) { switch (elements.length) { // implicit null check of elements case 0: @SuppressWarnings("unchecked") @@ -1282,7 +1289,7 @@ static List of() { * @since 10 */ @SideEffectFree - static @PolyNonEmpty List copyOf(@PolyNonEmpty Collection coll) { + static @Unmodifiable @PolyNonEmpty List copyOf(@MaybeModifiable @PolyNonEmpty Collection coll) { return ImmutableCollections.listCopy(coll); } } diff --git a/src/java.base/share/classes/java/util/ListIterator.java b/src/java.base/share/classes/java/util/ListIterator.java index 69a75a313eae..5b1aa4d9bd35 100644 --- a/src/java.base/share/classes/java/util/ListIterator.java +++ b/src/java.base/share/classes/java/util/ListIterator.java @@ -28,6 +28,9 @@ import org.checkerframework.checker.index.qual.GTENegativeOne; import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -173,8 +176,7 @@ public interface ListIterator extends Iterator { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void remove(@GuardSatisfied ListIterator this); - + void remove(@Shrinkable @GuardSatisfied ListIterator this); /** * Replaces the last element returned by {@link #next} or * {@link #previous} with the specified element (optional operation). @@ -197,8 +199,7 @@ public interface ListIterator extends Iterator { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void set(@GuardSatisfied ListIterator this, E e); - + void set(@Replaceable @GuardSatisfied ListIterator this, E e); /** * Inserts the specified element into the list (optional operation). * The element is inserted immediately before the element that @@ -222,5 +223,5 @@ public interface ListIterator extends Iterator { @CFComment("Not @EnsuresNonEmpty(this) because this adds *before* the cursor.") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void add(@GuardSatisfied ListIterator this, E e); + void add(@Growable @GuardSatisfied ListIterator this, E e); } diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java index f1342799f9e3..818839fb4d3d 100644 --- a/src/java.base/share/classes/java/util/Map.java +++ b/src/java.base/share/classes/java/util/Map.java @@ -28,6 +28,16 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.lock.qual.GuardSatisfied; import org.checkerframework.checker.lock.qual.ReleasesNoLocks; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -196,7 +206,7 @@ public interface Map { * @return the number of key-value mappings in this map */ @Pure - @NonNegative int size(@GuardSatisfied Map this); + @NonNegative int size(@MaybeModifiable @GuardSatisfied Map this); /** * Returns {@code true} if this map contains no key-value mappings. @@ -205,7 +215,7 @@ public interface Map { */ @Pure @EnsuresNonEmptyIf(result = false, expression = "this") - boolean isEmpty(@GuardSatisfied Map this); + boolean isEmpty(@MaybeModifiable @GuardSatisfied Map this); /** * Returns {@code true} if this map contains a mapping for the specified @@ -226,7 +236,7 @@ public interface Map { @EnsuresKeyForIf(expression={"#1"}, result=true, map={"this"}) @Pure @EnsuresNonEmptyIf(result=true, expression={"this"}) - boolean containsKey(@GuardSatisfied Map this, @GuardSatisfied @UnknownSignedness Object key); + boolean containsKey(@MaybeModifiable @GuardSatisfied Map this, @GuardSatisfied @UnknownSignedness Object key); /** * Returns {@code true} if this map maps one or more keys to the @@ -246,7 +256,7 @@ public interface Map { */ @Pure @EnsuresNonEmptyIf(result=true, expression={"this"}) - boolean containsValue(@GuardSatisfied Map this, @GuardSatisfied @UnknownSignedness Object value); + boolean containsValue(@MaybeModifiable @GuardSatisfied Map this, @GuardSatisfied @UnknownSignedness Object value); /** * Returns the value to which the specified key is mapped, @@ -273,7 +283,7 @@ public interface Map { * does not permit null keys ({@linkplain Collection##optional-restrictions optional}) */ @Pure - @Nullable V get(@GuardSatisfied Map this, @UnknownSignedness @GuardSatisfied Object key); + @Nullable V get(@MaybeModifiable @GuardSatisfied Map this, @UnknownSignedness @GuardSatisfied Object key); // Modification Operations @@ -306,8 +316,7 @@ public interface Map { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable V put(@GuardSatisfied Map this, K key, V value); - + @Nullable V put(@Growable @Replaceable @GuardSatisfied Map this, K key, V value); /** * Removes the mapping for a key from this map if it is present * (optional operation). More formally, if this map contains a mapping @@ -339,8 +348,7 @@ public interface Map { @CFComment("nullness: key is not @Nullable because this map might not permit null values") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable V remove(@GuardSatisfied Map this, @GuardSatisfied @UnknownSignedness Object key); - + @Nullable V remove(@Shrinkable @GuardSatisfied Map this, @GuardSatisfied @UnknownSignedness Object key); // Bulk Operations @@ -367,8 +375,7 @@ public interface Map { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void putAll(@GuardSatisfied Map this, Map m); - + void putAll(@Growable @Replaceable @GuardSatisfied Map this, Map m); /** * Removes all of the mappings from this map (optional operation). * The map will be empty after this call returns. @@ -378,8 +385,7 @@ public interface Map { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void clear(@GuardSatisfied Map this); - + void clear(@Shrinkable @GuardSatisfied Map this); // Views @@ -399,7 +405,7 @@ public interface Map { * @return a set view of the keys contained in this map */ @SideEffectFree - @PolyNonEmpty Set<@KeyFor({"this"}) K> keySet(@GuardSatisfied @PolyNonEmpty Map this); + @IteratorPolyMod @PolyShrinkable @Ungrowable @PolyNonEmpty Set<@KeyFor({"this"}) K> keySet(@PolyShrinkable @GuardSatisfied @PolyNonEmpty Map this); /** * Returns a {@link Collection} view of the values contained in this map. @@ -417,7 +423,7 @@ public interface Map { * @return a collection view of the values contained in this map */ @SideEffectFree - @PolyNonEmpty Collection values(@GuardSatisfied @PolyNonEmpty Map this); + @IteratorPolyMod @PolyShrinkable @Ungrowable @PolyNonEmpty Collection values(@PolyShrinkable @GuardSatisfied @PolyNonEmpty Map this); /** * Returns a {@link Set} view of the mappings contained in this map. @@ -436,7 +442,7 @@ public interface Map { * @return a set view of the mappings contained in this map */ @SideEffectFree - @PolyNonEmpty Set> entrySet(@GuardSatisfied @PolyNonEmpty Map this); + @IteratorPolyMod @PolyShrinkable @Ungrowable @PolyNonEmpty Set> entrySet(@PolyModifiable @GuardSatisfied @PolyNonEmpty Map this); /** * A map entry (key-value pair). The Entry may be unmodifiable, or the @@ -507,7 +513,7 @@ interface Entry { * removed from the backing map. */ @Pure - K getKey(Map.@GuardSatisfied Entry this); + K getKey(Map.@MaybeModifiable @GuardSatisfied Entry this); /** * Returns the value corresponding to this entry. If the mapping @@ -520,7 +526,7 @@ interface Entry { * removed from the backing map. */ @Pure - V getValue(Map.@GuardSatisfied Entry this); + V getValue(Map.@MaybeModifiable @GuardSatisfied Entry this); /** * Replaces the value corresponding to this entry with the specified @@ -544,8 +550,7 @@ interface Entry { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - V setValue(Map.@GuardSatisfied Entry this, V value); - + V setValue(Map.@Replaceable @GuardSatisfied Entry this, V value); /** * Compares the specified object with this entry for equality. * Returns {@code true} if the given object is also a map entry and @@ -565,7 +570,7 @@ interface Entry { * entry */ @Pure - boolean equals(Map.@GuardSatisfied Entry this, @GuardSatisfied @Nullable Object o); + boolean equals(Map.@MaybeModifiable @GuardSatisfied Entry this, @GuardSatisfied @Nullable Object o); /** * Returns the hash code value for this map entry. The hash code @@ -584,7 +589,7 @@ interface Entry { * @see #equals(Object) */ @Pure - int hashCode(Map.@GuardSatisfied Entry this); + int hashCode(Map.@MaybeModifiable @GuardSatisfied Entry this); /** * Returns a comparator that compares {@link Map.Entry} in natural order on key. @@ -685,7 +690,7 @@ public static Comparator> comparingByValue(Comparator Map.Entry copyOf(Map.Entry e) { + public static Map.@Unmodifiable Entry copyOf(Map.@MaybeModifiable Entry e) { Objects.requireNonNull(e); if (e instanceof KeyValueHolder) { return (Map.Entry) e; @@ -710,7 +715,7 @@ public static Comparator> comparingByValue(Comparator this, @GuardSatisfied @Nullable Object o); + boolean equals(@MaybeModifiable @GuardSatisfied Map this, @GuardSatisfied @Nullable Object o); /** * Returns the hash code value for this map. The hash code of a map is @@ -726,7 +731,7 @@ public static Comparator> comparingByValue(Comparator this); + int hashCode(@MaybeModifiable @GuardSatisfied Map this); // Defaultable methods @@ -751,7 +756,7 @@ public static Comparator> comparingByValue(Comparator this, @GuardSatisfied @UnknownSignedness Object key, @PolyModifiable V defaultValue) { V v; return (((v = get(key)) != null) || containsKey(key)) ? v @@ -835,7 +840,7 @@ default void forEach(@NonLeaked BiConsumer action) { * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - default void replaceAll(BiFunction function) { + default void replaceAll(@Replaceable Map this, BiFunction function) { Objects.requireNonNull(function); for (Map.Entry entry : entrySet()) { K k; @@ -904,7 +909,7 @@ default void replaceAll(BiFunction function) @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default @Nullable V putIfAbsent(K key, V value) { + default @Nullable V putIfAbsent(@Growable Map this, K key, V value) { V v = get(key); if (v == null) { v = put(key, value); @@ -950,7 +955,7 @@ default void replaceAll(BiFunction function) @CFComment("nullness: key and value are not @Nullable because this map might not permit null values") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default boolean remove(@GuardSatisfied @UnknownSignedness Object key, @GuardSatisfied @UnknownSignedness Object value) { + default boolean remove(@Shrinkable Map this, @GuardSatisfied @UnknownSignedness Object key, @GuardSatisfied @UnknownSignedness Object value) { Object curValue = get(key); if (!Objects.equals(curValue, value) || (curValue == null && !containsKey(key))) { @@ -1002,7 +1007,7 @@ default boolean remove(@GuardSatisfied @UnknownSignedness Object key, @GuardSati */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default boolean replace(K key, V oldValue, V newValue) { + default boolean replace(@Replaceable Map this, K key, V oldValue, V newValue) { Object curValue = get(key); if (!Objects.equals(curValue, oldValue) || (curValue == null && !containsKey(key))) { @@ -1052,7 +1057,7 @@ default boolean replace(K key, V oldValue, V newValue) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default @Nullable V replace(K key, V value) { + default @Nullable V replace(@Replaceable Map this, K key, V value) { V curValue; if (((curValue = get(key)) != null) || containsKey(key)) { curValue = put(key, value); @@ -1135,7 +1140,7 @@ default boolean replace(K key, V oldValue, V newValue) { * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - default @PolyNull V computeIfAbsent(K key, + default @PolyNull V computeIfAbsent(@Growable Map this, K key, Function mappingFunction) { Objects.requireNonNull(mappingFunction); V v; @@ -1213,7 +1218,7 @@ default boolean replace(K key, V oldValue, V newValue) { * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - default @Nullable V computeIfPresent(K key, + default @Nullable V computeIfPresent(@Shrinkable @Replaceable Map this, K key, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); V oldValue; @@ -1300,7 +1305,7 @@ default boolean replace(K key, V oldValue, V newValue) { * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - default @Nullable V compute(K key, + default @Nullable V compute(@Modifiable Map this, K key, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); V oldValue = get(key); @@ -1399,7 +1404,7 @@ default boolean replace(K key, V oldValue, V newValue) { * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - default @Nullable V merge(K key, @NonNull V value, + default @Nullable V merge(@Modifiable Map this, K key, @NonNull V value, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); @@ -1426,7 +1431,7 @@ default boolean replace(K key, V oldValue, V newValue) { */ @SuppressWarnings("unchecked") @SideEffectFree - static Map of() { + static @Unmodifiable Map of() { return (Map) ImmutableCollections.EMPTY_MAP; } @@ -1444,7 +1449,7 @@ static Map of() { * @since 9 */ @SideEffectFree - static @NonEmpty Map of(K k1, V v1) { + static @Unmodifiable @NonEmpty Map of(K k1, V v1) { return new ImmutableCollections.Map1<>(k1, v1); } @@ -1465,7 +1470,7 @@ static Map of() { * @since 9 */ @SideEffectFree - static @NonEmpty Map of(K k1, V v1, K k2, V v2) { + static @Unmodifiable @NonEmpty Map of(K k1, V v1, K k2, V v2) { return new ImmutableCollections.MapN<>(k1, v1, k2, v2); } @@ -1488,7 +1493,7 @@ static Map of() { * @since 9 */ @SideEffectFree - static @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3) { + static @Unmodifiable @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3) { return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3); } @@ -1513,7 +1518,7 @@ static Map of() { * @since 9 */ @SideEffectFree - static @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + static @Unmodifiable @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4); } @@ -1540,7 +1545,7 @@ static Map of() { * @since 9 */ @SideEffectFree - static @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + static @Unmodifiable @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); } @@ -1568,7 +1573,7 @@ static Map of() { * * @since 9 */ - static @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + static @Unmodifiable @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6); @@ -1600,7 +1605,7 @@ static Map of() { * * @since 9 */ - static @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + static @Unmodifiable @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7); @@ -1634,7 +1639,7 @@ static Map of() { * * @since 9 */ - static @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + static @Unmodifiable @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) { return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8); @@ -1670,7 +1675,7 @@ static Map of() { * * @since 9 */ - static @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + static @Unmodifiable @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) { return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9); @@ -1708,7 +1713,7 @@ static Map of() { * * @since 9 */ - static @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + static @Unmodifiable @NonEmpty Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) { return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10); @@ -1747,7 +1752,7 @@ static Map of() { */ @SafeVarargs @SuppressWarnings("varargs") - static @PolyNonEmpty Map ofEntries(Entry @PolyNonEmpty... entries) { + static @Unmodifiable @PolyNonEmpty Map ofEntries(Entry @PolyNonEmpty... entries) { if (entries.length == 0) { // implicit null check of entries array @SuppressWarnings("unchecked") var map = (Map) ImmutableCollections.EMPTY_MAP; @@ -1803,7 +1808,7 @@ static Map of() { * @see Map#ofEntries Map.ofEntries() * @since 9 */ - static Entry entry(@NonNull K k, @NonNull V v) { + static @Unmodifiable Entry entry(@NonNull K k, @NonNull V v) { // KeyValueHolder checks for nulls return new KeyValueHolder<>(k, v); } @@ -1827,7 +1832,7 @@ static Map of() { */ @SuppressWarnings({"rawtypes","unchecked"}) @SideEffectFree - static @PolyNonEmpty Map copyOf(@PolyNonEmpty Map map) { + static @Unmodifiable @PolyNonEmpty Map copyOf(@MaybeModifiable @PolyNonEmpty Map map) { if (map instanceof ImmutableCollections.AbstractImmutableMap) { return (Map)map; } else if (map.isEmpty()) { // Implicit nullcheck of map diff --git a/src/java.base/share/classes/java/util/NavigableMap.java b/src/java.base/share/classes/java/util/NavigableMap.java index d22898b89fd1..ffaa760cbe16 100644 --- a/src/java.base/share/classes/java/util/NavigableMap.java +++ b/src/java.base/share/classes/java/util/NavigableMap.java @@ -36,8 +36,14 @@ package java.util; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nullness.qual.KeyFor; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; import org.checkerframework.framework.qual.AnnotatedFor; @@ -125,6 +131,7 @@ public interface NavigableMap extends SortedMap { * @throws NullPointerException if the specified key is null * and this map does not permit null keys */ + @SideEffectFree Map.@Nullable Entry lowerEntry(K key); /** @@ -139,6 +146,7 @@ public interface NavigableMap extends SortedMap { * @throws NullPointerException if the specified key is null * and this map does not permit null keys */ + @Pure @Nullable K lowerKey(K key); /** @@ -154,6 +162,7 @@ public interface NavigableMap extends SortedMap { * @throws NullPointerException if the specified key is null * and this map does not permit null keys */ + @SideEffectFree Map.@Nullable Entry floorEntry(K key); /** @@ -168,6 +177,7 @@ public interface NavigableMap extends SortedMap { * @throws NullPointerException if the specified key is null * and this map does not permit null keys */ + @Pure @Nullable K floorKey(K key); /** @@ -183,6 +193,7 @@ public interface NavigableMap extends SortedMap { * @throws NullPointerException if the specified key is null * and this map does not permit null keys */ + @SideEffectFree Map.@Nullable Entry ceilingEntry(K key); /** @@ -197,6 +208,7 @@ public interface NavigableMap extends SortedMap { * @throws NullPointerException if the specified key is null * and this map does not permit null keys */ + @Pure @Nullable K ceilingKey(K key); /** @@ -212,6 +224,7 @@ public interface NavigableMap extends SortedMap { * @throws NullPointerException if the specified key is null * and this map does not permit null keys */ + @SideEffectFree Map.@Nullable Entry higherEntry(K key); /** @@ -226,6 +239,7 @@ public interface NavigableMap extends SortedMap { * @throws NullPointerException if the specified key is null * and this map does not permit null keys */ + @Pure @Nullable K higherKey(K key); /** @@ -235,6 +249,7 @@ public interface NavigableMap extends SortedMap { * @return an entry with the least key, * or {@code null} if this map is empty */ + @SideEffectFree Map.@Nullable Entry firstEntry(); /** @@ -244,6 +259,7 @@ public interface NavigableMap extends SortedMap { * @return an entry with the greatest key, * or {@code null} if this map is empty */ + @SideEffectFree Map.@Nullable Entry lastEntry(); /** @@ -255,8 +271,7 @@ public interface NavigableMap extends SortedMap { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - Map.@Nullable Entry pollFirstEntry(@GuardSatisfied NavigableMap this); - + Map.@Nullable Entry pollFirstEntry(@Shrinkable @GuardSatisfied NavigableMap this); /** * Removes and returns a key-value mapping associated with * the greatest key in this map, or {@code null} if the map is empty. @@ -266,8 +281,7 @@ public interface NavigableMap extends SortedMap { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - Map.@Nullable Entry pollLastEntry(@GuardSatisfied NavigableMap this); - + Map.@Nullable Entry pollLastEntry(@Shrinkable @GuardSatisfied NavigableMap this); /** * Returns a reverse order view of the mappings contained in this map. * The descending map is backed by this map, so changes to the map are @@ -284,7 +298,7 @@ public interface NavigableMap extends SortedMap { * @return a reverse order view of this map */ @SideEffectFree - NavigableMap descendingMap(); + @PolyModifiable NavigableMap descendingMap(@PolyModifiable NavigableMap this); /** * Returns a {@link NavigableSet} view of the keys contained in this map. @@ -301,7 +315,7 @@ public interface NavigableMap extends SortedMap { * @return a navigable set view of the keys in this map */ @SideEffectFree - NavigableSet<@KeyFor({"this"}) K> navigableKeySet(); + @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet<@KeyFor({"this"}) K> navigableKeySet(@PolyShrinkable NavigableMap this); /** * Returns a reverse order {@link NavigableSet} view of the keys contained in this map. @@ -318,7 +332,7 @@ public interface NavigableMap extends SortedMap { * @return a reverse order navigable set view of the keys in this map */ @SideEffectFree - NavigableSet<@KeyFor({"this"}) K> descendingKeySet(); + @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet<@KeyFor({"this"}) K> descendingKeySet(@PolyShrinkable NavigableMap this); /** * Returns a view of the portion of this map whose keys range from @@ -355,7 +369,7 @@ public interface NavigableMap extends SortedMap { * outside the bounds of the range */ @SideEffectFree - NavigableMap subMap(K fromKey, boolean fromInclusive, + @PolyModifiable NavigableMap subMap(@PolyModifiable NavigableMap this, K fromKey, boolean fromInclusive, K toKey, boolean toInclusive); /** @@ -386,7 +400,7 @@ NavigableMap subMap(K fromKey, boolean fromInclusive, * bounds of the range */ @SideEffectFree - NavigableMap headMap(K toKey, boolean inclusive); + @PolyModifiable NavigableMap headMap(@PolyModifiable NavigableMap this, K toKey, boolean inclusive); /** * Returns a view of the portion of this map whose keys are greater than (or @@ -416,7 +430,7 @@ NavigableMap subMap(K fromKey, boolean fromInclusive, * bounds of the range */ @SideEffectFree - NavigableMap tailMap(K fromKey, boolean inclusive); + @PolyModifiable NavigableMap tailMap(@PolyModifiable NavigableMap this, K fromKey, boolean inclusive); /** * {@inheritDoc} @@ -428,7 +442,7 @@ NavigableMap subMap(K fromKey, boolean fromInclusive, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - SortedMap subMap(K fromKey, K toKey); + @PolyModifiable SortedMap subMap(@PolyModifiable NavigableMap this, K fromKey, K toKey); /** * {@inheritDoc} @@ -440,7 +454,7 @@ NavigableMap subMap(K fromKey, boolean fromInclusive, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - SortedMap headMap(K toKey); + @PolyModifiable SortedMap headMap(@PolyModifiable NavigableMap this, K toKey); /** * {@inheritDoc} @@ -452,7 +466,7 @@ NavigableMap subMap(K fromKey, boolean fromInclusive, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - SortedMap tailMap(K fromKey); + @PolyModifiable SortedMap tailMap(@PolyModifiable NavigableMap this, K fromKey); /** * {@inheritDoc} @@ -467,7 +481,7 @@ NavigableMap subMap(K fromKey, boolean fromInclusive, * @since 21 */ @SideEffectFree - default NavigableMap reversed() { + default @PolyModifiable NavigableMap reversed(@PolyModifiable NavigableMap this) { return this.descendingMap(); } } diff --git a/src/java.base/share/classes/java/util/NavigableSet.java b/src/java.base/share/classes/java/util/NavigableSet.java index 39730b58d1d2..9a661d7cebca 100644 --- a/src/java.base/share/classes/java/util/NavigableSet.java +++ b/src/java.base/share/classes/java/util/NavigableSet.java @@ -37,6 +37,9 @@ import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.SeqUngrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.dataflow.qual.Pure; @@ -100,7 +103,7 @@ */ @CFComment({"lock/nullness: Subclasses of this interface/class may opt to prohibit null elements"}) @AnnotatedFor({"lock", "nullness"}) -public interface NavigableSet extends SortedSet { +public @SeqUngrowable interface NavigableSet extends SortedSet { /** * Returns the greatest element in this set strictly less than the * given element, or {@code null} if there is no such element. @@ -187,7 +190,7 @@ public interface NavigableSet extends SortedSet { * @return an iterator over the elements in this set, in ascending order */ @SideEffectFree - @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty NavigableSet this); + @PolyModifiable @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty NavigableSet this); /** * Returns a reverse order view of the elements contained in this set. @@ -206,8 +209,7 @@ public interface NavigableSet extends SortedSet { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - NavigableSet descendingSet(); - + @PolyModifiable NavigableSet descendingSet(@PolyModifiable NavigableSet this); /** * Returns an iterator over the elements in this set, in descending order. * Equivalent in effect to {@code descendingSet().iterator()}. @@ -216,8 +218,7 @@ public interface NavigableSet extends SortedSet { */ @SideEffectFree @DoesNotUnrefineReceiver("modifiability") - Iterator descendingIterator(); - + @PolyModifiable Iterator descendingIterator(@PolyModifiable NavigableSet this); /** * Returns a view of the portion of this set whose elements range from * {@code fromElement} to {@code toElement}. If {@code fromElement} and @@ -254,7 +255,7 @@ public interface NavigableSet extends SortedSet { * {@code toElement} lies outside the bounds of the range. */ @SideEffectFree - NavigableSet subSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E fromElement, boolean fromInclusive, + @PolyModifiable NavigableSet subSet(@PolyModifiable NavigableSet this, @GuardSatisfied E fromElement, boolean fromInclusive, @GuardSatisfied E toElement, boolean toInclusive); /** @@ -285,7 +286,7 @@ NavigableSet subSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E f * bounds of the range */ @SideEffectFree - NavigableSet headSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E toElement, boolean inclusive); + @PolyModifiable NavigableSet headSet(@PolyModifiable NavigableSet this, @GuardSatisfied E toElement, boolean inclusive); /** * Returns a view of the portion of this set whose elements are greater @@ -315,7 +316,7 @@ NavigableSet subSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E f * bounds of the range */ @SideEffectFree - NavigableSet tailSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E fromElement, boolean inclusive); + @PolyModifiable NavigableSet tailSet(@PolyModifiable NavigableSet this, @GuardSatisfied E fromElement, boolean inclusive); /** * {@inheritDoc} @@ -327,7 +328,7 @@ NavigableSet subSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E f * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - SortedSet subSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E fromElement, @GuardSatisfied E toElement); + @PolyModifiable SortedSet subSet(@PolyModifiable NavigableSet this, @GuardSatisfied E fromElement, @GuardSatisfied E toElement); /** * {@inheritDoc} @@ -339,7 +340,7 @@ NavigableSet subSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E f * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - SortedSet headSet(@GuardSatisfied NavigableSet this, E toElement); + @PolyModifiable SortedSet headSet(@PolyModifiable NavigableSet this, E toElement); /** * {@inheritDoc} @@ -351,7 +352,7 @@ NavigableSet subSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E f * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - SortedSet tailSet(@GuardSatisfied NavigableSet this, E fromElement); + @PolyModifiable SortedSet tailSet(@PolyModifiable NavigableSet this, E fromElement); /** * {@inheritDoc} @@ -366,7 +367,7 @@ NavigableSet subSet(@GuardSatisfied NavigableSet this, @GuardSatisfied E f */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default E removeFirst() { + default E removeFirst(@Shrinkable NavigableSet this) { if (this.isEmpty()) { throw new NoSuchElementException(); } else { @@ -387,7 +388,7 @@ default E removeFirst() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default E removeLast() { + default E removeLast(@Shrinkable NavigableSet this) { if (this.isEmpty()) { throw new NoSuchElementException(); } else { @@ -409,7 +410,7 @@ default E removeLast() { */ @SideEffectFree @DoesNotUnrefineReceiver("modifiability") - default NavigableSet reversed() { + default @PolyModifiable NavigableSet reversed(@PolyModifiable NavigableSet this) { return this.descendingSet(); } } diff --git a/src/java.base/share/classes/java/util/PriorityQueue.java b/src/java.base/share/classes/java/util/PriorityQueue.java index 8c6d95d1c8a0..7481c98f2794 100644 --- a/src/java.base/share/classes/java/util/PriorityQueue.java +++ b/src/java.base/share/classes/java/util/PriorityQueue.java @@ -30,6 +30,11 @@ import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.index.qual.Positive; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -105,7 +110,7 @@ * @param the type of elements held in this queue */ @CFComment({"lock/nullness: This class doesn't permits null elements"}) -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) @SuppressWarnings("unchecked") public class PriorityQueue extends AbstractQueue implements java.io.Serializable { @@ -148,7 +153,7 @@ public class PriorityQueue extends AbstractQueue * capacity (11) that orders its elements according to their * {@linkplain Comparable natural ordering}. */ - public PriorityQueue() { + public @Modifiable @IteratorPolyMod PriorityQueue() { this(DEFAULT_INITIAL_CAPACITY, null); } @@ -161,7 +166,7 @@ public PriorityQueue() { * @throws IllegalArgumentException if {@code initialCapacity} is less * than 1 */ - public PriorityQueue(@Positive int initialCapacity) { + public @Modifiable @IteratorPolyMod PriorityQueue(@Positive int initialCapacity) { this(initialCapacity, null); } @@ -174,7 +179,7 @@ public PriorityQueue(@Positive int initialCapacity) { * natural ordering} of the elements will be used. * @since 1.8 */ - public PriorityQueue(@Nullable Comparator comparator) { + public @Modifiable @IteratorPolyMod PriorityQueue(@Nullable Comparator comparator) { this(DEFAULT_INITIAL_CAPACITY, comparator); } @@ -189,7 +194,7 @@ public PriorityQueue(@Nullable Comparator comparator) { * @throws IllegalArgumentException if {@code initialCapacity} is * less than 1 */ - public PriorityQueue(@Positive int initialCapacity, + public @Modifiable @IteratorPolyMod PriorityQueue(@Positive int initialCapacity, @Nullable Comparator comparator) { // Note: This restriction of at least one is not actually needed, // but continues for 1.5 compatibility @@ -215,7 +220,7 @@ public PriorityQueue(@Positive int initialCapacity, * @throws NullPointerException if the specified collection or any * of its elements are null */ - public @PolyNonEmpty PriorityQueue(@PolyNonEmpty Collection c) { + public @Modifiable @IteratorPolyMod @PolyNonEmpty PriorityQueue(@PolyNonEmpty Collection c) { if (c instanceof SortedSet) { SortedSet ss = (SortedSet) c; this.comparator = (Comparator) ss.comparator(); @@ -246,7 +251,7 @@ else if (c instanceof PriorityQueue) { * @throws NullPointerException if the specified priority queue or any * of its elements are null */ - public PriorityQueue(PriorityQueue c) { + public @Modifiable @IteratorPolyMod PriorityQueue(PriorityQueue c) { this.comparator = (Comparator) c.comparator(); initFromPriorityQueue(c); } @@ -264,7 +269,7 @@ public PriorityQueue(PriorityQueue c) { * @throws NullPointerException if the specified sorted set or any * of its elements are null */ - public PriorityQueue(SortedSet c) { + public @Modifiable @IteratorPolyMod PriorityQueue(SortedSet c) { this.comparator = (Comparator) c.comparator(); initElementsFromCollection(c); } @@ -333,7 +338,7 @@ private void grow(int minCapacity) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(@GuardSatisfied PriorityQueue this, E e) { + public boolean add(@Growable @GuardSatisfied PriorityQueue this, E e) { return offer(e); } @@ -348,7 +353,7 @@ public boolean add(@GuardSatisfied PriorityQueue this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable PriorityQueue this, E e) { if (e == null) throw new NullPointerException(); modCount++; @@ -389,7 +394,7 @@ private int indexOf(@GuardSatisfied @Nullable @UnknownSignedness Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @CanShrink PriorityQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @GuardSatisfied @CanShrink PriorityQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { int i = indexOf(o); if (i == -1) return false; @@ -500,7 +505,7 @@ public boolean contains(@GuardSatisfied PriorityQueue this, @GuardSatisfied @ * @return an iterator over the elements in this queue */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty PriorityQueue this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty PriorityQueue this) { return new Itr(); } @@ -605,7 +610,7 @@ public void remove() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink PriorityQueue this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink PriorityQueue this) { modCount++; final Object[] es = queue; for (int i = 0, n = size; i < n; i++) @@ -615,7 +620,7 @@ public void clear(@GuardSatisfied @CanShrink PriorityQueue this) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink PriorityQueue this) { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink PriorityQueue this) { final Object[] es; final E result; @@ -941,7 +946,7 @@ public int characteristics() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@GuardSatisfied @CanShrink PriorityQueue this, Predicate filter) { + public boolean removeIf(@Shrinkable @GuardSatisfied @CanShrink PriorityQueue this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } diff --git a/src/java.base/share/classes/java/util/Properties.java b/src/java.base/share/classes/java/util/Properties.java index f537a320ec9f..0a0868820635 100644 --- a/src/java.base/share/classes/java/util/Properties.java +++ b/src/java.base/share/classes/java/util/Properties.java @@ -26,6 +26,14 @@ package java.util; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nullness.qual.KeyFor; import org.checkerframework.checker.nullness.qual.Nullable; @@ -154,7 +162,7 @@ * @author Xueming Shen * @since 1.0 */ -@AnnotatedFor({"index", "lock", "nullness", "propkey"}) +@AnnotatedFor({"index", "lock", "nullness", "propkey", "modifiability"}) public class Properties extends Hashtable { /** * use serialVersionUID from JDK 1.1.X for interoperability @@ -1372,28 +1380,28 @@ public boolean containsKey(@GuardSatisfied @Nullable @UnknownSignedness Object k @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized Object put(Object key, Object value) { + public synchronized Object put(@Growable @Replaceable Properties this, Object key, Object value) { return map.put(key, value); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized Object remove(@GuardSatisfied @Nullable @UnknownSignedness Object key) { + public synchronized Object remove(@Shrinkable Properties this, @GuardSatisfied @Nullable @UnknownSignedness Object key) { return map.remove(key); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized void putAll(Map t) { + public synchronized void putAll(@Growable @Replaceable Properties this, Map t) { map.putAll(t); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized void clear() { + public synchronized void clear(@Shrinkable Properties this) { map.clear(); } @@ -1405,19 +1413,19 @@ public synchronized String toString() { @Override @SideEffectFree - public Set<@KeyFor("this") Object> keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@KeyFor("this") Object> keySet(@PolyShrinkable Properties this) { return Collections.synchronizedSet(map.keySet(), this); } @Override @SideEffectFree - public Collection values() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable Properties this) { return Collections.synchronizedCollection(map.values(), this); } @Override @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable Properties this) { return Collections.synchronizedSet(new EntrySet(map.entrySet()), this); } @@ -1506,21 +1514,22 @@ public Iterator> iterator() { } } - @Override @Pure + @Override public synchronized boolean equals(@Nullable Object o) { return map.equals(o); } - @Override @Pure + @Override public synchronized int hashCode() { return map.hashCode(); } @Override @Pure - public Object getOrDefault(@GuardSatisfied @Nullable @UnknownSignedness Object key, Object defaultValue) { + public @PolyModifiable Object getOrDefault(@GuardSatisfied @Nullable @UnknownSignedness Object key, + @PolyModifiable Object defaultValue) { return map.getOrDefault(key, defaultValue); } @@ -1532,62 +1541,62 @@ public synchronized void forEach(BiConsumer acti @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized void replaceAll(BiFunction function) { + public synchronized void replaceAll(@Replaceable Properties this, BiFunction function) { map.replaceAll(function); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized Object putIfAbsent(Object key, Object value) { + public synchronized Object putIfAbsent(@Growable Properties this, Object key, Object value) { return map.putIfAbsent(key, value); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized boolean remove(@GuardSatisfied @Nullable @UnknownSignedness Object key, @GuardSatisfied @Nullable @UnknownSignedness Object value) { + public synchronized boolean remove(@Shrinkable Properties this, @GuardSatisfied @Nullable @UnknownSignedness Object key, @GuardSatisfied @Nullable @UnknownSignedness Object value) { return map.remove(key, value); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized boolean replace(Object key, Object oldValue, Object newValue) { + public synchronized boolean replace(@Replaceable Properties this, Object key, Object oldValue, Object newValue) { return map.replace(key, oldValue, newValue); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized @Nullable Object replace(Object key, Object value) { + public synchronized @Nullable Object replace(@Replaceable Properties this, Object key, Object value) { return map.replace(key, value); } @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized @PolyNull Object computeIfAbsent(Object key, + public synchronized @PolyNull Object computeIfAbsent(@Growable Properties this, Object key, Function mappingFunction) { return map.computeIfAbsent(key, mappingFunction); } @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized @Nullable Object computeIfPresent(Object key, + public synchronized @Nullable Object computeIfPresent(@Shrinkable @Replaceable Properties this, Object key, BiFunction remappingFunction) { return map.computeIfPresent(key, remappingFunction); } @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized @PolyNull Object compute(Object key, + public synchronized @PolyNull Object compute(@Modifiable Properties this, Object key, BiFunction remappingFunction) { return map.compute(key, remappingFunction); } @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized @Nullable Object merge(Object key, Object value, + public synchronized @Nullable Object merge(@Modifiable Properties this, Object key, Object value, BiFunction remappingFunction) { return map.merge(key, value, remappingFunction); } @@ -1600,7 +1609,7 @@ protected void rehash() { /* no-op */ } @Override @SideEffectFree - public synchronized Object clone() { + public synchronized @Modifiable Object clone() { Properties clone = (Properties) cloneHashtable(); clone.map = new ConcurrentHashMap<>(map); return clone; diff --git a/src/java.base/share/classes/java/util/Queue.java b/src/java.base/share/classes/java/util/Queue.java index 1f4525b91ee2..cb4654a6a43d 100644 --- a/src/java.base/share/classes/java/util/Queue.java +++ b/src/java.base/share/classes/java/util/Queue.java @@ -37,6 +37,9 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -148,7 +151,7 @@ * @param the type of elements held in this queue */ @CFComment({"lock/nullness: Subclasses of this interface/class may opt to prohibit null elements"}) -@AnnotatedFor({"lock", "nullness"}) +@AnnotatedFor({"lock", "nullness", "modifiability"}) public interface Queue extends Collection { /** * Inserts the specified element into this queue if it is possible to do so @@ -170,8 +173,7 @@ public interface Queue extends Collection { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean add(@GuardSatisfied Queue this, E e); - + boolean add(@Growable @GuardSatisfied Queue this, E e); /** * Inserts the specified element into this queue if it is possible to do * so immediately without violating capacity restrictions. @@ -191,8 +193,7 @@ public interface Queue extends Collection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offer(E e); - + boolean offer(@Growable Queue this, E e); /** * Retrieves and removes the head of this queue. This method differs * from {@link #poll() poll()} only in that it throws an exception if @@ -203,8 +204,7 @@ public interface Queue extends Collection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E remove(@GuardSatisfied @NonEmpty @CanShrink Queue this); - + E remove(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink Queue this); /** * Retrieves and removes the head of this queue, * or returns {@code null} if this queue is empty. @@ -213,8 +213,7 @@ public interface Queue extends Collection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable E poll(@GuardSatisfied @CanShrink Queue this); - + @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink Queue this); /** * Retrieves, but does not remove, the head of this queue. This method * differs from {@link #peek peek} only in that it throws an exception @@ -232,7 +231,6 @@ public interface Queue extends Collection { * * @return the head of this queue, or {@code null} if this queue is empty */ - @DoesNotUnrefineReceiver("modifiability") @Pure @Nullable E peek(); } diff --git a/src/java.base/share/classes/java/util/RegularEnumSet.java b/src/java.base/share/classes/java/util/RegularEnumSet.java index 759c2209fbdc..9d1db0f7acba 100644 --- a/src/java.base/share/classes/java/util/RegularEnumSet.java +++ b/src/java.base/share/classes/java/util/RegularEnumSet.java @@ -97,6 +97,7 @@ void complement() { * @return an iterator over the elements contained in this set */ @SideEffectFree + @DoesNotUnrefineReceiver("modifiability") public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty RegularEnumSet this) { return new EnumSetIterator<>(); } diff --git a/src/java.base/share/classes/java/util/ReverseOrderDequeView.java b/src/java.base/share/classes/java/util/ReverseOrderDequeView.java index 80ad06f0e092..de9992626695 100644 --- a/src/java.base/share/classes/java/util/ReverseOrderDequeView.java +++ b/src/java.base/share/classes/java/util/ReverseOrderDequeView.java @@ -186,6 +186,7 @@ public T[] toArray(T[] a) { return ArraysSupport.toArrayReversed(base, a); } + @SideEffectFree public T[] toArray(IntFunction generator) { return ArraysSupport.reverse(base.toArray(generator)); } diff --git a/src/java.base/share/classes/java/util/ReverseOrderListView.java b/src/java.base/share/classes/java/util/ReverseOrderListView.java index bfff797ecadc..2223d8fb1eb1 100644 --- a/src/java.base/share/classes/java/util/ReverseOrderListView.java +++ b/src/java.base/share/classes/java/util/ReverseOrderListView.java @@ -27,6 +27,7 @@ import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.common.value.qual.StaticallyExecutable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; @@ -321,6 +322,7 @@ public T[] toArray(T[] a) { return ArraysSupport.toArrayReversed(base, a); } + @SideEffectFree public T[] toArray(IntFunction generator) { return ArraysSupport.reverse(base.toArray(generator)); } @@ -374,12 +376,14 @@ public E get(int i) { } @Pure + @StaticallyExecutable public int indexOf(Object o) { int i = base.lastIndexOf(o); return i == -1 ? -1 : base.size() - i - 1; } @Pure + @StaticallyExecutable public int lastIndexOf(Object o) { int i = base.indexOf(o); return i == -1 ? -1 : base.size() - i - 1; diff --git a/src/java.base/share/classes/java/util/ReverseOrderSortedMapView.java b/src/java.base/share/classes/java/util/ReverseOrderSortedMapView.java index 3653cca48331..1169732bad82 100644 --- a/src/java.base/share/classes/java/util/ReverseOrderSortedMapView.java +++ b/src/java.base/share/classes/java/util/ReverseOrderSortedMapView.java @@ -25,15 +25,25 @@ package java.util; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; +import org.checkerframework.framework.qual.AnnotatedFor; /** * Provides a reversed-ordered view of a SortedMap. Not serializable. * * TODO: copy in equals and hashCode from AbstractMap */ +@AnnotatedFor({"modifiability"}) class ReverseOrderSortedMapView extends AbstractMap implements SortedMap { final SortedMap base; final Comparator cmp; @@ -65,7 +75,7 @@ public String toString() { // ========== Map ========== - public void clear() { + public void clear(@Shrinkable ReverseOrderSortedMapView this) { base.clear(); } @@ -89,15 +99,15 @@ public boolean isEmpty() { return base.isEmpty(); } - public @Nullable V put(K key, V value) { + public @Nullable V put(@Growable @Replaceable ReverseOrderSortedMapView this, K key, V value) { return base.put(key, value); } - public void putAll(Map m) { + public void putAll(@Growable @Replaceable ReverseOrderSortedMapView this, Map m) { base.putAll(m); } - public @Nullable V remove(Object key) { + public @Nullable V remove(@Shrinkable ReverseOrderSortedMapView this, Object key) { return base.remove(key); } @@ -107,7 +117,7 @@ public int size() { } @SideEffectFree - public Set keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set keySet(@PolyShrinkable ReverseOrderSortedMapView this) { return new AbstractSet<>() { // inherit add(), which throws UOE @SideEffectFree @@ -122,7 +132,7 @@ public Set keySet() { } @SideEffectFree - public Collection values() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable ReverseOrderSortedMapView this) { return new AbstractCollection<>() { // inherit add(), which throws UOE @SideEffectFree @@ -137,7 +147,7 @@ public Collection values() { } @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable ReverseOrderSortedMapView this) { return new AbstractSet<>() { // inherit add(), which throws UOE @SideEffectFree @@ -158,10 +168,12 @@ public SortedMap reversed() { return base; } + @Pure public K firstKey() { return base.lastKey(); } + @Pure public K lastKey() { return base.firstKey(); } @@ -174,19 +186,19 @@ public K lastKey() { return base.firstEntry(); } - public Map.@Nullable Entry pollFirstEntry() { + public Map.@Nullable Entry pollFirstEntry(@Shrinkable ReverseOrderSortedMapView this) { return base.pollLastEntry(); } - public Map.@Nullable Entry pollLastEntry() { + public Map.@Nullable Entry pollLastEntry(@Shrinkable ReverseOrderSortedMapView this) { return base.pollFirstEntry(); } - public @Nullable V putFirst(K k, V v) { + public @Nullable V putFirst(@SeqGrowable ReverseOrderSortedMapView this, K k, V v) { return base.putLast(k, v); } - public @Nullable V putLast(K k, V v) { + public @Nullable V putLast(@SeqGrowable ReverseOrderSortedMapView this, K k, V v) { return base.putFirst(k, v); } @@ -198,7 +210,7 @@ public K lastKey() { } @SideEffectFree - public SortedMap subMap(K fromKey, K toKey) { + public @PolyModifiable SortedMap subMap(@PolyModifiable ReverseOrderSortedMapView this, K fromKey, K toKey) { if (cmp.compare(fromKey, toKey) <= 0) { return new Submap(fromKey, toKey); } else { @@ -207,12 +219,12 @@ public SortedMap subMap(K fromKey, K toKey) { } @SideEffectFree - public SortedMap headMap(K toKey) { + public @PolyModifiable SortedMap headMap(@PolyModifiable ReverseOrderSortedMapView this, K toKey) { return new Submap(null, toKey); } @SideEffectFree - public SortedMap tailMap(K fromKey) { + public @PolyModifiable SortedMap tailMap(@PolyModifiable ReverseOrderSortedMapView this, K fromKey) { return new Submap(fromKey, null); } @@ -302,7 +314,7 @@ static class ViewEntry implements Map.Entry { public K getKey() { return key; } @Pure public V getValue() { return value; } - public @Nullable V setValue(V newValue) { return map.put(key, newValue); } + public @Nullable V setValue(@Replaceable ViewEntry this, V newValue) { return map.put(key, newValue); } @Pure public boolean equals(@Nullable Object o) { @@ -431,7 +443,7 @@ public String toString() { } @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable Submap this) { return new AbstractSet<>() { @SideEffectFree public Iterator> iterator() { @@ -450,14 +462,14 @@ public int size() { }; } - public @Nullable V put(K key, V value) { + public @Nullable V put(@Growable @Replaceable Submap this, K key, V value) { if (aboveHead(key) && belowTail(key)) return base.put(key, value); else throw new IllegalArgumentException(); } - public @Nullable V remove(Object o) { + public @Nullable V remove(@Shrinkable Submap this, Object o) { @SuppressWarnings("unchecked") K key = (K) o; if (aboveHead(key) && belowTail(key)) @@ -476,10 +488,12 @@ public int size() { return cmp; } + @Pure public K firstKey() { return this.entryIterator().next().getKey(); } + @Pure public K lastKey() { var it = this.entryIterator(); if (! it.hasNext()) @@ -491,7 +505,7 @@ public K lastKey() { } @SideEffectFree - public SortedMap subMap(K from, K to) { + public @PolyModifiable SortedMap subMap(@PolyModifiable Submap this, K from, K to) { if (aboveHead(from) && belowTail(from) && aboveHead(to) && belowTail(to) && cmp.compare(from, to) <= 0) { @@ -502,7 +516,7 @@ public SortedMap subMap(K from, K to) { } @SideEffectFree - public SortedMap headMap(K to) { + public @PolyModifiable SortedMap headMap(@PolyModifiable Submap this, K to) { if (aboveHead(to) && belowTail(to)) return new Submap(head, to); else @@ -510,7 +524,7 @@ public SortedMap headMap(K to) { } @SideEffectFree - public SortedMap tailMap(K from) { + public @PolyModifiable SortedMap tailMap(@PolyModifiable Submap this, K from) { if (aboveHead(from) && belowTail(from)) return new Submap(from, tail); else diff --git a/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java b/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java index 671c4cd363b5..385db239437d 100644 --- a/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java +++ b/src/java.base/share/classes/java/util/ReverseOrderSortedSetView.java @@ -196,6 +196,7 @@ public T[] toArray(T[] a) { return ArraysSupport.toArrayReversed(base, a); } + @SideEffectFree public T[] toArray(IntFunction generator) { return ArraysSupport.reverse(base.toArray(generator)); } @@ -207,8 +208,10 @@ public T[] toArray(IntFunction generator) { return comp; } + @Pure public E first() { return base.last(); } + @Pure public E last() { return base.first(); } @SideEffectFree @@ -362,12 +365,12 @@ public int size() { return ReverseOrderSortedSetView.this.comparator(); } - @SideEffectFree + @Pure public E first() { return this.iterator().next(); } - @SideEffectFree + @Pure public E last() { var it = this.iterator(); if (! it.hasNext()) diff --git a/src/java.base/share/classes/java/util/SequencedCollection.java b/src/java.base/share/classes/java/util/SequencedCollection.java index 6f76fddfa78e..5abb2fcb53e0 100644 --- a/src/java.base/share/classes/java/util/SequencedCollection.java +++ b/src/java.base/share/classes/java/util/SequencedCollection.java @@ -25,6 +25,9 @@ package java.util; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; @@ -114,7 +117,7 @@ public interface SequencedCollection extends Collection { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default void addFirst(E e) { + default void addFirst(@SeqGrowable SequencedCollection this, E e) { throw new UnsupportedOperationException(); } @@ -135,7 +138,7 @@ default void addFirst(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default void addLast(E e) { + default void addLast(@SeqGrowable SequencedCollection this, E e) { throw new UnsupportedOperationException(); } @@ -192,7 +195,7 @@ default E getLast() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default E removeFirst() { + default E removeFirst(@IteratorPolyMod @Shrinkable SequencedCollection this) { var it = this.iterator(); E e = it.next(); it.remove(); @@ -216,7 +219,7 @@ default E removeFirst() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default E removeLast() { + default E removeLast(@IteratorPolyMod @Shrinkable SequencedCollection this) { var it = this.reversed().iterator(); E e = it.next(); it.remove(); diff --git a/src/java.base/share/classes/java/util/SequencedMap.java b/src/java.base/share/classes/java/util/SequencedMap.java index 486dfcc47877..cf781cfa3560 100644 --- a/src/java.base/share/classes/java/util/SequencedMap.java +++ b/src/java.base/share/classes/java/util/SequencedMap.java @@ -25,6 +25,11 @@ package java.util; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; @@ -156,6 +161,7 @@ public interface SequencedMap extends Map { * @return the first key-value mapping, * or {@code null} if this map is empty */ + @SideEffectFree default Map.@Nullable Entry firstEntry() { var it = entrySet().iterator(); return it.hasNext() ? new NullableKeyValueHolder<>(it.next()) : null; @@ -173,6 +179,7 @@ public interface SequencedMap extends Map { * @return the last key-value mapping, * or {@code null} if this map is empty */ + @SideEffectFree default Map.@Nullable Entry lastEntry() { var it = reversed().entrySet().iterator(); return it.hasNext() ? new NullableKeyValueHolder<>(it.next()) : null; @@ -194,7 +201,7 @@ public interface SequencedMap extends Map { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default Map.@Nullable Entry pollFirstEntry() { + default Map.@Nullable Entry pollFirstEntry(@Shrinkable SequencedMap this) { var it = entrySet().iterator(); if (it.hasNext()) { var entry = new NullableKeyValueHolder<>(it.next()); @@ -221,7 +228,7 @@ public interface SequencedMap extends Map { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default Map.@Nullable Entry pollLastEntry() { + default Map.@Nullable Entry pollLastEntry(@Shrinkable SequencedMap this) { var it = reversed().entrySet().iterator(); if (it.hasNext()) { var entry = new NullableKeyValueHolder<>(it.next()); @@ -249,7 +256,7 @@ public interface SequencedMap extends Map { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default @Nullable V putFirst(K k, V v) { + default @Nullable V putFirst(@SeqGrowable SequencedMap this, K k, V v) { throw new UnsupportedOperationException(); } @@ -270,7 +277,7 @@ public interface SequencedMap extends Map { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default @Nullable V putLast(K k, V v) { + default @Nullable V putLast(@SeqGrowable SequencedMap this, K k, V v) { throw new UnsupportedOperationException(); } @@ -290,7 +297,7 @@ public interface SequencedMap extends Map { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default SequencedSet sequencedKeySet() { + default @PolyShrinkable @Ungrowable SequencedSet sequencedKeySet(@PolyShrinkable SequencedMap this) { class SeqKeySet extends AbstractMap.ViewCollection implements SequencedSet { Collection view() { return SequencedMap.this.keySet(); @@ -328,7 +335,7 @@ public int hashCode() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default SequencedCollection sequencedValues() { + default @PolyShrinkable @Ungrowable SequencedCollection sequencedValues(@PolyShrinkable SequencedMap this) { class SeqValues extends AbstractMap.ViewCollection implements SequencedCollection { Collection view() { return SequencedMap.this.values(); @@ -357,7 +364,7 @@ public SequencedCollection reversed() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default SequencedSet> sequencedEntrySet() { + default @PolyShrinkable @Ungrowable SequencedSet> sequencedEntrySet(@PolyModifiable SequencedMap this) { class SeqEntrySet extends AbstractMap.ViewCollection> implements SequencedSet> { Collection> view() { diff --git a/src/java.base/share/classes/java/util/Set.java b/src/java.base/share/classes/java/util/Set.java index e59991de6c8b..9400e3d6f09f 100644 --- a/src/java.base/share/classes/java/util/Set.java +++ b/src/java.base/share/classes/java/util/Set.java @@ -28,6 +28,12 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -143,7 +149,7 @@ public interface Set extends Collection { * @return the number of elements in this set (its cardinality) */ @Pure - @NonNegative int size(@GuardSatisfied Set this); + @NonNegative int size(@MaybeModifiable @GuardSatisfied Set this); /** * Returns {@code true} if this set contains no elements. @@ -152,7 +158,7 @@ public interface Set extends Collection { */ @Pure @EnsuresNonEmptyIf(result = false, expression = "this") - boolean isEmpty(@GuardSatisfied Set this); + boolean isEmpty(@MaybeModifiable @GuardSatisfied Set this); /** * Returns {@code true} if this set contains the specified element. @@ -171,7 +177,7 @@ public interface Set extends Collection { */ @Pure @EnsuresNonEmptyIf(result = true, expression = "this") - boolean contains(@GuardSatisfied Set this, @GuardSatisfied @UnknownSignedness Object o); + boolean contains(@MaybeModifiable @GuardSatisfied Set this, @GuardSatisfied @UnknownSignedness Object o); /** * Returns an iterator over the elements in this set. The elements are @@ -181,7 +187,7 @@ public interface Set extends Collection { * @return an iterator over the elements in this set */ @SideEffectFree - @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty Set this); + @MaybeModifiable @PolyGrowShrink @PolyNonEmpty Iterator iterator(@MaybeModifiable @PolyGrowShrink @PolyNonEmpty Set this); /** * Returns an array containing all of the elements in this set. @@ -200,7 +206,7 @@ public interface Set extends Collection { * @return an array containing all the elements in this set */ @SideEffectFree - @PolyNull @PolySigned Object[] toArray(Set<@PolyNull @PolySigned E> this); + @PolyNull @PolySigned Object[] toArray(@MaybeModifiable Set<@PolyNull @PolySigned E> this); /** * Returns an array containing all of the elements in this set; the @@ -244,7 +250,7 @@ public interface Set extends Collection { * set * @throws NullPointerException if the specified array is null */ - @Nullable T [] toArray(@PolyNull T[] a); + @Nullable T [] toArray(@MaybeModifiable Set this, @PolyNull T[] a); // Modification Operations @@ -282,7 +288,7 @@ public interface Set extends Collection { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean add(@GuardSatisfied Set this, E e); + boolean add(@Growable @GuardSatisfied Set this, E e); /** @@ -308,7 +314,7 @@ public interface Set extends Collection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean remove(@GuardSatisfied Set this, @UnknownSignedness Object o); + boolean remove(@Shrinkable @GuardSatisfied Set this, @UnknownSignedness Object o); // Bulk Operations @@ -333,7 +339,7 @@ public interface Set extends Collection { * @see #contains(Object) */ @Pure - boolean containsAll(@GuardSatisfied Set this, @GuardSatisfied Collection c); + boolean containsAll(@MaybeModifiable @GuardSatisfied Set this, @GuardSatisfied Collection c); /** * Adds all of the elements in the specified collection to this set if @@ -360,7 +366,7 @@ public interface Set extends Collection { @EnsuresNonEmptyIf(result = true, expression = "this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean addAll(@GuardSatisfied Set this, Collection c); + boolean addAll(@Growable @GuardSatisfied Set this, Collection c); /** * Retains only the elements in this set that are contained in the @@ -385,7 +391,7 @@ public interface Set extends Collection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean retainAll(@GuardSatisfied Set this, Collection c); + boolean retainAll(@Shrinkable @GuardSatisfied Set this, Collection c); /** * Removes from this set all of its elements that are contained in the @@ -410,7 +416,7 @@ public interface Set extends Collection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean removeAll(@GuardSatisfied Set this, Collection c); + boolean removeAll(@IteratorPolyMod @Shrinkable @GuardSatisfied Set this, Collection c); /** * Removes all of the elements from this set (optional operation). @@ -421,7 +427,7 @@ public interface Set extends Collection { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void clear(@GuardSatisfied Set this); + void clear(@Shrinkable @GuardSatisfied Set this); // Comparison and hashing @@ -439,7 +445,7 @@ public interface Set extends Collection { * @return {@code true} if the specified object is equal to this set */ @Pure - boolean equals(@GuardSatisfied Set this, @GuardSatisfied @Nullable Object o); + boolean equals(@MaybeModifiable @GuardSatisfied Set this, @GuardSatisfied @Nullable Object o); /** * Returns the hash code value for this set. The hash code of a set is @@ -455,7 +461,7 @@ public interface Set extends Collection { * @see Set#equals(Object) */ @Pure - int hashCode(@GuardSatisfied Set this); + int hashCode(@MaybeModifiable @GuardSatisfied Set this); /** * Creates a {@code Spliterator} over the elements in this set. @@ -482,7 +488,8 @@ public interface Set extends Collection { */ @Override @SideEffectFree - default Spliterator spliterator() { + @DoesNotUnrefineReceiver("modifiability") + default Spliterator spliterator(@MaybeModifiable Set this) { return Spliterators.spliterator(this, Spliterator.DISTINCT); } @@ -497,7 +504,7 @@ default Spliterator spliterator() { */ @SuppressWarnings("unchecked") @SideEffectFree - static Set of() { + static @Unmodifiable Set of() { return (Set) ImmutableCollections.EMPTY_SET; } @@ -513,7 +520,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1) { + static @Unmodifiable @NonEmpty Set of(E e1) { return new ImmutableCollections.Set12<>(e1); } @@ -531,7 +538,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1, E e2) { + static @Unmodifiable @NonEmpty Set of(E e1, E e2) { return new ImmutableCollections.Set12<>(e1, e2); } @@ -550,7 +557,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1, E e2, E e3) { + static @Unmodifiable @NonEmpty Set of(E e1, E e2, E e3) { return new ImmutableCollections.SetN<>(e1, e2, e3); } @@ -570,7 +577,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1, E e2, E e3, E e4) { + static @Unmodifiable @NonEmpty Set of(E e1, E e2, E e3, E e4) { return new ImmutableCollections.SetN<>(e1, e2, e3, e4); } @@ -591,7 +598,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5) { + static @Unmodifiable @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5) { return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5); } @@ -613,7 +620,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6) { + static @Unmodifiable @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6) { return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5, e6); } @@ -637,7 +644,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + static @Unmodifiable @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5, e6, e7); } @@ -662,7 +669,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + static @Unmodifiable @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5, e6, e7, e8); } @@ -688,7 +695,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + static @Unmodifiable @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9); } @@ -715,7 +722,7 @@ static Set of() { * @since 9 */ @SideEffectFree - static @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + static @Unmodifiable @NonEmpty Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); } @@ -749,7 +756,7 @@ static Set of() { @SafeVarargs @SuppressWarnings("varargs") @SideEffectFree - static Set of(E... elements) { + static @Unmodifiable Set of(E... elements) { switch (elements.length) { // implicit null check of elements case 0: @SuppressWarnings("unchecked") @@ -783,7 +790,7 @@ static Set of(E... elements) { */ @SuppressWarnings("unchecked") @SideEffectFree - static @PolyNonEmpty Set copyOf(@PolyNonEmpty Collection coll) { + static @Unmodifiable @PolyNonEmpty Set copyOf(@MaybeModifiable @PolyNonEmpty Collection coll) { if (coll instanceof ImmutableCollections.AbstractImmutableSet) { return (Set)coll; } else if (coll.isEmpty()) { // Implicit nullcheck of coll diff --git a/src/java.base/share/classes/java/util/SortedMap.java b/src/java.base/share/classes/java/util/SortedMap.java index 8116e8ec9ddf..80620c5dc68d 100644 --- a/src/java.base/share/classes/java/util/SortedMap.java +++ b/src/java.base/share/classes/java/util/SortedMap.java @@ -26,6 +26,12 @@ package java.util; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.SeqUngrowable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nullness.qual.KeyFor; import org.checkerframework.checker.nullness.qual.Nullable; @@ -123,7 +129,7 @@ @CFComment({"lock/nullness: Subclasses of this interface/class may opt to prohibit null elements"}) @AnnotatedFor({"lock", "nullness"}) -public interface SortedMap extends SequencedMap { +public @SeqUngrowable interface SortedMap extends SequencedMap { /** * Returns the comparator used to order the keys in this map, or * {@code null} if this map uses the {@linkplain Comparable @@ -166,7 +172,7 @@ public interface SortedMap extends SequencedMap { * outside the bounds of the range */ @SideEffectFree - SortedMap subMap(@GuardSatisfied SortedMap this, @GuardSatisfied K fromKey, @GuardSatisfied K toKey); + @PolyModifiable SortedMap subMap(@PolyModifiable @GuardSatisfied SortedMap this, @GuardSatisfied K fromKey, @GuardSatisfied K toKey); /** * Returns a view of the portion of this map whose keys are @@ -194,7 +200,7 @@ public interface SortedMap extends SequencedMap { * bounds of the range */ @SideEffectFree - SortedMap headMap(@GuardSatisfied SortedMap this, K toKey); + @PolyModifiable SortedMap headMap(@PolyModifiable @GuardSatisfied SortedMap this, K toKey); /** * Returns a view of the portion of this map whose keys are @@ -222,7 +228,7 @@ public interface SortedMap extends SequencedMap { * bounds of the range */ @SideEffectFree - SortedMap tailMap(@GuardSatisfied SortedMap this, K fromKey); + @PolyModifiable SortedMap tailMap(@PolyModifiable @GuardSatisfied SortedMap this, K fromKey); /** * Returns the first (lowest) key currently in this map. @@ -230,7 +236,7 @@ public interface SortedMap extends SequencedMap { * @return the first (lowest) key currently in this map * @throws NoSuchElementException if this map is empty */ - @SideEffectFree + @Pure @KeyFor("this") K firstKey(@GuardSatisfied @NonEmpty SortedMap this); /** @@ -239,7 +245,7 @@ public interface SortedMap extends SequencedMap { * @return the last (highest) key currently in this map * @throws NoSuchElementException if this map is empty */ - @SideEffectFree + @Pure @KeyFor("this") K lastKey(@GuardSatisfied @NonEmpty SortedMap this); /** @@ -260,7 +266,7 @@ public interface SortedMap extends SequencedMap { * ascending order */ @SideEffectFree - Set<@KeyFor({"this"}) K> keySet(@GuardSatisfied SortedMap this); + @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@KeyFor({"this"}) K> keySet(@PolyShrinkable @GuardSatisfied SortedMap this); /** * Returns a {@link Collection} view of the values contained in this map. @@ -281,7 +287,7 @@ public interface SortedMap extends SequencedMap { * sorted in ascending key order */ @SideEffectFree - Collection values(@GuardSatisfied SortedMap this); + @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable @GuardSatisfied SortedMap this); /** * Returns a {@link Set} view of the mappings contained in this map. @@ -302,7 +308,7 @@ public interface SortedMap extends SequencedMap { * sorted in ascending key order */ @SideEffectFree - Set> entrySet(@GuardSatisfied SortedMap this); + @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable @GuardSatisfied SortedMap this); /** * Throws {@code UnsupportedOperationException}. The encounter order induced by this @@ -317,7 +323,7 @@ public interface SortedMap extends SequencedMap { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default @Nullable V putFirst(K k, V v) { + default @Nullable V putFirst(@SeqGrowable SortedMap this, K k, V v) { throw new UnsupportedOperationException(); } @@ -334,7 +340,7 @@ public interface SortedMap extends SequencedMap { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default @Nullable V putLast(K k, V v) { + default @Nullable V putLast(@SeqGrowable SortedMap this, K k, V v) { throw new UnsupportedOperationException(); } @@ -350,7 +356,7 @@ public interface SortedMap extends SequencedMap { */ @SideEffectFree @DoesNotUnrefineReceiver("modifiability") - default SortedMap reversed() { + default @PolyModifiable SortedMap reversed(@PolyModifiable SortedMap this) { return ReverseOrderSortedMapView.of(this); } } diff --git a/src/java.base/share/classes/java/util/SortedSet.java b/src/java.base/share/classes/java/util/SortedSet.java index 923f4786a947..8f1a6a78f842 100644 --- a/src/java.base/share/classes/java/util/SortedSet.java +++ b/src/java.base/share/classes/java/util/SortedSet.java @@ -26,6 +26,10 @@ package java.util; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.SeqUngrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nullness.qual.Nullable; @@ -118,7 +122,7 @@ @CFComment({"lock/nullness: Subclasses of this interface/class may opt to prohibit null elements"}) @AnnotatedFor({"lock", "nullness"}) -public interface SortedSet extends Set, SequencedSet { +public @SeqUngrowable interface SortedSet extends Set, SequencedSet { /** * Returns the comparator used to order the elements in this set, * or {@code null} if this set uses the {@linkplain Comparable @@ -163,7 +167,7 @@ public interface SortedSet extends Set, SequencedSet { * {@code toElement} lies outside the bounds of the range */ @SideEffectFree - SortedSet subSet(@GuardSatisfied SortedSet this, @GuardSatisfied E fromElement, @GuardSatisfied E toElement); + @PolyModifiable SortedSet subSet(@PolyModifiable @GuardSatisfied SortedSet this, @GuardSatisfied E fromElement, @GuardSatisfied E toElement); /** * Returns a view of the portion of this set whose elements are @@ -191,7 +195,7 @@ public interface SortedSet extends Set, SequencedSet { * bounds of the range */ @SideEffectFree - SortedSet headSet(@GuardSatisfied SortedSet this, E toElement); + @PolyModifiable SortedSet headSet(@PolyModifiable @GuardSatisfied SortedSet this, E toElement); /** * Returns a view of the portion of this set whose elements are @@ -219,7 +223,7 @@ public interface SortedSet extends Set, SequencedSet { * bounds of the range */ @SideEffectFree - SortedSet tailSet(@GuardSatisfied SortedSet this, E fromElement); + @PolyModifiable SortedSet tailSet(@PolyModifiable @GuardSatisfied SortedSet this, E fromElement); /** * Returns the first (lowest) element currently in this set. @@ -227,7 +231,7 @@ public interface SortedSet extends Set, SequencedSet { * @return the first (lowest) element currently in this set * @throws NoSuchElementException if this set is empty */ - @SideEffectFree + @Pure E first(@GuardSatisfied @NonEmpty SortedSet this); /** @@ -236,7 +240,7 @@ public interface SortedSet extends Set, SequencedSet { * @return the last (highest) element currently in this set * @throws NoSuchElementException if this set is empty */ - @SideEffectFree + @Pure E last(@GuardSatisfied @NonEmpty SortedSet this); /** @@ -272,6 +276,7 @@ public interface SortedSet extends Set, SequencedSet { */ @Override @SideEffectFree + @DoesNotUnrefineReceiver("modifiability") default Spliterator spliterator() { return new Spliterators.IteratorSpliterator( this, Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED) { @@ -298,7 +303,7 @@ default Spliterator spliterator() { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default void addFirst(E e) { + default void addFirst(@SeqGrowable SortedSet this, E e) { throw new UnsupportedOperationException(); } @@ -316,7 +321,7 @@ default void addFirst(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default void addLast(E e) { + default void addLast(@SeqGrowable SortedSet this, E e) { throw new UnsupportedOperationException(); } @@ -364,7 +369,7 @@ default E getLast() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default E removeFirst() { + default E removeFirst(@Shrinkable SortedSet this) { E e = this.first(); this.remove(e); return e; @@ -384,7 +389,7 @@ default E removeFirst() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - default E removeLast() { + default E removeLast(@Shrinkable SortedSet this) { E e = this.last(); this.remove(e); return e; @@ -402,7 +407,7 @@ default E removeLast() { */ @SideEffectFree @DoesNotUnrefineReceiver("modifiability") - default SortedSet reversed() { + default @PolyModifiable SortedSet reversed(@PolyModifiable SortedSet this) { return ReverseOrderSortedSetView.of(this); } } diff --git a/src/java.base/share/classes/java/util/Stack.java b/src/java.base/share/classes/java/util/Stack.java index 04fef7b38263..8157acec9b07 100644 --- a/src/java.base/share/classes/java/util/Stack.java +++ b/src/java.base/share/classes/java/util/Stack.java @@ -27,6 +27,10 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.dataflow.qual.Pure; @@ -59,12 +63,12 @@ * @since 1.0 */ @CFComment({"lock/nullness: permit null elements"}) -@AnnotatedFor({"lock", "nullness"}) +@AnnotatedFor({"lock", "nullness", "modifiability"}) public class Stack extends Vector { /** * Creates an empty Stack. */ - public Stack() { + public @Modifiable @IteratorPolyMod Stack() { } /** @@ -79,7 +83,7 @@ public Stack() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E push(@GuardSatisfied Stack this, E item) { + public E push(@Growable @GuardSatisfied Stack this, E item) { addElement(item); return item; @@ -95,7 +99,7 @@ public E push(@GuardSatisfied Stack this, E item) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized E pop(@GuardSatisfied @NonEmpty @CanShrink Stack this) { + public synchronized E pop(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink Stack this) { E obj; int len = size(); @@ -148,6 +152,7 @@ public boolean empty() { * the object is located; the return value {@code -1} * indicates that the object is not on the stack. */ + @Pure public synchronized int search(Object o) { int i = lastIndexOf(o); diff --git a/src/java.base/share/classes/java/util/TreeMap.java b/src/java.base/share/classes/java/util/TreeMap.java index b822f4cb1af9..3f3cb0bf46ff 100644 --- a/src/java.base/share/classes/java/util/TreeMap.java +++ b/src/java.base/share/classes/java/util/TreeMap.java @@ -27,6 +27,15 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.SeqUngrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; @@ -140,8 +149,8 @@ */ @CFComment({"lock/nullness: This permits null element when using a custom comparator that allows null"}) -@AnnotatedFor({"lock", "nullness", "index"}) -public class TreeMap +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) +public @SeqUngrowable class TreeMap extends AbstractMap implements NavigableMap, Cloneable, java.io.Serializable { @@ -178,7 +187,7 @@ public class TreeMap * {@code put(Object key, Object value)} call will throw a * {@code ClassCastException}. */ - public TreeMap() { + public @Growable @Shrinkable @Replaceable @SeqUngrowable TreeMap() { comparator = null; } @@ -196,7 +205,7 @@ public TreeMap() { * If {@code null}, the {@linkplain Comparable natural * ordering} of the keys will be used. */ - public TreeMap(@Nullable Comparator comparator) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable TreeMap(@Nullable Comparator comparator) { this.comparator = comparator; } @@ -214,7 +223,7 @@ public TreeMap(@Nullable Comparator comparator) { * or are not mutually comparable * @throws NullPointerException if the specified map is null */ - public @PolyNonEmpty TreeMap(@PolyNonEmpty Map m) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @PolyNonEmpty TreeMap(@PolyNonEmpty Map m) { comparator = null; putAll(m); } @@ -228,7 +237,7 @@ public TreeMap(@Nullable Comparator comparator) { * and whose comparator is to be used to sort this map * @throws NullPointerException if the specified map is null */ - public @PolyNonEmpty TreeMap(@PolyNonEmpty SortedMap m) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @PolyNonEmpty TreeMap(@PolyNonEmpty SortedMap m) { comparator = m.comparator(); try { buildFromSorted(m.size(), m.entrySet().iterator(), null, null); @@ -311,7 +320,6 @@ public boolean containsValue(@GuardSatisfied TreeMap this, @GuardSatisfied * and this map uses natural ordering, or its comparator * does not permit null keys */ - @DoesNotUnrefineReceiver("modifiability") @Pure public @Nullable V get(@GuardSatisfied TreeMap this, @UnknownSignedness @GuardSatisfied Object key) { Entry p = getEntry(key); @@ -326,6 +334,7 @@ public boolean containsValue(@GuardSatisfied TreeMap this, @GuardSatisfied /** * @throws NoSuchElementException {@inheritDoc} */ + @Pure public @KeyFor("this") K firstKey(@NonEmpty TreeMap this) { return key(getFirstEntry()); } @@ -333,6 +342,7 @@ public boolean containsValue(@GuardSatisfied TreeMap this, @GuardSatisfied /** * @throws NoSuchElementException {@inheritDoc} */ + @Pure public @KeyFor("this") K lastKey(@NonEmpty TreeMap this) { return key(getLastEntry()); } @@ -347,7 +357,7 @@ public boolean containsValue(@GuardSatisfied TreeMap this, @GuardSatisfied */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putFirst(K k, V v) { + public @Nullable V putFirst(@SeqGrowable TreeMap this, K k, V v) { throw new UnsupportedOperationException(); } @@ -361,7 +371,7 @@ public boolean containsValue(@GuardSatisfied TreeMap this, @GuardSatisfied */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putLast(K k, V v) { + public @Nullable V putLast(@SeqGrowable TreeMap this, K k, V v) { throw new UnsupportedOperationException(); } @@ -379,7 +389,7 @@ public boolean containsValue(@GuardSatisfied TreeMap this, @GuardSatisfied */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putAll(@GuardSatisfied TreeMap this, Map map) { + public void putAll(@Growable @Replaceable @GuardSatisfied TreeMap this, Map map) { int mapSize = map.size(); if (size==0 && mapSize!=0 && map instanceof SortedMap) { if (Objects.equals(comparator, ((SortedMap)map).comparator())) { @@ -602,14 +612,14 @@ else if (cmp > 0) @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(@GuardSatisfied TreeMap this, K key, V value) { + public @Nullable V put(@Growable @Replaceable @GuardSatisfied TreeMap this, K key, V value) { return put(key, value, true); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(@Growable TreeMap this, K key, V value) { return put(key, value, false); } @@ -625,7 +635,7 @@ else if (cmp > 0) */ @Override @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V computeIfAbsent(K key, Function mappingFunction) { + public @PolyNull V computeIfAbsent(@Growable TreeMap this, K key, Function mappingFunction) { Objects.requireNonNull(mappingFunction); V newValue; Entry t = root; @@ -696,7 +706,7 @@ else if (cmp > 0) */ @Override @DoesNotUnrefineReceiver("modifiability") - public @Nullable V computeIfPresent(K key, BiFunction remappingFunction) { + public @Nullable V computeIfPresent(@Shrinkable @Replaceable TreeMap this, K key, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); Entry oldEntry = getEntry(key); if (oldEntry != null && oldEntry.value != null) { @@ -718,7 +728,7 @@ else if (cmp > 0) */ @Override @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V compute(K key, BiFunction remappingFunction) { + public @PolyNull V compute(@Growable @Shrinkable @Replaceable TreeMap this, K key, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); V newValue; Entry t = root; @@ -781,7 +791,7 @@ else if (cmp > 0) */ @Override @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V merge(K key, @NonNull V value, BiFunction remappingFunction) { + public @PolyNull V merge(@Growable @Shrinkable @Replaceable TreeMap this, K key, @NonNull V value, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); Entry t = root; @@ -957,7 +967,7 @@ else if (cmp > 0) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V remove(@GuardSatisfied TreeMap this, @GuardSatisfied @UnknownSignedness Object key) { + public @Nullable V remove(@Shrinkable @GuardSatisfied TreeMap this, @GuardSatisfied @UnknownSignedness Object key) { Entry p = getEntry(key); if (p == null) return null; @@ -973,7 +983,7 @@ else if (cmp > 0) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied TreeMap this) { + public void clear(@Shrinkable @GuardSatisfied TreeMap this) { modCount++; size = 0; root = null; @@ -985,9 +995,8 @@ public void clear(@GuardSatisfied TreeMap this) { * * @return a shallow copy of this map */ - @DoesNotUnrefineReceiver("modifiability") @SideEffectFree - public Object clone(@GuardSatisfied TreeMap this) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable Object clone(@GuardSatisfied TreeMap this) { TreeMap clone; try { clone = (TreeMap) super.clone(); @@ -1017,6 +1026,7 @@ public Object clone(@GuardSatisfied TreeMap this) { /** * @since 1.6 */ + @SideEffectFree public Map.@Nullable Entry firstEntry() { return exportEntry(getFirstEntry()); } @@ -1024,6 +1034,7 @@ public Object clone(@GuardSatisfied TreeMap this) { /** * @since 1.6 */ + @SideEffectFree public Map.@Nullable Entry lastEntry() { return exportEntry(getLastEntry()); } @@ -1033,7 +1044,7 @@ public Object clone(@GuardSatisfied TreeMap this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public Map.@Nullable Entry pollFirstEntry(@GuardSatisfied TreeMap this) { + public Map.@Nullable Entry pollFirstEntry(@Shrinkable @GuardSatisfied TreeMap this) { Entry p = getFirstEntry(); Map.Entry result = exportEntry(p); if (p != null) @@ -1046,7 +1057,7 @@ public Object clone(@GuardSatisfied TreeMap this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public Map.@Nullable Entry pollLastEntry(@GuardSatisfied TreeMap this) { + public Map.@Nullable Entry pollLastEntry(@Shrinkable @GuardSatisfied TreeMap this) { Entry p = getLastEntry(); Map.Entry result = exportEntry(p); if (p != null) @@ -1061,6 +1072,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * does not permit null keys * @since 1.6 */ + @SideEffectFree public Map.@Nullable Entry lowerEntry(K key) { return exportEntry(getLowerEntry(key)); } @@ -1072,6 +1084,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * does not permit null keys * @since 1.6 */ + @Pure public @Nullable K lowerKey(K key) { return keyOrNull(getLowerEntry(key)); } @@ -1083,6 +1096,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * does not permit null keys * @since 1.6 */ + @SideEffectFree public Map.@Nullable Entry floorEntry(K key) { return exportEntry(getFloorEntry(key)); } @@ -1094,6 +1108,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * does not permit null keys * @since 1.6 */ + @Pure public @Nullable K floorKey(K key) { return keyOrNull(getFloorEntry(key)); } @@ -1105,6 +1120,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * does not permit null keys * @since 1.6 */ + @SideEffectFree public Map.@Nullable Entry ceilingEntry(K key) { return exportEntry(getCeilingEntry(key)); } @@ -1116,6 +1132,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * does not permit null keys * @since 1.6 */ + @Pure public @Nullable K ceilingKey(K key) { return keyOrNull(getCeilingEntry(key)); } @@ -1127,6 +1144,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * does not permit null keys * @since 1.6 */ + @SideEffectFree public Map.@Nullable Entry higherEntry(K key) { return exportEntry(getHigherEntry(key)); } @@ -1138,6 +1156,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * does not permit null keys * @since 1.6 */ + @Pure public @Nullable K higherKey(K key) { return keyOrNull(getHigherEntry(key)); } @@ -1178,9 +1197,8 @@ public Object clone(@GuardSatisfied TreeMap this) { * operations. It does not support the {@code add} or {@code addAll} * operations. */ - @DoesNotUnrefineReceiver("modifiability") @SideEffectFree - public Set<@KeyFor({"this"}) K> keySet(@GuardSatisfied TreeMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@KeyFor({"this"}) K> keySet(@PolyShrinkable @GuardSatisfied TreeMap this) { return navigableKeySet(); } @@ -1188,7 +1206,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * @since 1.6 */ @SideEffectFree - public NavigableSet<@KeyFor({"this"}) K> navigableKeySet(@GuardSatisfied TreeMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet<@KeyFor({"this"}) K> navigableKeySet(@PolyShrinkable @GuardSatisfied TreeMap this) { KeySet nks = navigableKeySet; return (nks != null) ? nks : (navigableKeySet = new KeySet<>(this)); } @@ -1197,7 +1215,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * @since 1.6 */ @SideEffectFree - public NavigableSet<@KeyFor({"this"}) K> descendingKeySet(@GuardSatisfied TreeMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet<@KeyFor({"this"}) K> descendingKeySet(@PolyShrinkable @GuardSatisfied TreeMap this) { return descendingMap().navigableKeySet(); } @@ -1223,7 +1241,7 @@ public Object clone(@GuardSatisfied TreeMap this) { * support the {@code add} or {@code addAll} operations. */ @SideEffectFree - public Collection values(@GuardSatisfied TreeMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable @GuardSatisfied TreeMap this) { Collection vs = values; if (vs == null) { vs = new Values(); @@ -1255,7 +1273,7 @@ public Collection values(@GuardSatisfied TreeMap this) { * {@code add} or {@code addAll} operations. */ @SideEffectFree - public Set> entrySet(@GuardSatisfied TreeMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable @GuardSatisfied TreeMap this) { EntrySet es = entrySet; return (es != null) ? es : (entrySet = new EntrySet()); } @@ -1264,7 +1282,7 @@ public Collection values(@GuardSatisfied TreeMap this) { * @since 1.6 */ @SideEffectFree - public NavigableMap descendingMap(@GuardSatisfied TreeMap this) { + public @PolyModifiable NavigableMap descendingMap(@PolyModifiable @GuardSatisfied TreeMap this) { NavigableMap km = descendingMap; return (km != null) ? km : (descendingMap = new DescendingSubMap<>(this, @@ -1281,7 +1299,7 @@ public NavigableMap descendingMap(@GuardSatisfied TreeMap this) { * @since 1.6 */ @SideEffectFree - public NavigableMap subMap(@GuardSatisfied TreeMap this, @GuardSatisfied K fromKey, boolean fromInclusive, + public @PolyModifiable NavigableMap subMap(@PolyModifiable @GuardSatisfied TreeMap this, @GuardSatisfied K fromKey, boolean fromInclusive, @GuardSatisfied K toKey, boolean toInclusive) { return new AscendingSubMap<>(this, false, fromKey, fromInclusive, @@ -1297,7 +1315,7 @@ public NavigableMap subMap(@GuardSatisfied TreeMap this, @GuardSatisf * @since 1.6 */ @SideEffectFree - public NavigableMap headMap(@GuardSatisfied TreeMap this, @GuardSatisfied K toKey, boolean inclusive) { + public @PolyModifiable NavigableMap headMap(@PolyModifiable @GuardSatisfied TreeMap this, @GuardSatisfied K toKey, boolean inclusive) { return new AscendingSubMap<>(this, true, null, true, false, toKey, inclusive); @@ -1312,7 +1330,7 @@ public NavigableMap headMap(@GuardSatisfied TreeMap this, @GuardSatis * @since 1.6 */ @SideEffectFree - public NavigableMap tailMap(@GuardSatisfied TreeMap this, @GuardSatisfied K fromKey, boolean inclusive) { + public @PolyModifiable NavigableMap tailMap(@PolyModifiable @GuardSatisfied TreeMap this, @GuardSatisfied K fromKey, boolean inclusive) { return new AscendingSubMap<>(this, false, fromKey, inclusive, true, null, true); @@ -1326,7 +1344,7 @@ public NavigableMap tailMap(@GuardSatisfied TreeMap this, @GuardSatis * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public SortedMap subMap(@GuardSatisfied TreeMap this, @GuardSatisfied K fromKey, @GuardSatisfied K toKey) { + public @PolyModifiable SortedMap subMap(@PolyModifiable @GuardSatisfied TreeMap this, @GuardSatisfied K fromKey, @GuardSatisfied K toKey) { return subMap(fromKey, true, toKey, false); } @@ -1338,7 +1356,7 @@ public SortedMap subMap(@GuardSatisfied TreeMap this, @GuardSatisfied * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public SortedMap headMap(@GuardSatisfied TreeMap this, K toKey) { + public @PolyModifiable SortedMap headMap(@PolyModifiable @GuardSatisfied TreeMap this, K toKey) { return headMap(toKey, false); } @@ -1350,14 +1368,14 @@ public SortedMap headMap(@GuardSatisfied TreeMap this, K toKey) { * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public SortedMap tailMap(@GuardSatisfied TreeMap this, K fromKey) { + public @PolyModifiable SortedMap tailMap(@PolyModifiable @GuardSatisfied TreeMap this, K fromKey) { return tailMap(fromKey, true); } @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean replace(K key, V oldValue, V newValue) { + public boolean replace(@Replaceable TreeMap this, K key, V oldValue, V newValue) { Entry p = getEntry(key); if (p!=null && Objects.equals(oldValue, p.value)) { p.value = newValue; @@ -1369,7 +1387,7 @@ public boolean replace(K key, V oldValue, V newValue) { @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V replace(K key, V value) { + public @Nullable V replace(@Replaceable TreeMap this, K key, V value) { Entry p = getEntry(key); if (p!=null) { V oldValue = p.value; @@ -1394,7 +1412,7 @@ public void forEach(BiConsumer action) { @Override @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(BiFunction function) { + public void replaceAll(@Replaceable TreeMap this, BiFunction function) { Objects.requireNonNull(function); int expectedModCount = modCount; @@ -1544,13 +1562,17 @@ public Iterator descendingIterator() { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") public void clear() { m.clear(); } + @Pure public @Nullable E lower(E e) { return m.lowerKey(e); } + @Pure public @Nullable E floor(E e) { return m.floorKey(e); } + @Pure public @Nullable E ceiling(E e) { return m.ceilingKey(e); } + @Pure public @Nullable E higher(E e) { return m.higherKey(e); } - @SideEffectFree + @Pure public E first() { return m.firstKey(); } - @SideEffectFree + @Pure public E last() { return m.lastKey(); } @Pure public @Nullable Comparator comparator() { return m.comparator(); } @@ -1959,7 +1981,7 @@ public final boolean containsKey(@UnknownSignedness Object key) { @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public final @Nullable V put(K key, V value) { + public final @Nullable V put(@Growable @Replaceable NavigableSubMap this, K key, V value) { if (!inRange(key)) throw new IllegalArgumentException("key out of range"); return m.put(key, value); @@ -1967,21 +1989,21 @@ public final boolean containsKey(@UnknownSignedness Object key) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(@Growable NavigableSubMap this, K key, V value) { if (!inRange(key)) throw new IllegalArgumentException("key out of range"); return m.putIfAbsent(key, value); } @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V merge(K key, @NonNull V value, BiFunction remappingFunction) { + public @PolyNull V merge(@Growable @Shrinkable @Replaceable NavigableSubMap this, K key, @NonNull V value, BiFunction remappingFunction) { if (!inRange(key)) throw new IllegalArgumentException("key out of range"); return m.merge(key, value, remappingFunction); } @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V computeIfAbsent(K key, Function mappingFunction) { + public @PolyNull V computeIfAbsent(@Growable NavigableSubMap this, K key, Function mappingFunction) { if (!inRange(key)) { // Do not throw if mapping function returns null // to preserve compatibility with default computeIfAbsent implementation @@ -1992,7 +2014,7 @@ public final boolean containsKey(@UnknownSignedness Object key) { } @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V compute(K key, BiFunction remappingFunction) { + public @PolyNull V compute(@Growable @Shrinkable @Replaceable NavigableSubMap this, K key, BiFunction remappingFunction) { if (!inRange(key)) { // Do not throw if remapping function returns null // to preserve compatibility with default computeIfAbsent implementation @@ -2003,7 +2025,7 @@ public final boolean containsKey(@UnknownSignedness Object key) { } @DoesNotUnrefineReceiver("modifiability") - public @Nullable V computeIfPresent(K key, BiFunction remappingFunction) { + public @Nullable V computeIfPresent(@Shrinkable @Replaceable NavigableSubMap this, K key, BiFunction remappingFunction) { return !inRange(key) ? null : m.computeIfPresent(key, remappingFunction); } @@ -2014,7 +2036,7 @@ public final boolean containsKey(@UnknownSignedness Object key) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public final @Nullable V remove(Object key) { + public final @Nullable V remove(@Shrinkable NavigableSubMap this, Object key) { return !inRange(key) ? null : m.remove(key); } @@ -2022,6 +2044,7 @@ public final boolean containsKey(@UnknownSignedness Object key) { return exportEntry(subCeiling(key)); } + @Pure public final @Nullable K ceilingKey(K key) { return keyOrNull(subCeiling(key)); } @@ -2030,6 +2053,7 @@ public final boolean containsKey(@UnknownSignedness Object key) { return exportEntry(subHigher(key)); } + @Pure public final @Nullable K higherKey(K key) { return keyOrNull(subHigher(key)); } @@ -2038,6 +2062,7 @@ public final boolean containsKey(@UnknownSignedness Object key) { return exportEntry(subFloor(key)); } + @Pure public final @Nullable K floorKey(K key) { return keyOrNull(subFloor(key)); } @@ -2046,14 +2071,17 @@ public final boolean containsKey(@UnknownSignedness Object key) { return exportEntry(subLower(key)); } + @Pure public final @Nullable K lowerKey(K key) { return keyOrNull(subLower(key)); } + @Pure public final K firstKey() { return key(subLowest()); } + @Pure public final K lastKey() { return key(subHighest()); } @@ -2066,7 +2094,7 @@ public final K lastKey() { return exportEntry(subHighest()); } - public final Map.@Nullable Entry pollFirstEntry() { + public final Map.@Nullable Entry pollFirstEntry(@Shrinkable NavigableSubMap this) { TreeMap.Entry e = subLowest(); Map.Entry result = exportEntry(e); if (e != null) @@ -2074,7 +2102,7 @@ public final K lastKey() { return result; } - public final Map.@Nullable Entry pollLastEntry() { + public final Map.@Nullable Entry pollLastEntry(@Shrinkable NavigableSubMap this) { TreeMap.Entry e = subHighest(); Map.Entry result = exportEntry(e); if (e != null) @@ -2088,19 +2116,19 @@ public final K lastKey() { transient KeySet navigableKeySetView; @SideEffectFree - public final NavigableSet navigableKeySet() { + public final @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet navigableKeySet(@PolyShrinkable NavigableSubMap this) { KeySet nksv = navigableKeySetView; return (nksv != null) ? nksv : (navigableKeySetView = new TreeMap.KeySet<>(this)); } @SideEffectFree - public final Set keySet() { + public final @IteratorPolyMod @PolyShrinkable @Ungrowable Set keySet(@PolyShrinkable NavigableSubMap this) { return navigableKeySet(); } @SideEffectFree - public NavigableSet descendingKeySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet descendingKeySet(@PolyShrinkable NavigableSubMap this) { return descendingMap().navigableKeySet(); } @@ -2386,7 +2414,7 @@ static final class AscendingSubMap extends NavigableSubMap { } @SideEffectFree - public NavigableMap subMap(K fromKey, boolean fromInclusive, + public @PolyModifiable NavigableMap subMap(@PolyModifiable AscendingSubMap this, K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { if (!inRange(fromKey, fromInclusive)) throw new IllegalArgumentException("fromKey out of range"); @@ -2398,7 +2426,7 @@ public NavigableMap subMap(K fromKey, boolean fromInclusive, } @SideEffectFree - public NavigableMap headMap(K toKey, boolean inclusive) { + public @PolyModifiable NavigableMap headMap(@PolyModifiable AscendingSubMap this, K toKey, boolean inclusive) { if (!inRange(toKey, inclusive)) throw new IllegalArgumentException("toKey out of range"); return new AscendingSubMap<>(m, @@ -2407,7 +2435,7 @@ public NavigableMap headMap(K toKey, boolean inclusive) { } @SideEffectFree - public NavigableMap tailMap(K fromKey, boolean inclusive) { + public @PolyModifiable NavigableMap tailMap(@PolyModifiable AscendingSubMap this, K fromKey, boolean inclusive) { if (!inRange(fromKey, inclusive)) throw new IllegalArgumentException("fromKey out of range"); return new AscendingSubMap<>(m, @@ -2416,7 +2444,7 @@ public NavigableMap tailMap(K fromKey, boolean inclusive) { } @SideEffectFree - public NavigableMap descendingMap() { + public @PolyModifiable NavigableMap descendingMap(@PolyModifiable AscendingSubMap this) { NavigableMap mv = descendingMapView; return (mv != null) ? mv : (descendingMapView = @@ -2445,7 +2473,7 @@ public Iterator> iterator() { } @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable AscendingSubMap this) { EntrySetView es = entrySetView; return (es != null) ? es : (entrySetView = new AscendingEntrySetView()); } @@ -2480,7 +2508,7 @@ static final class DescendingSubMap extends NavigableSubMap { } @SideEffectFree - public NavigableMap subMap(K fromKey, boolean fromInclusive, + public @PolyModifiable NavigableMap subMap(@PolyModifiable DescendingSubMap this, K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { if (!inRange(fromKey, fromInclusive)) throw new IllegalArgumentException("fromKey out of range"); @@ -2492,7 +2520,7 @@ public NavigableMap subMap(K fromKey, boolean fromInclusive, } @SideEffectFree - public NavigableMap headMap(K toKey, boolean inclusive) { + public @PolyModifiable NavigableMap headMap(@PolyModifiable DescendingSubMap this, K toKey, boolean inclusive) { if (!inRange(toKey, inclusive)) throw new IllegalArgumentException("toKey out of range"); return new DescendingSubMap<>(m, @@ -2501,7 +2529,7 @@ public NavigableMap headMap(K toKey, boolean inclusive) { } @SideEffectFree - public NavigableMap tailMap(K fromKey, boolean inclusive) { + public @PolyModifiable NavigableMap tailMap(@PolyModifiable DescendingSubMap this, K fromKey, boolean inclusive) { if (!inRange(fromKey, inclusive)) throw new IllegalArgumentException("fromKey out of range"); return new DescendingSubMap<>(m, @@ -2510,7 +2538,7 @@ public NavigableMap tailMap(K fromKey, boolean inclusive) { } @SideEffectFree - public NavigableMap descendingMap() { + public @PolyModifiable NavigableMap descendingMap(@PolyModifiable DescendingSubMap this) { NavigableMap mv = descendingMapView; return (mv != null) ? mv : (descendingMapView = @@ -2539,7 +2567,7 @@ public Iterator> iterator() { } @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable DescendingSubMap this) { EntrySetView es = entrySetView; return (es != null) ? es : (entrySetView = new DescendingEntrySetView()); } @@ -2578,7 +2606,9 @@ private Object readResolve() { } @SideEffectFree public Set> entrySet() { throw new InternalError(); } + @Pure public K lastKey() { throw new InternalError(); } + @Pure public K firstKey() { throw new InternalError(); } @SideEffectFree public SortedMap subMap(K fromKey, K toKey) { throw new InternalError(); } diff --git a/src/java.base/share/classes/java/util/TreeSet.java b/src/java.base/share/classes/java/util/TreeSet.java index 9bf5779ff16c..878746a18924 100644 --- a/src/java.base/share/classes/java/util/TreeSet.java +++ b/src/java.base/share/classes/java/util/TreeSet.java @@ -28,6 +28,13 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.SeqUngrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -111,14 +118,14 @@ */ @CFComment({"lock/nullness: Subclasses of this interface/class may opt to prohibit null elements"}) -@AnnotatedFor({"lock", "nullness"}) -public class TreeSet extends AbstractSet +@AnnotatedFor({"lock", "nullness", "modifiability"}) +public @SeqUngrowable class TreeSet extends AbstractSet implements NavigableSet, Cloneable, java.io.Serializable { /** * The backing map. */ - private transient NavigableMap m; + private transient @Growable @Shrinkable NavigableMap m; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); @@ -126,7 +133,7 @@ public class TreeSet extends AbstractSet /** * Constructs a set backed by the specified navigable map. */ - TreeSet(NavigableMap m) { + @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod TreeSet(NavigableMap m) { this.m = m; } @@ -143,7 +150,7 @@ public class TreeSet extends AbstractSet * integers), the {@code add} call will throw a * {@code ClassCastException}. */ - public TreeSet() { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod TreeSet() { this(new TreeMap<>()); } @@ -160,7 +167,7 @@ public TreeSet() { * If {@code null}, the {@linkplain Comparable natural * ordering} of the elements will be used. */ - public TreeSet(@Nullable Comparator comparator) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod TreeSet(@Nullable Comparator comparator) { this(new TreeMap<>(comparator)); } @@ -178,7 +185,7 @@ public TreeSet(@Nullable Comparator comparator) { * not {@link Comparable}, or are not mutually comparable * @throws NullPointerException if the specified collection is null */ - public @PolyNonEmpty TreeSet(@PolyNonEmpty Collection c) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod @PolyNonEmpty TreeSet(@PolyNonEmpty Collection c) { this(); addAll(c); } @@ -190,7 +197,7 @@ public TreeSet(@Nullable Comparator comparator) { * @param s sorted set whose elements will comprise the new set * @throws NullPointerException if the specified sorted set is null */ - public @PolyNonEmpty TreeSet(@PolyNonEmpty SortedSet s) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod @PolyNonEmpty TreeSet(@PolyNonEmpty SortedSet s) { this(s.comparator()); addAll(s); } @@ -201,7 +208,7 @@ public TreeSet(@Nullable Comparator comparator) { * @return an iterator over the elements in this set in ascending order */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty TreeSet this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty TreeSet this) { return m.navigableKeySet().iterator(); } @@ -212,7 +219,8 @@ public TreeSet(@Nullable Comparator comparator) { * @since 1.6 */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyNonEmpty TreeSet this) { + @DoesNotUnrefineReceiver("modifiability") + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty TreeSet this) { return m.descendingKeySet().iterator(); } @@ -221,7 +229,7 @@ public TreeSet(@Nullable Comparator comparator) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public NavigableSet descendingSet() { + public @PolyModifiable NavigableSet descendingSet(@PolyModifiable TreeSet this) { return new TreeSet<>(m.descendingMap()); } @@ -287,7 +295,7 @@ public boolean contains(@GuardSatisfied TreeSet this, @GuardSatisfied @Unknow @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(@GuardSatisfied TreeSet this, E e) { + public boolean add(@Growable @GuardSatisfied TreeSet this, E e) { return m.put(e, PRESENT)==null; } @@ -310,7 +318,7 @@ public boolean add(@GuardSatisfied TreeSet this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied TreeSet this, @GuardSatisfied @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @GuardSatisfied TreeSet this, @GuardSatisfied @UnknownSignedness Object o) { return m.remove(o)==PRESENT; } @@ -320,7 +328,7 @@ public boolean remove(@GuardSatisfied TreeSet this, @GuardSatisfied @UnknownS */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied TreeSet this) { + public void clear(@Shrinkable @GuardSatisfied TreeSet this) { m.clear(); } @@ -337,7 +345,7 @@ public void clear(@GuardSatisfied TreeSet this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(@GuardSatisfied TreeSet this, Collection c) { + public boolean addAll(@Growable @GuardSatisfied TreeSet this, Collection c) { // Use linear-time version if applicable if (m.size()==0 && c.size() > 0 && c instanceof SortedSet && @@ -360,7 +368,7 @@ public boolean addAll(@GuardSatisfied TreeSet this, Collection * @since 1.6 */ @SideEffectFree - public @PolyGrowShrink NavigableSet subSet(@GuardSatisfied @PolyGrowShrink TreeSet this, @GuardSatisfied E fromElement, boolean fromInclusive, + public @PolyGrowShrink @PolyModifiable NavigableSet subSet(@PolyModifiable @GuardSatisfied @PolyGrowShrink TreeSet this, @GuardSatisfied E fromElement, boolean fromInclusive, @GuardSatisfied E toElement, boolean toInclusive) { return new TreeSet<>(m.subMap(fromElement, fromInclusive, toElement, toInclusive)); @@ -375,7 +383,7 @@ public boolean addAll(@GuardSatisfied TreeSet this, Collection * @since 1.6 */ @SideEffectFree - public @PolyGrowShrink NavigableSet headSet(@GuardSatisfied @PolyGrowShrink TreeSet this, @GuardSatisfied E toElement, boolean inclusive) { + public @PolyGrowShrink @PolyModifiable NavigableSet headSet(@PolyModifiable @GuardSatisfied @PolyGrowShrink TreeSet this, @GuardSatisfied E toElement, boolean inclusive) { return new TreeSet<>(m.headMap(toElement, inclusive)); } @@ -388,7 +396,7 @@ public boolean addAll(@GuardSatisfied TreeSet this, Collection * @since 1.6 */ @SideEffectFree - public @PolyGrowShrink NavigableSet tailSet(@GuardSatisfied @PolyGrowShrink TreeSet this, @GuardSatisfied E fromElement, boolean inclusive) { + public @PolyGrowShrink @PolyModifiable NavigableSet tailSet(@PolyModifiable @GuardSatisfied @PolyGrowShrink TreeSet this, @GuardSatisfied E fromElement, boolean inclusive) { return new TreeSet<>(m.tailMap(fromElement, inclusive)); } @@ -400,7 +408,7 @@ public boolean addAll(@GuardSatisfied TreeSet this, Collection * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public @PolyGrowShrink SortedSet subSet(@GuardSatisfied @PolyGrowShrink TreeSet this, @GuardSatisfied E fromElement, @GuardSatisfied E toElement) { + public @PolyGrowShrink @PolyModifiable SortedSet subSet(@PolyModifiable @GuardSatisfied @PolyGrowShrink TreeSet this, @GuardSatisfied E fromElement, @GuardSatisfied E toElement) { return subSet(fromElement, true, toElement, false); } @@ -412,7 +420,7 @@ public boolean addAll(@GuardSatisfied TreeSet this, Collection * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public @PolyGrowShrink SortedSet headSet(@GuardSatisfied @PolyGrowShrink TreeSet this, E toElement) { + public @PolyGrowShrink @PolyModifiable SortedSet headSet(@PolyModifiable @GuardSatisfied @PolyGrowShrink TreeSet this, E toElement) { return headSet(toElement, false); } @@ -424,7 +432,7 @@ public boolean addAll(@GuardSatisfied TreeSet this, Collection * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public @PolyGrowShrink SortedSet tailSet(@GuardSatisfied @PolyGrowShrink TreeSet this, E fromElement) { + public @PolyGrowShrink @PolyModifiable SortedSet tailSet(@PolyModifiable @GuardSatisfied @PolyGrowShrink TreeSet this, E fromElement) { return tailSet(fromElement, true); } @@ -436,7 +444,7 @@ public boolean addAll(@GuardSatisfied TreeSet this, Collection /** * @throws NoSuchElementException {@inheritDoc} */ - @SideEffectFree + @Pure public E first(@GuardSatisfied @NonEmpty TreeSet this) { return m.firstKey(); } @@ -444,7 +452,7 @@ public E first(@GuardSatisfied @NonEmpty TreeSet this) { /** * @throws NoSuchElementException {@inheritDoc} */ - @SideEffectFree + @Pure public E last(@GuardSatisfied @NonEmpty TreeSet this) { return m.lastKey(); } @@ -504,7 +512,7 @@ public E last(@GuardSatisfied @NonEmpty TreeSet this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollFirst(@GuardSatisfied TreeSet this) { + public @Nullable E pollFirst(@Shrinkable @GuardSatisfied TreeSet this) { Map.Entry e = m.pollFirstEntry(); return (e == null) ? null : e.getKey(); } @@ -514,7 +522,7 @@ public E last(@GuardSatisfied @NonEmpty TreeSet this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollLast(@GuardSatisfied TreeSet this) { + public @Nullable E pollLast(@Shrinkable @GuardSatisfied TreeSet this) { Map.Entry e = m.pollLastEntry(); return (e == null) ? null : e.getKey(); } @@ -530,7 +538,7 @@ public E last(@GuardSatisfied @NonEmpty TreeSet this) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E e) { + public void addFirst(@SeqGrowable TreeSet this, E e) { throw new UnsupportedOperationException(); } @@ -545,7 +553,7 @@ public void addFirst(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E e) { + public void addLast(@SeqGrowable TreeSet this, E e) { throw new UnsupportedOperationException(); } @@ -557,7 +565,7 @@ public void addLast(E e) { */ @SideEffectFree @SuppressWarnings("unchecked") - public Object clone(@GuardSatisfied TreeSet this) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod Object clone(@GuardSatisfied TreeSet this) { TreeSet clone; try { clone = (TreeSet) super.clone(); @@ -642,6 +650,7 @@ private void readObject(java.io.ObjectInputStream s) * @since 1.8 */ @SideEffectFree + @DoesNotUnrefineReceiver("modifiability") public Spliterator spliterator() { return TreeMap.keySpliteratorFor(m); } diff --git a/src/java.base/share/classes/java/util/Vector.java b/src/java.base/share/classes/java/util/Vector.java index 61435671375b..8a2bbb832f2f 100644 --- a/src/java.base/share/classes/java/util/Vector.java +++ b/src/java.base/share/classes/java/util/Vector.java @@ -30,6 +30,12 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -38,6 +44,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; import org.checkerframework.checker.signedness.qual.PolySigned; import org.checkerframework.checker.signedness.qual.UnknownSignedness; +import org.checkerframework.common.value.qual.StaticallyExecutable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; @@ -110,7 +117,7 @@ * @since 1.0 */ @CFComment({"lock/nullness: permits nullable object"}) -@AnnotatedFor({"lock", "nullness", "index"}) +@AnnotatedFor({"lock", "nullness", "index", "modifiability"}) public class Vector extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable @@ -160,7 +167,7 @@ public class Vector * @throws IllegalArgumentException if the specified initial capacity * is negative */ - public Vector(@NonNegative int initialCapacity, int capacityIncrement) { + public @Modifiable @IteratorPolyMod Vector(@NonNegative int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ @@ -177,7 +184,7 @@ public Vector(@NonNegative int initialCapacity, int capacityIncrement) { * @throws IllegalArgumentException if the specified initial capacity * is negative */ - public Vector(@NonNegative int initialCapacity) { + public @Modifiable @IteratorPolyMod Vector(@NonNegative int initialCapacity) { this(initialCapacity, 0); } @@ -186,7 +193,7 @@ public Vector(@NonNegative int initialCapacity) { * has size {@code 10} and its standard capacity increment is * zero. */ - public Vector() { + public @Modifiable @IteratorPolyMod Vector() { this(10); } @@ -200,7 +207,7 @@ public Vector() { * @throws NullPointerException if the specified collection is null * @since 1.2 */ - public @PolyNonEmpty Vector(@PolyNonEmpty Collection c) { + public @Modifiable @IteratorPolyMod @PolyNonEmpty Vector(@PolyNonEmpty Collection c) { Object[] a = c.toArray(); elementCount = a.length; if (c.getClass() == ArrayList.class) { @@ -401,6 +408,7 @@ public boolean contains(@GuardSatisfied Vector this, @GuardSatisfied @Nullabl * this vector, or -1 if this vector does not contain the element */ @Pure + @StaticallyExecutable public @GTENegativeOne int indexOf(@GuardSatisfied Vector this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return indexOf(o, 0); } @@ -422,6 +430,7 @@ public boolean contains(@GuardSatisfied Vector this, @GuardSatisfied @Nullabl * @see Object#equals(Object) */ @Pure + @StaticallyExecutable public synchronized @GTENegativeOne int indexOf(@GuardSatisfied Vector this, @GuardSatisfied @Nullable @UnknownSignedness Object o, @NonNegative int index) { if (o == null) { for (int i = index ; i < elementCount ; i++) @@ -447,6 +456,7 @@ public boolean contains(@GuardSatisfied Vector this, @GuardSatisfied @Nullabl * this vector, or -1 if this vector does not contain the element */ @Pure + @StaticallyExecutable public synchronized @GTENegativeOne int lastIndexOf(@GuardSatisfied Vector this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return lastIndexOf(o, elementCount-1); } @@ -468,6 +478,7 @@ public boolean contains(@GuardSatisfied Vector this, @GuardSatisfied @Nullabl * than or equal to the current size of this vector */ @Pure + @StaticallyExecutable public synchronized @GTENegativeOne int lastIndexOf(@GuardSatisfied Vector this, @GuardSatisfied @Nullable @UnknownSignedness Object o, @NonNegative int index) { if (index >= elementCount) throw new IndexOutOfBoundsException(index + " >= "+ elementCount); @@ -554,7 +565,7 @@ public synchronized E lastElement(@NonEmpty Vector this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized void setElementAt(@GuardSatisfied Vector this, E obj, @NonNegative int index) { + public synchronized void setElementAt(@Replaceable @GuardSatisfied Vector this, E obj, @NonNegative int index) { if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); @@ -583,7 +594,7 @@ public synchronized void setElementAt(@GuardSatisfied Vector this, E obj, @No */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized void removeElementAt(@GuardSatisfied @CanShrink Vector this, @NonNegative int index) { + public synchronized void removeElementAt(@Shrinkable @GuardSatisfied @CanShrink Vector this, @NonNegative int index) { if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); @@ -625,7 +636,7 @@ else if (index < 0) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized void insertElementAt(@GuardSatisfied Vector this, E obj, @NonNegative int index) { + public synchronized void insertElementAt(@Growable @GuardSatisfied Vector this, E obj, @NonNegative int index) { if (index > elementCount) { throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount); @@ -655,7 +666,7 @@ public synchronized void insertElementAt(@GuardSatisfied Vector this, E obj, */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized void addElement(@GuardSatisfied Vector this, E obj) { + public synchronized void addElement(@Growable @GuardSatisfied Vector this, E obj) { modCount++; add(obj, elementData, elementCount); } @@ -677,7 +688,7 @@ public synchronized void addElement(@GuardSatisfied Vector this, E obj) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized boolean removeElement(@GuardSatisfied @CanShrink Vector this, Object obj) { + public synchronized boolean removeElement(@Shrinkable @GuardSatisfied @CanShrink Vector this, Object obj) { modCount++; int i = indexOf(obj); if (i >= 0) { @@ -695,7 +706,7 @@ public synchronized boolean removeElement(@GuardSatisfied @CanShrink Vector t */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized void removeAllElements(@GuardSatisfied @CanShrink Vector this) { + public synchronized void removeAllElements(@Shrinkable @GuardSatisfied @CanShrink Vector this) { final Object[] es = elementData; for (int to = elementCount, i = elementCount = 0; i < to; i++) es[i] = null; @@ -710,7 +721,7 @@ public synchronized void removeAllElements(@GuardSatisfied @CanShrink Vector * @return a clone of this vector */ @SideEffectFree - public synchronized Object clone(@GuardSatisfied Vector this) { + public synchronized @Modifiable @IteratorPolyMod Object clone(@GuardSatisfied Vector this) { try { @SuppressWarnings("unchecked") Vector v = (Vector) super.clone(); @@ -817,7 +828,7 @@ public synchronized E get(@GuardSatisfied Vector this, @NonNegative int index @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized E set(@GuardSatisfied Vector this, @NonNegative int index, E element) { + public synchronized E set(@Replaceable @GuardSatisfied Vector this, @NonNegative int index, E element) { if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); @@ -848,7 +859,7 @@ private void add(E e, Object[] elementData, int s) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized boolean add(@GuardSatisfied Vector this, E e) { + public synchronized boolean add(@Growable @GuardSatisfied Vector this, E e) { modCount++; add(e, elementData, elementCount); return true; @@ -867,7 +878,7 @@ public synchronized boolean add(@GuardSatisfied Vector this, E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @CanShrink Vector this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @GuardSatisfied @CanShrink Vector this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return removeElement(o); } @@ -885,7 +896,7 @@ public boolean remove(@GuardSatisfied @CanShrink Vector this, @GuardSatisfied @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void add(@GuardSatisfied Vector this, @NonNegative int index, E element) { + public void add(@Growable @GuardSatisfied Vector this, @NonNegative int index, E element) { insertElementAt(element, index); } @@ -902,7 +913,7 @@ public void add(@GuardSatisfied Vector this, @NonNegative int index, E elemen */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized E remove(@GuardSatisfied @CanShrink Vector this, @NonNegative int index) { + public synchronized E remove(@Shrinkable @GuardSatisfied @CanShrink Vector this, @NonNegative int index) { modCount++; if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); @@ -925,7 +936,7 @@ public synchronized E remove(@GuardSatisfied @CanShrink Vector this, @NonNega */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink Vector this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink Vector this) { removeAllElements(); } @@ -961,7 +972,7 @@ public synchronized boolean containsAll(@GuardSatisfied Vector this, @GuardSa */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(@GuardSatisfied Vector this, Collection c) { + public boolean addAll(@Growable @GuardSatisfied Vector this, Collection c) { Object[] a = c.toArray(); modCount++; int numNew = a.length; @@ -997,7 +1008,7 @@ public boolean addAll(@GuardSatisfied Vector this, Collection c) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeAll(@GuardSatisfied @CanShrink Vector this, Collection c) { + public boolean removeAll(@Shrinkable @GuardSatisfied @CanShrink Vector this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); } @@ -1023,7 +1034,7 @@ public boolean removeAll(@GuardSatisfied @CanShrink Vector this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink Vector this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } @@ -1035,7 +1046,7 @@ public boolean retainAll(@GuardSatisfied @CanShrink Vector this, Collection this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink Vector this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -1106,7 +1117,7 @@ private synchronized boolean bulkRemove(Predicate filter) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized boolean addAll(@GuardSatisfied Vector this, @NonNegative int index, Collection c) { + public synchronized boolean addAll(@Growable @GuardSatisfied Vector this, @NonNegative int index, Collection c) { if (index < 0 || index > elementCount) throw new ArrayIndexOutOfBoundsException(index); @@ -1199,7 +1210,7 @@ public synchronized String toString(@GuardSatisfied Vector this) { * {@code (fromIndex > toIndex)} */ @SideEffectFree - public synchronized @PolyGrowShrink List subList(@GuardSatisfied @PolyGrowShrink Vector this, int fromIndex, int toIndex) { + public synchronized @PolyGrowShrink @PolyModifiable List subList(@PolyModifiable @GuardSatisfied @PolyGrowShrink Vector this, int fromIndex, int toIndex) { return Collections.synchronizedList(super.subList(fromIndex, toIndex), this); } @@ -1213,7 +1224,7 @@ public synchronized String toString(@GuardSatisfied Vector this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - protected synchronized void removeRange(@GuardSatisfied @CanShrink Vector this, int fromIndex, int toIndex) { + protected synchronized void removeRange(@Shrinkable @GuardSatisfied @CanShrink Vector this, int fromIndex, int toIndex) { modCount++; shiftTailOverGap(elementData, fromIndex, toIndex); } @@ -1284,7 +1295,7 @@ private void writeObject(java.io.ObjectOutputStream s) * * @throws IndexOutOfBoundsException {@inheritDoc} */ - public synchronized @PolyGrowShrink ListIterator listIterator(@PolyGrowShrink Vector this, @NonNegative int index) { + public synchronized @PolyGrowShrink @PolyModifiable ListIterator listIterator(@PolyGrowShrink @PolyModifiable Vector this, @NonNegative int index) { if (index < 0 || index > elementCount) throw new IndexOutOfBoundsException("Index: "+index); return new ListItr(index); @@ -1298,7 +1309,7 @@ private void writeObject(java.io.ObjectOutputStream s) * * @see #listIterator(int) */ - public synchronized @PolyGrowShrink @PolyNonEmpty ListIterator listIterator(@PolyGrowShrink Vector this) { + public synchronized @PolyGrowShrink @PolyModifiable @PolyNonEmpty ListIterator listIterator(@PolyGrowShrink @PolyModifiable Vector this) { return new ListItr(0); } @@ -1310,7 +1321,7 @@ private void writeObject(java.io.ObjectOutputStream s) * @return an iterator over the elements in this list in proper sequence */ @SideEffectFree - public synchronized @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty Vector this) { + public synchronized @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty Vector this) { return new Itr(); } @@ -1466,7 +1477,7 @@ public synchronized void forEach(Consumer action) { @SuppressWarnings({"unchecked"}) @Override @DoesNotUnrefineReceiver("modifiability") - public synchronized void replaceAll(UnaryOperator operator) { + public synchronized void replaceAll(@Replaceable Vector this, UnaryOperator operator) { Objects.requireNonNull(operator); final int expectedModCount = modCount; final Object[] es = elementData; @@ -1483,7 +1494,7 @@ public synchronized void replaceAll(UnaryOperator operator) { @Override @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public synchronized void sort(Comparator c) { + public synchronized void sort(@Replaceable Vector this, Comparator c) { final int expectedModCount = modCount; Arrays.sort((E[]) elementData, 0, elementCount, c); if (modCount != expectedModCount) diff --git a/src/java.base/share/classes/java/util/WeakHashMap.java b/src/java.base/share/classes/java/util/WeakHashMap.java index 00874db0c643..2bac3a625818 100644 --- a/src/java.base/share/classes/java/util/WeakHashMap.java +++ b/src/java.base/share/classes/java/util/WeakHashMap.java @@ -27,6 +27,14 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; @@ -151,7 +159,7 @@ * @see java.lang.ref.WeakReference */ @CFComment({"lock: permits null keys and values"}) -@AnnotatedFor({"lock", "index", "nullness"}) +@AnnotatedFor({"lock", "index", "nullness", "modifiability"}) public class WeakHashMap extends AbstractMap implements Map { @@ -227,7 +235,7 @@ private Entry[] newTable(int n) { * @throws IllegalArgumentException if the initial capacity is negative, * or if the load factor is nonpositive. */ - public WeakHashMap(@NonNegative int initialCapacity, float loadFactor) { + public @Modifiable WeakHashMap(@NonNegative int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Initial Capacity: "+ initialCapacity); @@ -254,7 +262,7 @@ public WeakHashMap(@NonNegative int initialCapacity, float loadFactor) { * @param initialCapacity The initial capacity of the {@code WeakHashMap} * @throws IllegalArgumentException if the initial capacity is negative */ - public WeakHashMap(@NonNegative int initialCapacity) { + public @Modifiable WeakHashMap(@NonNegative int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } @@ -262,7 +270,7 @@ public WeakHashMap(@NonNegative int initialCapacity) { * Constructs a new, empty {@code WeakHashMap} with the default initial * capacity (16) and load factor (0.75). */ - public WeakHashMap() { + public @Modifiable WeakHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } @@ -276,7 +284,7 @@ public WeakHashMap() { * @throws NullPointerException if the specified map is null * @since 1.3 */ - public @PolyNonEmpty WeakHashMap(@PolyNonEmpty Map m) { + public @Modifiable @PolyNonEmpty WeakHashMap(@PolyNonEmpty Map m) { this(Math.max((int) Math.ceil(m.size() / (double)DEFAULT_LOAD_FACTOR), DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); @@ -485,7 +493,7 @@ Entry getEntry(Object key) { @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(@GuardSatisfied WeakHashMap this, K key, V value) { + public @Nullable V put(@Growable @Replaceable @GuardSatisfied WeakHashMap this, K key, V value) { Object k = maskNull(key); int h = hash(k); Entry[] tab = getTable(); @@ -579,7 +587,7 @@ private void transfer(Entry[] src, Entry[] dest) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putAll(@GuardSatisfied WeakHashMap this, Map m) { + public void putAll(@Growable @Replaceable @GuardSatisfied WeakHashMap this, Map m) { int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; @@ -630,7 +638,7 @@ public void putAll(@GuardSatisfied WeakHashMap this, Map this, @GuardSatisfied @Nullable @UnknownSignedness Object key) { + public @Nullable V remove(@Shrinkable @GuardSatisfied WeakHashMap this, @GuardSatisfied @Nullable @UnknownSignedness Object key) { Object k = maskNull(key); int h = hash(k); Entry[] tab = getTable(); @@ -691,7 +699,7 @@ boolean removeMapping(Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied WeakHashMap this) { + public void clear(@Shrinkable @GuardSatisfied WeakHashMap this) { // clear out ref queue. We don't need to expunge entries // since table is getting cleared. while (queue.poll() != null) @@ -927,7 +935,7 @@ public Map.Entry next(@NonEmpty EntryIterator this) { * operations. */ @SideEffectFree - public Set<@KeyFor({"this"}) K> keySet(@GuardSatisfied WeakHashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set<@KeyFor({"this"}) K> keySet(@PolyShrinkable @GuardSatisfied WeakHashMap this) { Set ks = keySet; if (ks == null) { ks = new KeySet(); @@ -990,7 +998,7 @@ public Spliterator spliterator() { * support the {@code add} or {@code addAll} operations. */ @SideEffectFree - public Collection values(@GuardSatisfied WeakHashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable @GuardSatisfied WeakHashMap this) { Collection vs = values; if (vs == null) { vs = new Values(); @@ -1043,7 +1051,7 @@ public Spliterator spliterator() { * {@code add} or {@code addAll} operations. */ @SideEffectFree - public Set> entrySet(@GuardSatisfied WeakHashMap this) { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyModifiable @GuardSatisfied WeakHashMap this) { Set> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } @@ -1127,7 +1135,7 @@ public void forEach(BiConsumer action) { @SuppressWarnings("unchecked") @Override @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(BiFunction function) { + public void replaceAll(@Replaceable WeakHashMap this, BiFunction function) { Objects.requireNonNull(function); int expectedModCount = modCount; diff --git a/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java b/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java index 6d6f515a3ac2..a66a441eb1e3 100644 --- a/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/ArrayBlockingQueue.java @@ -38,6 +38,11 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -101,7 +106,7 @@ * @author Doug Lea * @param the type of elements held in this queue */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public class ArrayBlockingQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { @@ -272,7 +277,7 @@ void removeAt(final int removeIndex) { * @param capacity the capacity of this queue * @throws IllegalArgumentException if {@code capacity < 1} */ - public ArrayBlockingQueue(int capacity) { + public @Modifiable @IteratorPolyMod ArrayBlockingQueue(int capacity) { this(capacity, false); } @@ -286,7 +291,7 @@ public ArrayBlockingQueue(int capacity) { * if {@code false} the access order is unspecified. * @throws IllegalArgumentException if {@code capacity < 1} */ - public ArrayBlockingQueue(int capacity, boolean fair) { + public @Modifiable @IteratorPolyMod ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = new Object[capacity]; @@ -311,7 +316,7 @@ public ArrayBlockingQueue(int capacity, boolean fair) { * @throws NullPointerException if the specified collection or any * of its elements are null */ - public ArrayBlockingQueue(int capacity, boolean fair, + public @Modifiable @IteratorPolyMod ArrayBlockingQueue(int capacity, boolean fair, Collection c) { this(capacity, fair); @@ -347,7 +352,7 @@ public ArrayBlockingQueue(int capacity, boolean fair, @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable ArrayBlockingQueue this, E e) { return super.add(e); } @@ -362,7 +367,7 @@ public boolean add(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable ArrayBlockingQueue this, E e) { Objects.requireNonNull(e); final ReentrantLock lock = this.lock; lock.lock(); @@ -387,7 +392,7 @@ public boolean offer(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void put(E e) throws InterruptedException { + public void put(@Growable ArrayBlockingQueue this, E e) throws InterruptedException { Objects.requireNonNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); @@ -410,8 +415,7 @@ public void put(E e) throws InterruptedException { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e, long timeout, TimeUnit unit) - throws InterruptedException { + public boolean offer(@Growable ArrayBlockingQueue this, E e, long timeout, TimeUnit unit) throws InterruptedException { Objects.requireNonNull(e); long nanos = unit.toNanos(timeout); @@ -432,7 +436,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink ArrayBlockingQueue this) { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink ArrayBlockingQueue this) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -444,7 +448,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E take(@GuardSatisfied @CanShrink ArrayBlockingQueue this) throws InterruptedException { + public E take(@Shrinkable @GuardSatisfied @CanShrink ArrayBlockingQueue this) throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { @@ -458,7 +462,7 @@ public E take(@GuardSatisfied @CanShrink ArrayBlockingQueue this) throws Inte @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink ArrayBlockingQueue this, long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink ArrayBlockingQueue this, long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); @@ -545,7 +549,7 @@ public int remainingCapacity() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink ArrayBlockingQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink ArrayBlockingQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { if (o == null) return false; final ReentrantLock lock = this.lock; lock.lock(); @@ -700,7 +704,7 @@ public String toString() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink ArrayBlockingQueue this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink ArrayBlockingQueue this) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -741,7 +745,7 @@ private static void circularClear(Object[] items, int i, int end) { */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink ArrayBlockingQueue this, Collection c) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink ArrayBlockingQueue this, @Growable Collection c) { return drainTo(c, Integer.MAX_VALUE); } @@ -753,7 +757,7 @@ public int drainTo(@GuardSatisfied @CanShrink ArrayBlockingQueue this, Collec */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink ArrayBlockingQueue this, Collection c, int maxElements) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink ArrayBlockingQueue this, @Growable Collection c, int maxElements) { Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); @@ -806,7 +810,7 @@ else if (i > take) * @return an iterator over the elements in this queue in proper sequence */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty ArrayBlockingQueue this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ArrayBlockingQueue this) { return new Itr(); } @@ -1526,7 +1530,7 @@ public void forEach(Consumer action) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink ArrayBlockingQueue this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink ArrayBlockingQueue this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -1536,7 +1540,7 @@ public boolean removeIf(@CanShrink ArrayBlockingQueue this, Predicate this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink ArrayBlockingQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); } @@ -1546,7 +1550,7 @@ public boolean removeAll(@CanShrink ArrayBlockingQueue this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink ArrayBlockingQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } diff --git a/src/java.base/share/classes/java/util/concurrent/BlockingDeque.java b/src/java.base/share/classes/java/util/concurrent/BlockingDeque.java index 947d5908a9da..dfcca9fd4a5b 100644 --- a/src/java.base/share/classes/java/util/concurrent/BlockingDeque.java +++ b/src/java.base/share/classes/java/util/concurrent/BlockingDeque.java @@ -38,6 +38,11 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -211,7 +216,7 @@ * @author Doug Lea * @param the type of elements held in this deque */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public interface BlockingDeque extends BlockingQueue, Deque { /* * We have "diamond" multiple interface inheritance here, and that @@ -236,7 +241,7 @@ public interface BlockingDeque extends BlockingQueue< @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void addFirst(E e); + void addFirst(@SeqGrowable BlockingDeque this, E e); /** * Inserts the specified element at the end of this deque if it is @@ -254,7 +259,7 @@ public interface BlockingDeque extends BlockingQueue< @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void addLast(E e); + void addLast(@SeqGrowable BlockingDeque this, E e); /** * Inserts the specified element at the front of this deque if it is @@ -272,7 +277,7 @@ public interface BlockingDeque extends BlockingQueue< */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offerFirst(E e); + boolean offerFirst(@SeqGrowable BlockingDeque this, E e); /** * Inserts the specified element at the end of this deque if it is @@ -290,7 +295,7 @@ public interface BlockingDeque extends BlockingQueue< */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offerLast(E e); + boolean offerLast(@SeqGrowable BlockingDeque this, E e); /** * Inserts the specified element at the front of this deque, @@ -306,7 +311,7 @@ public interface BlockingDeque extends BlockingQueue< */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void putFirst(E e) throws InterruptedException; + void putFirst(@SeqGrowable BlockingDeque this, E e) throws InterruptedException; /** * Inserts the specified element at the end of this deque, @@ -322,7 +327,7 @@ public interface BlockingDeque extends BlockingQueue< */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void putLast(E e) throws InterruptedException; + void putLast(@SeqGrowable BlockingDeque this, E e) throws InterruptedException; /** * Inserts the specified element at the front of this deque, @@ -345,7 +350,7 @@ public interface BlockingDeque extends BlockingQueue< */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offerFirst(E e, long timeout, TimeUnit unit) + boolean offerFirst(@SeqGrowable BlockingDeque this, E e, long timeout, TimeUnit unit) throws InterruptedException; /** @@ -369,7 +374,7 @@ boolean offerFirst(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offerLast(E e, long timeout, TimeUnit unit) + boolean offerLast(@SeqGrowable BlockingDeque this, E e, long timeout, TimeUnit unit) throws InterruptedException; /** @@ -381,7 +386,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E takeFirst(@CanShrink BlockingDeque this) throws InterruptedException; + E takeFirst(@Shrinkable @CanShrink BlockingDeque this) throws InterruptedException; /** * Retrieves and removes the last element of this deque, waiting @@ -392,7 +397,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E takeLast(@CanShrink BlockingDeque this) throws InterruptedException; + E takeLast(@Shrinkable @CanShrink BlockingDeque this) throws InterruptedException; /** * Retrieves and removes the first element of this deque, waiting @@ -409,7 +414,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable E pollFirst(@CanShrink BlockingDeque this, long timeout, TimeUnit unit) + @Nullable E pollFirst(@Shrinkable @CanShrink BlockingDeque this, long timeout, TimeUnit unit) throws InterruptedException; /** @@ -427,7 +432,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable E pollLast(@CanShrink BlockingDeque this, long timeout, TimeUnit unit) + @Nullable E pollLast(@Shrinkable @CanShrink BlockingDeque this, long timeout, TimeUnit unit) throws InterruptedException; /** @@ -448,7 +453,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean removeFirstOccurrence(@CanShrink BlockingDeque this, Object o); + boolean removeFirstOccurrence(@Shrinkable @CanShrink BlockingDeque this, Object o); /** * Removes the last occurrence of the specified element from this deque. @@ -468,7 +473,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean removeLastOccurrence(@CanShrink BlockingDeque this, Object o); + boolean removeLastOccurrence(@Shrinkable @CanShrink BlockingDeque this, Object o); // *** BlockingQueue methods *** @@ -494,7 +499,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean add(E e); + boolean add(@Growable BlockingDeque this, E e); /** * Inserts the specified element into the queue represented by this deque @@ -516,7 +521,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offer(E e); + boolean offer(@Growable BlockingDeque this, E e); /** * Inserts the specified element into the queue represented by this deque @@ -535,7 +540,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void put(E e) throws InterruptedException; + void put(@Growable BlockingDeque this, E e) throws InterruptedException; /** * Inserts the specified element into the queue represented by this deque @@ -557,7 +562,7 @@ boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offer(E e, long timeout, TimeUnit unit) + boolean offer(@Growable BlockingDeque this, E e, long timeout, TimeUnit unit) throws InterruptedException; /** @@ -573,7 +578,7 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E remove(@GuardSatisfied @NonEmpty @CanShrink BlockingDeque this); + E remove(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink BlockingDeque this); /** * Retrieves and removes the head of the queue represented by this deque @@ -586,7 +591,7 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable E poll(@CanShrink BlockingDeque this); + @Nullable E poll(@Shrinkable @CanShrink BlockingDeque this); /** * Retrieves and removes the head of the queue represented by this deque @@ -600,7 +605,7 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E take(@CanShrink BlockingDeque this) throws InterruptedException; + E take(@Shrinkable @CanShrink BlockingDeque this) throws InterruptedException; /** * Retrieves and removes the head of the queue represented by this deque @@ -616,7 +621,7 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable E poll(@CanShrink BlockingDeque this, long timeout, TimeUnit unit) + @Nullable E poll(@Shrinkable @CanShrink BlockingDeque this, long timeout, TimeUnit unit) throws InterruptedException; /** @@ -666,7 +671,7 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean remove(@CanShrink BlockingDeque this, @UnknownSignedness Object o); + boolean remove(@Shrinkable @CanShrink BlockingDeque this, @UnknownSignedness Object o); /** * Returns {@code true} if this deque contains the specified element. @@ -700,7 +705,7 @@ boolean offer(E e, long timeout, TimeUnit unit) * @return an iterator over the elements in this deque in proper sequence */ @SideEffectFree - @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty BlockingDeque this); + @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty BlockingDeque this); // *** Stack methods *** @@ -719,5 +724,5 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void push(E e); + void push(@SeqGrowable BlockingDeque this, E e); } diff --git a/src/java.base/share/classes/java/util/concurrent/BlockingQueue.java b/src/java.base/share/classes/java/util/concurrent/BlockingQueue.java index cdc9d81e3a8f..d86c877f2464 100644 --- a/src/java.base/share/classes/java/util/concurrent/BlockingQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/BlockingQueue.java @@ -37,6 +37,9 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -189,7 +192,7 @@ * @author Doug Lea * @param the type of elements held in this queue */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public interface BlockingQueue extends Queue { /** * Inserts the specified element into this queue if it is possible to do @@ -212,7 +215,7 @@ public interface BlockingQueue extends Queue { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean add(E e); + boolean add(@Growable BlockingQueue this, E e); /** * Inserts the specified element into this queue if it is possible to do @@ -233,7 +236,7 @@ public interface BlockingQueue extends Queue { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offer(E e); + boolean offer(@Growable BlockingQueue this, E e); /** * Inserts the specified element into this queue, waiting if necessary @@ -249,7 +252,7 @@ public interface BlockingQueue extends Queue { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - void put(E e) throws InterruptedException; + void put(@Growable BlockingQueue this, E e) throws InterruptedException; /** * Inserts the specified element into this queue, waiting up to the @@ -271,7 +274,7 @@ public interface BlockingQueue extends Queue { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean offer(E e, long timeout, TimeUnit unit) + boolean offer(@Growable BlockingQueue this, E e, long timeout, TimeUnit unit) throws InterruptedException; /** @@ -283,7 +286,7 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - E take() throws InterruptedException; + E take(@Shrinkable BlockingQueue this) throws InterruptedException; /** * Retrieves and removes the head of this queue, waiting up to the @@ -299,7 +302,7 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable E poll(long timeout, TimeUnit unit) + @Nullable E poll(@Shrinkable BlockingQueue this, long timeout, TimeUnit unit) throws InterruptedException; /** @@ -335,7 +338,7 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean remove(@CanShrink BlockingQueue this, @UnknownSignedness Object o); + boolean remove(@Shrinkable @CanShrink BlockingQueue this, @UnknownSignedness Object o); /** * Returns {@code true} if this queue contains the specified element. @@ -379,7 +382,7 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - int drainTo(@GuardSatisfied @CanShrink BlockingQueue this, Collection c); + int drainTo(@Shrinkable @GuardSatisfied @CanShrink BlockingQueue this, @Growable Collection c); /** * Removes at most the given number of available elements from @@ -406,5 +409,5 @@ boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - int drainTo(@GuardSatisfied @CanShrink BlockingQueue this, Collection c, int maxElements); + int drainTo(@Shrinkable @GuardSatisfied @CanShrink BlockingQueue this, @Growable Collection c, int maxElements); } diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java index e033132881ca..72ee6de5259a 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java @@ -36,6 +36,15 @@ package java.util.concurrent; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -280,7 +289,7 @@ * @param the type of keys maintained by this map * @param the type of mapped values */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public class ConcurrentHashMap extends AbstractMap implements ConcurrentMap, Serializable { private static final long serialVersionUID = 7249069246763182397L; @@ -852,7 +861,7 @@ static final void setTabAt(Node[] tab, int i, Node v) { /** * Creates a new, empty map with the default initial table size (16). */ - public ConcurrentHashMap() { + public @Modifiable ConcurrentHashMap() { } /** @@ -865,7 +874,7 @@ public ConcurrentHashMap() { * @throws IllegalArgumentException if the initial capacity of * elements is negative */ - public ConcurrentHashMap(int initialCapacity) { + public @Modifiable ConcurrentHashMap(int initialCapacity) { this(initialCapacity, LOAD_FACTOR, 1); } @@ -874,7 +883,7 @@ public ConcurrentHashMap(int initialCapacity) { * * @param m the map */ - public ConcurrentHashMap(Map m) { + public @Modifiable ConcurrentHashMap(Map m) { this.sizeCtl = DEFAULT_CAPACITY; putAll(m); } @@ -894,7 +903,7 @@ public ConcurrentHashMap(Map m) { * * @since 1.6 */ - public ConcurrentHashMap(int initialCapacity, float loadFactor) { + public @Modifiable ConcurrentHashMap(int initialCapacity, float loadFactor) { this(initialCapacity, loadFactor, 1); } @@ -916,7 +925,7 @@ public ConcurrentHashMap(int initialCapacity, float loadFactor) { * negative or the load factor or concurrencyLevel are * nonpositive */ - public ConcurrentHashMap(int initialCapacity, + public @Modifiable ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) throw new IllegalArgumentException(); @@ -1039,7 +1048,7 @@ public boolean containsValue(@GuardSatisfied @UnknownSignedness Object value) { @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(K key, V value) { + public @Nullable V put(@Growable @Replaceable ConcurrentHashMap this, K key, V value) { return putVal(key, value, false); } @@ -1122,7 +1131,7 @@ else if (f instanceof ReservationNode) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putAll(Map m) { + public void putAll(@Growable @Replaceable ConcurrentHashMap this, Map m) { tryPresize(m.size()); for (Map.Entry e : m.entrySet()) putVal(e.getKey(), e.getValue(), false); @@ -1139,7 +1148,7 @@ public void putAll(Map m) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V remove(@GuardSatisfied @UnknownSignedness Object key) { + public @Nullable V remove(@Shrinkable ConcurrentHashMap this, @GuardSatisfied @UnknownSignedness Object key) { return replaceNode(key, null, null); } @@ -1226,7 +1235,7 @@ else if (f instanceof ReservationNode) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear() { + public void clear(@Shrinkable ConcurrentHashMap this) { long delta = 0L; // negative number of deletions int i = 0; Node[] tab = table; @@ -1277,7 +1286,7 @@ else if ((fh = f.hash) == MOVED) { * @return the set view */ @SideEffectFree - public KeySetView<@KeyFor({"this"}) K,V> keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable KeySetView<@KeyFor({"this"}) K,V> keySet(@PolyShrinkable ConcurrentHashMap this) { KeySetView ks; if ((ks = keySet) != null) return ks; return keySet = new KeySetView(this, null); @@ -1302,7 +1311,7 @@ else if ((fh = f.hash) == MOVED) { * @return the collection view */ @SideEffectFree - public Collection values() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable ConcurrentHashMap this) { ValuesView vs; if ((vs = values) != null) return vs; return values = new ValuesView(this); @@ -1326,7 +1335,7 @@ public Collection values() { * @return the set view */ @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyShrinkable ConcurrentHashMap this) { EntrySetView es; if ((es = entrySet) != null) return es; return entrySet = new EntrySetView(this); @@ -1590,7 +1599,7 @@ private void readObject(java.io.ObjectInputStream s) @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(@Growable ConcurrentHashMap this, K key, V value) { return putVal(key, value, true); } @@ -1601,7 +1610,7 @@ private void readObject(java.io.ObjectInputStream s) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @UnknownSignedness Object key, @GuardSatisfied @UnknownSignedness Object value) { + public boolean remove(@Shrinkable ConcurrentHashMap this, @GuardSatisfied @UnknownSignedness Object key, @GuardSatisfied @UnknownSignedness Object value) { if (key == null) throw new NullPointerException(); return value != null && replaceNode(key, null, value) != null; @@ -1614,7 +1623,7 @@ public boolean remove(@GuardSatisfied @UnknownSignedness Object key, @GuardSatis */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean replace(K key, V oldValue, V newValue) { + public boolean replace(@Replaceable ConcurrentHashMap this, K key, V oldValue, V newValue) { if (key == null || oldValue == null || newValue == null) throw new NullPointerException(); return replaceNode(key, newValue, oldValue) != null; @@ -1629,7 +1638,7 @@ public boolean replace(K key, V oldValue, V newValue) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V replace(K key, V value) { + public @Nullable V replace(@Replaceable ConcurrentHashMap this, K key, V value) { if (key == null || value == null) throw new NullPointerException(); return replaceNode(key, value, null); @@ -1649,7 +1658,9 @@ public boolean replace(K key, V oldValue, V newValue) { * @throws NullPointerException if the specified key is null */ @Pure - public V getOrDefault(@GuardSatisfied @UnknownSignedness Object key, V defaultValue) { + public @PolyModifiable V getOrDefault(ConcurrentHashMap this, + @GuardSatisfied @UnknownSignedness Object key, + @PolyModifiable V defaultValue) { V v; return (v = get(key)) == null ? defaultValue : v; } @@ -1667,7 +1678,7 @@ public void forEach(BiConsumer action) { } @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(BiFunction function) { + public void replaceAll(@Replaceable ConcurrentHashMap this, BiFunction function) { if (function == null) throw new NullPointerException(); Node[] t; if ((t = table) != null) { @@ -1751,7 +1762,7 @@ boolean removeValueIf(Predicate function) { * in which case the mapping is left unestablished */ @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V computeIfAbsent(K key, Function mappingFunction) { + public @PolyNull V computeIfAbsent(@Growable ConcurrentHashMap this, K key, Function mappingFunction) { if (key == null || mappingFunction == null) throw new NullPointerException(); int h = spread(key.hashCode()); @@ -1864,7 +1875,7 @@ else if (f instanceof ReservationNode) * in which case the mapping is unchanged */ @DoesNotUnrefineReceiver("modifiability") - public @Nullable V computeIfPresent(K key, BiFunction remappingFunction) { + public @Nullable V computeIfPresent(@Shrinkable @Replaceable ConcurrentHashMap this, K key, BiFunction remappingFunction) { if (key == null || remappingFunction == null) throw new NullPointerException(); int h = spread(key.hashCode()); @@ -1959,7 +1970,7 @@ else if (f instanceof ReservationNode) * in which case the mapping is unchanged */ @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V compute(K key, + public @PolyNull V compute(@Modifiable ConcurrentHashMap this, K key, BiFunction remappingFunction) { if (key == null || remappingFunction == null) throw new NullPointerException(); @@ -2089,7 +2100,7 @@ else if (f instanceof ReservationNode) * in which case the mapping is unchanged */ @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V merge(K key, @NonNull V value, BiFunction remappingFunction) { + public @PolyNull V merge(@Modifiable ConcurrentHashMap this, K key, @NonNull V value, BiFunction remappingFunction) { if (key == null || value == null || remappingFunction == null) throw new NullPointerException(); int h = spread(key.hashCode()); @@ -2288,7 +2299,7 @@ public static KeySetView newKeySet(int initialCapacity) { * @throws NullPointerException if the mappedValue is null */ @SideEffectFree - public KeySetView keySet(V mappedValue) { + public @IteratorPolyMod KeySetView keySet(V mappedValue) { if (mappedValue == null) throw new NullPointerException(); return new KeySetView(this, mappedValue); @@ -3594,7 +3605,7 @@ public final Map.Entry next(@NonEmpty EntryIterator this) { /** * Exported Entry for EntryIterator. */ - static final class MapEntry implements Map.Entry { + static final @Modifiable class MapEntry implements Map.Entry { final K key; // non-null V val; // non-null final ConcurrentHashMap map; @@ -4327,7 +4338,7 @@ public int reduceValuesToInt(long parallelismThreshold, * @since 1.8 */ public void forEachEntry(long parallelismThreshold, - Consumer> action) { + Consumer> action) { if (action == null) throw new NullPointerException(); new ForEachEntryTask(null, batchFor(parallelismThreshold), 0, 0, table, action).invoke(); @@ -4347,7 +4358,7 @@ public void forEachEntry(long parallelismThreshold, * @since 1.8 */ public void forEachEntry(long parallelismThreshold, - Function, ? extends @Nullable U> transformer, + Function, ? extends @Nullable U> transformer, Consumer action) { if (transformer == null || action == null) throw new NullPointerException(); @@ -4373,7 +4384,7 @@ public void forEachEntry(long parallelismThreshold, * @since 1.8 */ public @Nullable U searchEntries(long parallelismThreshold, - Function, ? extends @Nullable U> searchFunction) { + Function, ? extends @Nullable U> searchFunction) { if (searchFunction == null) throw new NullPointerException(); return new SearchEntriesTask (null, batchFor(parallelismThreshold), 0, 0, table, @@ -4415,7 +4426,7 @@ public void forEachEntry(long parallelismThreshold, * @since 1.8 */ public @Nullable U reduceEntries(long parallelismThreshold, - Function, ? extends @Nullable U> transformer, + Function, ? extends @Nullable U> transformer, BiFunction reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); @@ -4440,7 +4451,7 @@ public void forEachEntry(long parallelismThreshold, * @since 1.8 */ public double reduceEntriesToDouble(long parallelismThreshold, - ToDoubleFunction> transformer, + ToDoubleFunction> transformer, double basis, DoubleBinaryOperator reducer) { if (transformer == null || reducer == null) @@ -4466,7 +4477,7 @@ public double reduceEntriesToDouble(long parallelismThreshold, * @since 1.8 */ public long reduceEntriesToLong(long parallelismThreshold, - ToLongFunction> transformer, + ToLongFunction> transformer, long basis, LongBinaryOperator reducer) { if (transformer == null || reducer == null) @@ -4492,7 +4503,7 @@ public long reduceEntriesToLong(long parallelismThreshold, * @since 1.8 */ public int reduceEntriesToInt(long parallelismThreshold, - ToIntFunction> transformer, + ToIntFunction> transformer, int basis, IntBinaryOperator reducer) { if (transformer == null || reducer == null) @@ -4925,7 +4936,7 @@ public void forEach(Consumer action) { * entries. This class cannot be directly instantiated. See * {@link #entrySet()}. */ - static final class EntrySetView extends CollectionView> + static final @Modifiable class EntrySetView extends CollectionView> implements Set>, java.io.Serializable { private static final long serialVersionUID = 2249069246763182397L; EntrySetView(ConcurrentHashMap map) { super(map); } @@ -5382,6 +5393,7 @@ static final class SearchKeysTask super(p, b, i, f, t); this.searchFunction = searchFunction; this.result = result; } + @Pure public final U getRawResult() { return result.get(); } public final void compute() { final Function searchFunction; @@ -5426,6 +5438,7 @@ static final class SearchValuesTask super(p, b, i, f, t); this.searchFunction = searchFunction; this.result = result; } + @Pure public final U getRawResult() { return result.get(); } public final void compute() { final Function searchFunction; @@ -5470,6 +5483,7 @@ static final class SearchEntriesTask super(p, b, i, f, t); this.searchFunction = searchFunction; this.result = result; } + @Pure public final U getRawResult() { return result.get(); } public final void compute() { final Function, ? extends U> searchFunction; @@ -5514,6 +5528,7 @@ static final class SearchMappingsTask super(p, b, i, f, t); this.searchFunction = searchFunction; this.result = result; } + @Pure public final U getRawResult() { return result.get(); } public final void compute() { final BiFunction searchFunction; diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java index 508917c643bd..e001002516ae 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java @@ -38,6 +38,12 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -112,7 +118,7 @@ * @author Martin Buchholz * @param the type of elements held in this deque */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public class ConcurrentLinkedDeque extends AbstractCollection implements Deque, java.io.Serializable { @@ -788,7 +794,7 @@ private E screenNullResult(@NonNull E v) { /** * Constructs an empty deque. */ - public ConcurrentLinkedDeque() { + public @Modifiable @IteratorPolyMod ConcurrentLinkedDeque() { head = tail = new Node(); } @@ -801,7 +807,7 @@ public ConcurrentLinkedDeque() { * @throws NullPointerException if the specified collection or any * of its elements are null */ - public ConcurrentLinkedDeque(Collection c) { + public @Modifiable @IteratorPolyMod ConcurrentLinkedDeque(Collection c) { // Copy c into a private chain of Nodes Node h = null, t = null; for (E e : c) { @@ -846,7 +852,7 @@ private void initHeadTail(Node h, Node t) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E e) { + public void addFirst(@SeqGrowable ConcurrentLinkedDeque this, E e) { linkFirst(e); } @@ -862,7 +868,7 @@ public void addFirst(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E e) { + public void addLast(@SeqGrowable ConcurrentLinkedDeque this, E e) { linkLast(e); } @@ -875,7 +881,7 @@ public void addLast(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerFirst(E e) { + public boolean offerFirst(@SeqGrowable ConcurrentLinkedDeque this, E e) { linkFirst(e); return true; } @@ -891,7 +897,7 @@ public boolean offerFirst(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerLast(E e) { + public boolean offerLast(@SeqGrowable ConcurrentLinkedDeque this, E e) { linkLast(e); return true; } @@ -948,7 +954,7 @@ public E getLast(@NonEmpty ConcurrentLinkedDeque this) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollFirst(@GuardSatisfied @CanShrink ConcurrentLinkedDeque this) { + public @Nullable E pollFirst(@Shrinkable @GuardSatisfied @CanShrink ConcurrentLinkedDeque this) { restart: for (;;) { for (Node first = first(), p = first;;) { final E item; @@ -971,7 +977,7 @@ public E getLast(@NonEmpty ConcurrentLinkedDeque this) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollLast(@GuardSatisfied @CanShrink ConcurrentLinkedDeque this) { + public @Nullable E pollLast(@Shrinkable @GuardSatisfied @CanShrink ConcurrentLinkedDeque this) { restart: for (;;) { for (Node last = last(), p = last;;) { final E item; @@ -997,7 +1003,7 @@ public E getLast(@NonEmpty ConcurrentLinkedDeque this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeFirst(@GuardSatisfied @NonEmpty @CanShrink ConcurrentLinkedDeque this) { + public E removeFirst(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink ConcurrentLinkedDeque this) { return screenNullResult(pollFirst()); } @@ -1006,7 +1012,7 @@ public E removeFirst(@GuardSatisfied @NonEmpty @CanShrink ConcurrentLinkedDeque< */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeLast(@GuardSatisfied @NonEmpty @CanShrink ConcurrentLinkedDeque this) { + public E removeLast(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink ConcurrentLinkedDeque this) { return screenNullResult(pollLast()); } @@ -1021,7 +1027,7 @@ public E removeLast(@GuardSatisfied @NonEmpty @CanShrink ConcurrentLinkedDeque this, E e) { return offerLast(e); } @@ -1036,13 +1042,13 @@ public boolean offer(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable ConcurrentLinkedDeque this, E e) { return offerLast(e); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@CanShrink ConcurrentLinkedDeque this) { return pollFirst(); } + public @Nullable E poll(@Shrinkable @CanShrink ConcurrentLinkedDeque this) { return pollFirst(); } @Pure public @Nullable E peek() { return peekFirst(); } @@ -1051,15 +1057,14 @@ public boolean add(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@NonEmpty @CanShrink ConcurrentLinkedDeque this) { return removeFirst(); } + public E remove(@Shrinkable @NonEmpty @CanShrink ConcurrentLinkedDeque this) { return removeFirst(); } /** * @throws NoSuchElementException {@inheritDoc} */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E pop(@GuardSatisfied @NonEmpty @CanShrink ConcurrentLinkedDeque this) { return removeFirst(); } - + public E pop(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink ConcurrentLinkedDeque this) { return removeFirst(); } /** * @throws NoSuchElementException {@inheritDoc} */ @@ -1071,8 +1076,7 @@ public boolean add(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void push(E e) { addFirst(e); } - + public void push(@SeqGrowable ConcurrentLinkedDeque this, E e) { addFirst(e); } /** * Removes the first occurrence of the specified element from this deque. * If the deque does not contain the element, it is unchanged. @@ -1087,7 +1091,7 @@ public boolean add(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeFirstOccurrence(@CanShrink ConcurrentLinkedDeque this, Object o) { + public boolean removeFirstOccurrence(@Shrinkable @CanShrink ConcurrentLinkedDeque this, Object o) { Objects.requireNonNull(o); for (Node p = first(); p != null; p = succ(p)) { final E item; @@ -1115,7 +1119,7 @@ public boolean removeFirstOccurrence(@CanShrink ConcurrentLinkedDeque this, O */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeLastOccurrence(@CanShrink ConcurrentLinkedDeque this, Object o) { + public boolean removeLastOccurrence(@Shrinkable @CanShrink ConcurrentLinkedDeque this, Object o) { Objects.requireNonNull(o); for (Node p = last(); p != null; p = pred(p)) { final E item; @@ -1209,7 +1213,7 @@ public int size() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink ConcurrentLinkedDeque this, @GuardSatisfied @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink ConcurrentLinkedDeque this, @GuardSatisfied @UnknownSignedness Object o) { return removeFirstOccurrence(o); } @@ -1227,7 +1231,7 @@ public boolean remove(@CanShrink ConcurrentLinkedDeque this, @GuardSatisfied */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(Collection c) { + public boolean addAll(@Growable ConcurrentLinkedDeque this, Collection c) { if (c == this) // As historically specified in AbstractQueue#addAll throw new IllegalArgumentException(); @@ -1283,7 +1287,7 @@ else if (p.prev == p) // NEXT_TERMINATOR */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink ConcurrentLinkedDeque this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink ConcurrentLinkedDeque this) { while (pollFirst() != null) ; } @@ -1416,7 +1420,7 @@ else if (a != null && size <= a.length) { * @return an iterator over the elements in this deque in proper sequence */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty ConcurrentLinkedDeque this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentLinkedDeque this) { return new Itr(); } @@ -1431,7 +1435,7 @@ else if (a != null && size <= a.length) { * @return an iterator over the elements in this deque in reverse order */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyNonEmpty ConcurrentLinkedDeque this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentLinkedDeque this) { return new DescendingItr(); } @@ -1692,7 +1696,7 @@ private void readObject(java.io.ObjectInputStream s) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink ConcurrentLinkedDeque this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink ConcurrentLinkedDeque this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -1702,7 +1706,7 @@ public boolean removeIf(@CanShrink ConcurrentLinkedDeque this, Predicate this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink ConcurrentLinkedDeque this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); } @@ -1712,7 +1716,7 @@ public boolean removeAll(@CanShrink ConcurrentLinkedDeque this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink ConcurrentLinkedDeque this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java index e11412a7992b..53b9b4ac918b 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java @@ -38,6 +38,11 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -124,7 +129,7 @@ * @author Doug Lea * @param the type of elements held in this queue */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public class ConcurrentLinkedQueue extends AbstractQueue implements Queue, java.io.Serializable { private static final long serialVersionUID = 196745693267521676L; @@ -260,7 +265,7 @@ boolean casItem(E cmp, E val) { /** * Creates a {@code ConcurrentLinkedQueue} that is initially empty. */ - public ConcurrentLinkedQueue() { + public @Modifiable @IteratorPolyMod ConcurrentLinkedQueue() { head = tail = new Node(); } @@ -273,7 +278,7 @@ public ConcurrentLinkedQueue() { * @throws NullPointerException if the specified collection or any * of its elements are null */ - public ConcurrentLinkedQueue(Collection c) { + public @Modifiable @IteratorPolyMod ConcurrentLinkedQueue(Collection c) { Node h = null, t = null; for (E e : c) { Node newNode = new Node(Objects.requireNonNull(e)); @@ -301,7 +306,7 @@ public ConcurrentLinkedQueue(Collection c) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable ConcurrentLinkedQueue this, E e) { return offer(e); } @@ -375,7 +380,7 @@ private Node skipDeadNodes(@Nullable Node pred, Node c, Node p, @Nul */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable ConcurrentLinkedQueue this, E e) { final Node newNode = new Node(Objects.requireNonNull(e)); for (Node t = tail, p = t;;) { @@ -406,7 +411,7 @@ else if (p == q) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink ConcurrentLinkedQueue this) { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink ConcurrentLinkedQueue this) { restartFromHead: for (;;) { for (Node h = head, p = h, q;; p = q) { final E item; @@ -552,7 +557,7 @@ public boolean contains(@GuardSatisfied @UnknownSignedness Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink ConcurrentLinkedQueue this, @GuardSatisfied @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink ConcurrentLinkedQueue this, @GuardSatisfied @UnknownSignedness Object o) { if (o == null) return false; restartFromHead: for (;;) { for (Node p = head, pred = null; p != null; ) { @@ -590,7 +595,7 @@ public boolean remove(@CanShrink ConcurrentLinkedQueue this, @GuardSatisfied */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(Collection c) { + public boolean addAll(@Growable ConcurrentLinkedQueue this, Collection c) { if (c == this) // As historically specified in AbstractQueue#addAll throw new IllegalArgumentException(); @@ -764,7 +769,7 @@ else if (a != null && size <= a.length) { * @return an iterator over the elements in this queue in proper sequence */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty ConcurrentLinkedQueue this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentLinkedQueue this) { return new Itr(); } @@ -1007,7 +1012,7 @@ public Spliterator spliterator() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink ConcurrentLinkedQueue this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink ConcurrentLinkedQueue this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -1017,7 +1022,7 @@ public boolean removeIf(@CanShrink ConcurrentLinkedQueue this, Predicate this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink ConcurrentLinkedQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); } @@ -1027,14 +1032,14 @@ public boolean removeAll(@CanShrink ConcurrentLinkedQueue this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink ConcurrentLinkedQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink ConcurrentLinkedQueue this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink ConcurrentLinkedQueue this) { bulkRemove(e -> true); } diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java index 81fa40933318..2cf609428b6e 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java @@ -35,6 +35,12 @@ package java.util.concurrent; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.MaybeModifiable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nullness.qual.EnsuresKeyFor; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -95,7 +101,9 @@ public interface ConcurrentMap this, + Object key, + @PolyModifiable V defaultValue) { V v; return ((v = get(key)) != null) ? v : defaultValue; } @@ -120,7 +128,7 @@ default V getOrDefault(Object key, V defaultValue) { */ @Override @DoesNotUnrefineReceiver("modifiability") - default void forEach(BiConsumer action) { + default void forEach(@MaybeModifiable ConcurrentMap this, BiConsumer action) { Objects.requireNonNull(action); for (Map.Entry entry : entrySet()) { K k; @@ -170,7 +178,7 @@ default void forEach(BiConsumer action) { @EnsuresKeyFor(value={"#1"}, map={"this"}) @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable V putIfAbsent(K key, V value); + @Nullable V putIfAbsent(@Growable ConcurrentMap this, K key, V value); /** * Removes the entry for a key only if currently mapped to a given value. @@ -203,7 +211,7 @@ default void forEach(BiConsumer action) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean remove(@UnknownSignedness Object key, @UnknownSignedness Object value); + boolean remove(@Shrinkable ConcurrentMap this, @UnknownSignedness Object key, @UnknownSignedness Object value); /** * Replaces the entry for a key only if currently mapped to a given value. @@ -237,7 +245,7 @@ default void forEach(BiConsumer action) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - boolean replace(K key, V oldValue, V newValue); + boolean replace(@Replaceable ConcurrentMap this, K key, V oldValue, V newValue); /** * Replaces the entry for a key only if currently mapped to some value. @@ -271,7 +279,7 @@ default void forEach(BiConsumer action) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - @Nullable V replace(K key, V value); + @Nullable V replace(@Replaceable ConcurrentMap this, K key, V value); /** * {@inheritDoc} @@ -305,7 +313,7 @@ default void forEach(BiConsumer action) { */ @Override @DoesNotUnrefineReceiver("modifiability") - default void replaceAll(BiFunction function) { + default void replaceAll(@Replaceable ConcurrentMap this, BiFunction function) { Objects.requireNonNull(function); forEach((k,v) -> { while (!replace(k, v, function.apply(k, v))) { @@ -346,7 +354,7 @@ default void replaceAll(BiFunction function) */ @Override @DoesNotUnrefineReceiver("modifiability") - default @PolyNull V computeIfAbsent(K key, + default @PolyNull V computeIfAbsent(@Growable ConcurrentMap this, K key, Function mappingFunction) { Objects.requireNonNull(mappingFunction); V oldValue, newValue; @@ -389,7 +397,7 @@ default void replaceAll(BiFunction function) */ @Override @DoesNotUnrefineReceiver("modifiability") - default @Nullable V computeIfPresent(K key, + default @Nullable V computeIfPresent(@Shrinkable @Replaceable ConcurrentMap this, K key, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); for (V oldValue; (oldValue = get(key)) != null; ) { @@ -438,7 +446,7 @@ default void replaceAll(BiFunction function) */ @Override @DoesNotUnrefineReceiver("modifiability") - default @PolyNull V compute(K key, + default @PolyNull V compute(@Modifiable ConcurrentMap this, K key, BiFunction remappingFunction) { retry: for (;;) { V oldValue = get(key); @@ -499,7 +507,7 @@ else if ((oldValue = putIfAbsent(key, newValue)) == null) */ @Override @DoesNotUnrefineReceiver("modifiability") - default @PolyNull V merge(K key, @NonNull V value, + default @PolyNull V merge(@Modifiable ConcurrentMap this, K key, @NonNull V value, BiFunction remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentNavigableMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentNavigableMap.java index 1ff2b80f2088..1eb2448eac2a 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentNavigableMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentNavigableMap.java @@ -35,6 +35,10 @@ package java.util.concurrent; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; import org.checkerframework.dataflow.qual.SideEffectFree; import java.util.NavigableMap; @@ -62,7 +66,8 @@ public interface ConcurrentNavigableMap * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, + @PolyModifiable ConcurrentNavigableMap subMap(@PolyModifiable ConcurrentNavigableMap this, + K fromKey, boolean fromInclusive, K toKey, boolean toInclusive); /** @@ -71,7 +76,8 @@ ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - ConcurrentNavigableMap headMap(K toKey, boolean inclusive); + @PolyModifiable ConcurrentNavigableMap headMap(@PolyModifiable ConcurrentNavigableMap this, + K toKey, boolean inclusive); /** * @throws ClassCastException {@inheritDoc} @@ -79,7 +85,8 @@ ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - ConcurrentNavigableMap tailMap(K fromKey, boolean inclusive); + @PolyModifiable ConcurrentNavigableMap tailMap(@PolyModifiable ConcurrentNavigableMap this, + K fromKey, boolean inclusive); /** * @throws ClassCastException {@inheritDoc} @@ -87,7 +94,8 @@ ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - ConcurrentNavigableMap subMap(K fromKey, K toKey); + @PolyModifiable ConcurrentNavigableMap subMap(@PolyModifiable ConcurrentNavigableMap this, + K fromKey, K toKey); /** * @throws ClassCastException {@inheritDoc} @@ -95,7 +103,8 @@ ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - ConcurrentNavigableMap headMap(K toKey); + @PolyModifiable ConcurrentNavigableMap headMap(@PolyModifiable ConcurrentNavigableMap this, + K toKey); /** * @throws ClassCastException {@inheritDoc} @@ -103,7 +112,8 @@ ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - ConcurrentNavigableMap tailMap(K fromKey); + @PolyModifiable ConcurrentNavigableMap tailMap(@PolyModifiable ConcurrentNavigableMap this, + K fromKey); /** * Returns a reverse order view of the mappings contained in this map. @@ -118,7 +128,7 @@ ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, * @return a reverse order view of this map */ @SideEffectFree - ConcurrentNavigableMap descendingMap(); + @PolyModifiable ConcurrentNavigableMap descendingMap(@PolyModifiable ConcurrentNavigableMap this); /** * Returns a {@link NavigableSet} view of the keys contained in this map. @@ -137,7 +147,7 @@ ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, * @return a navigable set view of the keys in this map */ @SideEffectFree - NavigableSet navigableKeySet(); + @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet navigableKeySet(@PolyShrinkable ConcurrentNavigableMap this); /** * Returns a {@link NavigableSet} view of the keys contained in this map. @@ -158,7 +168,7 @@ ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, * @return a navigable set view of the keys in this map */ @SideEffectFree - NavigableSet keySet(); + @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet keySet(@PolyShrinkable ConcurrentNavigableMap this); /** * Returns a reverse order {@link NavigableSet} view of the keys contained in this map. @@ -177,5 +187,5 @@ ConcurrentNavigableMap subMap(K fromKey, boolean fromInclusive, * @return a reverse order navigable set view of the keys in this map */ @SideEffectFree - NavigableSet descendingKeySet(); + @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet descendingKeySet(@PolyShrinkable ConcurrentNavigableMap this); } diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java index 6c8505d4cfb4..af7f43ff504b 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java @@ -36,6 +36,16 @@ package java.util.concurrent; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.PolyShrinkable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.SeqUngrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Ungrowable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; @@ -47,6 +57,7 @@ import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.framework.qual.AnnotatedFor; import org.checkerframework.framework.qual.DoesNotUnrefineReceiver; import java.lang.invoke.MethodHandles; @@ -123,7 +134,8 @@ * @param the type of mapped values * @since 1.6 */ -public class ConcurrentSkipListMap extends AbstractMap +@AnnotatedFor({"nullness", "modifiability"}) +public @SeqUngrowable class ConcurrentSkipListMap extends AbstractMap implements ConcurrentNavigableMap, Cloneable, Serializable { /* * This class implements a tree-like two-dimensionally linked skip @@ -1083,7 +1095,7 @@ else if (c <= 0 && (rel & LT) != 0) { * Constructs a new, empty map, sorted according to the * {@linkplain Comparable natural ordering} of the keys. */ - public ConcurrentSkipListMap() { + public @Growable @Shrinkable @Replaceable @SeqUngrowable ConcurrentSkipListMap() { this.comparator = null; } @@ -1095,7 +1107,7 @@ public ConcurrentSkipListMap() { * If {@code null}, the {@linkplain Comparable natural * ordering} of the keys will be used. */ - public ConcurrentSkipListMap(@Nullable Comparator comparator) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable ConcurrentSkipListMap(@Nullable Comparator comparator) { this.comparator = comparator; } @@ -1110,7 +1122,7 @@ public ConcurrentSkipListMap(@Nullable Comparator comparator) { * @throws NullPointerException if the specified map or any of its keys * or values are null */ - public ConcurrentSkipListMap(Map m) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable ConcurrentSkipListMap(Map m) { this.comparator = null; putAll(m); } @@ -1124,7 +1136,7 @@ public ConcurrentSkipListMap(Map m) { * @throws NullPointerException if the specified sorted map or any of * its keys or values are null */ - public ConcurrentSkipListMap(SortedMap m) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable ConcurrentSkipListMap(SortedMap m) { this.comparator = m.comparator(); buildFromSorted(m); // initializes transients } @@ -1136,7 +1148,7 @@ public ConcurrentSkipListMap(SortedMap m) { * @return a shallow copy of this map */ @SideEffectFree - public ConcurrentSkipListMap clone() { + public @Growable @Shrinkable @Replaceable @SeqUngrowable ConcurrentSkipListMap clone() { try { @SuppressWarnings("unchecked") ConcurrentSkipListMap clone = @@ -1341,7 +1353,9 @@ public boolean containsKey(@GuardSatisfied @UnknownSignedness Object key) { * @since 1.8 */ @Pure - public V getOrDefault(@GuardSatisfied @UnknownSignedness Object key, V defaultValue) { + public @PolyModifiable V getOrDefault(ConcurrentSkipListMap this, + @GuardSatisfied @UnknownSignedness Object key, + @PolyModifiable V defaultValue) { V v; return (v = doGet(key)) == null ? defaultValue : v; } @@ -1361,7 +1375,7 @@ public V getOrDefault(@GuardSatisfied @UnknownSignedness Object key, V defaultVa */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(K key, V value) { + public @Nullable V put(@Growable @Replaceable ConcurrentSkipListMap this, K key, V value) { if (value == null) throw new NullPointerException(); return doPut(key, value, false); @@ -1379,7 +1393,7 @@ public V getOrDefault(@GuardSatisfied @UnknownSignedness Object key, V defaultVa */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V remove(@GuardSatisfied @UnknownSignedness Object key) { + public @Nullable V remove(@Shrinkable ConcurrentSkipListMap this, @GuardSatisfied @UnknownSignedness Object key) { return doRemove(key, null); } @@ -1436,7 +1450,7 @@ public boolean isEmpty() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear() { + public void clear(@Shrinkable ConcurrentSkipListMap this) { Index h, r, d; Node b; VarHandle.acquireFence(); while ((h = head) != null) { @@ -1482,7 +1496,7 @@ else if ((d = h.down) != null) // remove levels * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V computeIfAbsent(K key, + public @PolyNull V computeIfAbsent(@Growable ConcurrentSkipListMap this, K key, Function mappingFunction) { if (key == null || mappingFunction == null) throw new NullPointerException(); @@ -1507,7 +1521,7 @@ else if ((d = h.down) != null) // remove levels * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - public @Nullable V computeIfPresent(K key, + public @Nullable V computeIfPresent(@Shrinkable @Replaceable ConcurrentSkipListMap this, K key, BiFunction remappingFunction) { if (key == null || remappingFunction == null) throw new NullPointerException(); @@ -1540,7 +1554,7 @@ else if (doRemove(key, v) != null) * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V compute(K key, + public @PolyNull V compute(@Growable @Shrinkable @Replaceable ConcurrentSkipListMap this, K key, BiFunction remappingFunction) { if (key == null || remappingFunction == null) throw new NullPointerException(); @@ -1580,7 +1594,7 @@ else if (doRemove(key, v) != null) * @since 1.8 */ @DoesNotUnrefineReceiver("modifiability") - public @PolyNull V merge(K key, @NonNull V value, + public @PolyNull V merge(@Growable @Shrinkable @Replaceable ConcurrentSkipListMap this, K key, @NonNull V value, BiFunction remappingFunction) { if (key == null || value == null || remappingFunction == null) throw new NullPointerException(); @@ -1643,14 +1657,14 @@ else if (doRemove(key, v) != null) * @return a navigable set view of the keys in this map */ @SideEffectFree - public NavigableSet keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet keySet(@PolyShrinkable ConcurrentSkipListMap this) { KeySet ks; if ((ks = keySet) != null) return ks; return keySet = new KeySet<>(this); } @SideEffectFree - public NavigableSet navigableKeySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet navigableKeySet(@PolyShrinkable ConcurrentSkipListMap this) { KeySet ks; if ((ks = keySet) != null) return ks; return keySet = new KeySet<>(this); @@ -1676,7 +1690,7 @@ public NavigableSet navigableKeySet() { * weakly consistent. */ @SideEffectFree - public Collection values() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable ConcurrentSkipListMap this) { Values vs; if ((vs = values) != null) return vs; return values = new Values<>(this); @@ -1710,14 +1724,14 @@ public Collection values() { * sorted in ascending key order */ @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyShrinkable ConcurrentSkipListMap this) { EntrySet es; if ((es = entrySet) != null) return es; return entrySet = new EntrySet(this); } @SideEffectFree - public ConcurrentNavigableMap descendingMap() { + public @PolyModifiable ConcurrentNavigableMap descendingMap(@PolyModifiable ConcurrentSkipListMap this) { ConcurrentNavigableMap dm; if ((dm = descendingMap) != null) return dm; return descendingMap = @@ -1725,7 +1739,7 @@ public ConcurrentNavigableMap descendingMap() { } @SideEffectFree - public NavigableSet descendingKeySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet descendingKeySet(@PolyShrinkable ConcurrentSkipListMap this) { return descendingMap().navigableKeySet(); } @@ -1823,7 +1837,7 @@ public boolean equals(@Nullable Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(@Growable ConcurrentSkipListMap this, K key, V value) { if (value == null) throw new NullPointerException(); return doPut(key, value, true); @@ -1838,7 +1852,7 @@ public boolean equals(@Nullable Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @UnknownSignedness Object key, @GuardSatisfied @UnknownSignedness Object value) { + public boolean remove(@Shrinkable ConcurrentSkipListMap this, @GuardSatisfied @UnknownSignedness Object key, @GuardSatisfied @UnknownSignedness Object value) { if (key == null) throw new NullPointerException(); return value != null && doRemove(key, value) != null; @@ -1853,7 +1867,7 @@ public boolean remove(@GuardSatisfied @UnknownSignedness Object key, @GuardSatis */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean replace(K key, V oldValue, V newValue) { + public boolean replace(@Replaceable ConcurrentSkipListMap this, K key, V oldValue, V newValue) { if (key == null || oldValue == null || newValue == null) throw new NullPointerException(); for (;;) { @@ -1880,7 +1894,7 @@ public boolean replace(K key, V oldValue, V newValue) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V replace(K key, V value) { + public @Nullable V replace(@Replaceable ConcurrentSkipListMap this, K key, V value) { if (key == null || value == null) throw new NullPointerException(); for (;;) { @@ -1902,6 +1916,7 @@ public boolean replace(K key, V oldValue, V newValue) { /** * @throws NoSuchElementException {@inheritDoc} */ + @Pure public K firstKey(@NonEmpty ConcurrentSkipListMap this) { Node n = findFirst(); if (n == null) @@ -1912,6 +1927,7 @@ public K firstKey(@NonEmpty ConcurrentSkipListMap this) { /** * @throws NoSuchElementException {@inheritDoc} */ + @Pure public K lastKey(@NonEmpty ConcurrentSkipListMap this) { Node n = findLast(); if (n == null) @@ -1929,7 +1945,7 @@ public K lastKey(@NonEmpty ConcurrentSkipListMap this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putFirst(K k, V v) { + public @Nullable V putFirst(@SeqGrowable ConcurrentSkipListMap this, K k, V v) { throw new UnsupportedOperationException(); } @@ -1943,7 +1959,7 @@ public K lastKey(@NonEmpty ConcurrentSkipListMap this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putLast(K k, V v) { + public @Nullable V putLast(@SeqGrowable ConcurrentSkipListMap this, K k, V v) { throw new UnsupportedOperationException(); } @@ -1953,7 +1969,7 @@ public K lastKey(@NonEmpty ConcurrentSkipListMap this) { * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public ConcurrentNavigableMap subMap(K fromKey, + public @PolyModifiable ConcurrentNavigableMap subMap(@PolyModifiable ConcurrentSkipListMap this, K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { @@ -1969,7 +1985,7 @@ public ConcurrentNavigableMap subMap(K fromKey, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public ConcurrentNavigableMap headMap(K toKey, + public @PolyModifiable ConcurrentNavigableMap headMap(@PolyModifiable ConcurrentSkipListMap this, K toKey, boolean inclusive) { if (toKey == null) throw new NullPointerException(); @@ -1983,7 +1999,7 @@ public ConcurrentNavigableMap headMap(K toKey, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public ConcurrentNavigableMap tailMap(K fromKey, + public @PolyModifiable ConcurrentNavigableMap tailMap(@PolyModifiable ConcurrentSkipListMap this, K fromKey, boolean inclusive) { if (fromKey == null) throw new NullPointerException(); @@ -1997,7 +2013,7 @@ public ConcurrentNavigableMap tailMap(K fromKey, * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public ConcurrentNavigableMap subMap(K fromKey, K toKey) { + public @PolyModifiable ConcurrentNavigableMap subMap(@PolyModifiable ConcurrentSkipListMap this, K fromKey, K toKey) { return subMap(fromKey, true, toKey, false); } @@ -2007,7 +2023,7 @@ public ConcurrentNavigableMap subMap(K fromKey, K toKey) { * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public ConcurrentNavigableMap headMap(K toKey) { + public @PolyModifiable ConcurrentNavigableMap headMap(@PolyModifiable ConcurrentSkipListMap this, K toKey) { return headMap(toKey, false); } @@ -2017,7 +2033,7 @@ public ConcurrentNavigableMap headMap(K toKey) { * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public ConcurrentNavigableMap tailMap(K fromKey) { + public @PolyModifiable ConcurrentNavigableMap tailMap(@PolyModifiable ConcurrentSkipListMap this, K fromKey) { return tailMap(fromKey, true); } @@ -2032,7 +2048,8 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified key is null */ - public Map.@Nullable Entry lowerEntry(K key) { + @Pure + public Map.@Unmodifiable @Nullable Entry lowerEntry(K key) { return findNearEntry(key, LT, comparator); } @@ -2040,6 +2057,7 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified key is null */ + @Pure public @Nullable K lowerKey(K key) { Node n = findNear(key, LT, comparator); return (n == null) ? null : n.key; @@ -2055,7 +2073,8 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified key is null */ - public Map.@Nullable Entry floorEntry(K key) { + @Pure + public Map.@Unmodifiable @Nullable Entry floorEntry(K key) { return findNearEntry(key, LT|EQ, comparator); } @@ -2064,6 +2083,7 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified key is null */ + @Pure public @Nullable K floorKey(K key) { Node n = findNear(key, LT|EQ, comparator); return (n == null) ? null : n.key; @@ -2078,7 +2098,8 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified key is null */ - public Map.@Nullable Entry ceilingEntry(K key) { + @Pure + public Map.@Unmodifiable @Nullable Entry ceilingEntry(K key) { return findNearEntry(key, GT|EQ, comparator); } @@ -2086,6 +2107,7 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified key is null */ + @Pure public @Nullable K ceilingKey(K key) { Node n = findNear(key, GT|EQ, comparator); return (n == null) ? null : n.key; @@ -2101,7 +2123,8 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified key is null */ - public Map.@Nullable Entry higherEntry(K key) { + @Pure + public Map.@Unmodifiable @Nullable Entry higherEntry(K key) { return findNearEntry(key, GT, comparator); } @@ -2110,6 +2133,7 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * @throws ClassCastException {@inheritDoc} * @throws NullPointerException if the specified key is null */ + @Pure public @Nullable K higherKey(K key) { Node n = findNear(key, GT, comparator); return (n == null) ? null : n.key; @@ -2121,7 +2145,8 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * The returned entry does not support * the {@code Entry.setValue} method. */ - public Map.@Nullable Entry firstEntry() { + @Pure + public Map.@Unmodifiable @Nullable Entry firstEntry() { return findFirstEntry(); } @@ -2131,7 +2156,8 @@ public ConcurrentNavigableMap tailMap(K fromKey) { * The returned entry does not support * the {@code Entry.setValue} method. */ - public Map.@Nullable Entry lastEntry() { + @Pure + public Map.@Unmodifiable @Nullable Entry lastEntry() { return findLastEntry(); } @@ -2143,7 +2169,7 @@ public ConcurrentNavigableMap tailMap(K fromKey) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public Map.@Nullable Entry pollFirstEntry() { + public Map.@Unmodifiable @Nullable Entry pollFirstEntry(@Shrinkable ConcurrentSkipListMap this) { return doRemoveFirstEntry(); } @@ -2155,7 +2181,7 @@ public ConcurrentNavigableMap tailMap(K fromKey) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public Map.@Nullable Entry pollLastEntry() { + public Map.@Unmodifiable @Nullable Entry pollLastEntry(@Shrinkable ConcurrentSkipListMap this) { return doRemoveLastEntry(); } @@ -2281,15 +2307,19 @@ static final class KeySet @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") public void clear() { m.clear(); } + @Pure public @Nullable K lower(K e) { return m.lowerKey(e); } + @Pure public @Nullable K floor(K e) { return m.floorKey(e); } + @Pure public @Nullable K ceiling(K e) { return m.ceilingKey(e); } + @Pure public @Nullable K higher(K e) { return m.higherKey(e); } @Pure public @Nullable Comparator comparator() { return m.comparator(); } - @SideEffectFree + @Pure public K first() { return m.firstKey(); } - @SideEffectFree + @Pure public K last() { return m.lastKey(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") @@ -2779,14 +2809,14 @@ public boolean containsKey(@UnknownSignedness Object key) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V put(K key, V value) { + public @Nullable V put(@Growable @Replaceable SubMap this, K key, V value) { checkKeyBounds(key, m.comparator); return m.put(key, value); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V remove(Object key) { + public @Nullable V remove(@Shrinkable SubMap this, Object key) { return (!inBounds(key, m.comparator)) ? null : m.remove(key); } @@ -2827,7 +2857,7 @@ public boolean containsValue(@UnknownSignedness Object value) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear() { + public void clear(@Shrinkable SubMap this) { Comparator cmp = m.comparator; for (ConcurrentSkipListMap.Node n = loNode(cmp); isBeforeEnd(n, cmp); @@ -2841,27 +2871,27 @@ public void clear() { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(@Growable SubMap this, K key, V value) { checkKeyBounds(key, m.comparator); return m.putIfAbsent(key, value); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@UnknownSignedness Object key, @UnknownSignedness Object value) { + public boolean remove(@Shrinkable SubMap this, @UnknownSignedness Object key, @UnknownSignedness Object value) { return inBounds(key, m.comparator) && m.remove(key, value); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean replace(K key, V oldValue, V newValue) { + public boolean replace(@Replaceable SubMap this, K key, V oldValue, V newValue) { checkKeyBounds(key, m.comparator); return m.replace(key, oldValue, newValue); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable V replace(K key, V value) { + public @Nullable V replace(@Replaceable SubMap this, K key, V value) { checkKeyBounds(key, m.comparator); return m.replace(key, value); } @@ -2919,7 +2949,7 @@ SubMap newSubMap(K fromKey, boolean fromInclusive, } @SideEffectFree - public SubMap subMap(K fromKey, boolean fromInclusive, + public @PolyModifiable SubMap subMap(@PolyModifiable SubMap this, K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { if (fromKey == null || toKey == null) throw new NullPointerException(); @@ -2927,134 +2957,146 @@ public SubMap subMap(K fromKey, boolean fromInclusive, } @SideEffectFree - public SubMap headMap(K toKey, boolean inclusive) { + public @PolyModifiable SubMap headMap(@PolyModifiable SubMap this, K toKey, boolean inclusive) { if (toKey == null) throw new NullPointerException(); return newSubMap(null, false, toKey, inclusive); } @SideEffectFree - public SubMap tailMap(K fromKey, boolean inclusive) { + public @PolyModifiable SubMap tailMap(@PolyModifiable SubMap this, K fromKey, boolean inclusive) { if (fromKey == null) throw new NullPointerException(); return newSubMap(fromKey, inclusive, null, false); } @SideEffectFree - public SubMap subMap(K fromKey, K toKey) { + public @PolyModifiable SubMap subMap(@PolyModifiable SubMap this, K fromKey, K toKey) { return subMap(fromKey, true, toKey, false); } @SideEffectFree - public SubMap headMap(K toKey) { + public @PolyModifiable SubMap headMap(@PolyModifiable SubMap this, K toKey) { return headMap(toKey, false); } @SideEffectFree - public SubMap tailMap(K fromKey) { + public @PolyModifiable SubMap tailMap(@PolyModifiable SubMap this, K fromKey) { return tailMap(fromKey, true); } @SideEffectFree - public SubMap descendingMap() { + public @PolyModifiable SubMap descendingMap(@PolyModifiable SubMap this) { return new SubMap(m, lo, loInclusive, hi, hiInclusive, !isDescending); } /* ---------------- Relational methods -------------- */ + @Pure public Map.@Nullable Entry ceilingEntry(K key) { return getNearEntry(key, GT|EQ); } + @Pure public @Nullable K ceilingKey(K key) { return getNearKey(key, GT|EQ); } + @Pure public Map.@Nullable Entry lowerEntry(K key) { return getNearEntry(key, LT); } + @Pure public @Nullable K lowerKey(K key) { return getNearKey(key, LT); } + @Pure public Map.@Nullable Entry floorEntry(K key) { return getNearEntry(key, LT|EQ); } + @Pure public @Nullable K floorKey(K key) { return getNearKey(key, LT|EQ); } + @Pure public Map.@Nullable Entry higherEntry(K key) { return getNearEntry(key, GT); } + @Pure public @Nullable K higherKey(K key) { return getNearKey(key, GT); } + @Pure public K firstKey() { return isDescending ? highestKey() : lowestKey(); } + @Pure public K lastKey() { return isDescending ? lowestKey() : highestKey(); } + @Pure public Map.@Nullable Entry firstEntry() { return isDescending ? highestEntry() : lowestEntry(); } + @Pure public Map.@Nullable Entry lastEntry() { return isDescending ? lowestEntry() : highestEntry(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public Map.@Nullable Entry pollFirstEntry() { + public Map.@Nullable Entry pollFirstEntry(@Shrinkable SubMap this) { return isDescending ? removeHighest() : removeLowest(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public Map.@Nullable Entry pollLastEntry() { + public Map.@Nullable Entry pollLastEntry(@Shrinkable SubMap this) { return isDescending ? removeLowest() : removeHighest(); } /* ---------------- Submap Views -------------- */ @SideEffectFree - public NavigableSet keySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet keySet(@PolyShrinkable SubMap this) { KeySet ks; if ((ks = keySetView) != null) return ks; return keySetView = new KeySet<>(this); } @SideEffectFree - public NavigableSet navigableKeySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable NavigableSet navigableKeySet(@PolyShrinkable SubMap this) { KeySet ks; if ((ks = keySetView) != null) return ks; return keySetView = new KeySet<>(this); } @SideEffectFree - public Collection values() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Collection values(@PolyShrinkable SubMap this) { Values vs; if ((vs = valuesView) != null) return vs; return valuesView = new Values<>(this); } @SideEffectFree - public Set> entrySet() { + public @IteratorPolyMod @PolyShrinkable @Ungrowable Set> entrySet(@PolyShrinkable SubMap this) { EntrySet es; if ((es = entrySetView) != null) return es; return entrySetView = new EntrySet(this); } @SideEffectFree - public NavigableSet descendingKeySet() { + public @IteratorPolyMod NavigableSet descendingKeySet() { return descendingMap().navigableKeySet(); } @@ -3232,7 +3274,7 @@ public void forEach(BiConsumer action) { } @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(BiFunction function) { + public void replaceAll(@Replaceable ConcurrentSkipListMap this, BiFunction function) { if (function == null) throw new NullPointerException(); Node b, n; V v; if ((b = baseHead()) != null) { diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java index f4abbc8b9c73..17e6547ea04f 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java @@ -37,6 +37,13 @@ import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.SeqUngrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -108,8 +115,8 @@ * @param the type of elements maintained by this set * @since 1.6 */ -@AnnotatedFor({"nullness"}) -public class ConcurrentSkipListSet +@AnnotatedFor({"nullness", "modifiability"}) +public @SeqUngrowable class ConcurrentSkipListSet extends AbstractSet implements NavigableSet, Cloneable, java.io.Serializable { @@ -127,7 +134,7 @@ public class ConcurrentSkipListSet * Constructs a new, empty set that orders its elements according to * their {@linkplain Comparable natural ordering}. */ - public ConcurrentSkipListSet() { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod ConcurrentSkipListSet() { m = new ConcurrentSkipListMap(); } @@ -139,7 +146,7 @@ public ConcurrentSkipListSet() { * If {@code null}, the {@linkplain Comparable natural * ordering} of the elements will be used. */ - public ConcurrentSkipListSet(@Nullable Comparator comparator) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod ConcurrentSkipListSet(@Nullable Comparator comparator) { m = new ConcurrentSkipListMap(comparator); } @@ -154,7 +161,7 @@ public ConcurrentSkipListSet(@Nullable Comparator comparator) { * @throws NullPointerException if the specified collection or any * of its elements are null */ - public ConcurrentSkipListSet(Collection c) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod ConcurrentSkipListSet(Collection c) { m = new ConcurrentSkipListMap(); addAll(c); } @@ -167,7 +174,7 @@ public ConcurrentSkipListSet(Collection c) { * @throws NullPointerException if the specified sorted set or any * of its elements are null */ - public ConcurrentSkipListSet(SortedSet s) { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod ConcurrentSkipListSet(SortedSet s) { m = new ConcurrentSkipListMap(s.comparator()); addAll(s); } @@ -175,7 +182,7 @@ public ConcurrentSkipListSet(SortedSet s) { /** * For use by submaps */ - ConcurrentSkipListSet(ConcurrentNavigableMap m) { + @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod ConcurrentSkipListSet(ConcurrentNavigableMap m) { this.m = m; } @@ -186,7 +193,7 @@ public ConcurrentSkipListSet(SortedSet s) { * @return a shallow copy of this set */ @SideEffectFree - public ConcurrentSkipListSet clone() { + public @Growable @Shrinkable @Replaceable @SeqUngrowable @IteratorPolyMod ConcurrentSkipListSet clone() { try { @SuppressWarnings("unchecked") ConcurrentSkipListSet clone = @@ -265,7 +272,7 @@ public boolean contains(@GuardSatisfied @UnknownSignedness Object o) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable ConcurrentSkipListSet this, E e) { return m.putIfAbsent(e, Boolean.TRUE) == null; } @@ -285,7 +292,7 @@ public boolean add(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @UnknownSignedness Object o) { + public boolean remove(@Shrinkable ConcurrentSkipListSet this, @GuardSatisfied @UnknownSignedness Object o) { return m.remove(o, Boolean.TRUE); } @@ -294,7 +301,7 @@ public boolean remove(@GuardSatisfied @UnknownSignedness Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear() { + public void clear(@Shrinkable ConcurrentSkipListSet this) { m.clear(); } @@ -304,7 +311,7 @@ public void clear() { * @return an iterator over the elements in this set in ascending order */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty ConcurrentSkipListSet this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentSkipListSet this) { return m.navigableKeySet().iterator(); } @@ -314,7 +321,7 @@ public void clear() { * @return an iterator over the elements in this set in descending order */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyNonEmpty ConcurrentSkipListSet this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentSkipListSet this) { return m.descendingKeySet().iterator(); } @@ -365,7 +372,7 @@ public boolean equals(@Nullable Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeAll(Collection c) { + public boolean removeAll(@Shrinkable ConcurrentSkipListSet this, Collection c) { // Override AbstractSet version to avoid unnecessary call to size() boolean modified = false; for (Object e : c) @@ -414,14 +421,14 @@ public boolean removeAll(Collection this) { Map.Entry e = m.pollFirstEntry(); return (e == null) ? null : e.getKey(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollLast() { + public @Nullable E pollLast(@Shrinkable ConcurrentSkipListSet this) { Map.Entry e = m.pollLastEntry(); return (e == null) ? null : e.getKey(); } @@ -437,7 +444,7 @@ public boolean removeAll(Collection this) { return m.firstKey(); } @@ -445,7 +452,7 @@ public E first(@NonEmpty ConcurrentSkipListSet this) { /** * @throws java.util.NoSuchElementException {@inheritDoc} */ - @SideEffectFree + @Pure public E last(@NonEmpty ConcurrentSkipListSet this) { return m.lastKey(); } @@ -461,7 +468,7 @@ public E last(@NonEmpty ConcurrentSkipListSet this) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E e) { + public void addFirst(@SeqGrowable ConcurrentSkipListSet this, E e) { throw new UnsupportedOperationException(); } @@ -476,7 +483,7 @@ public void addFirst(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E e) { + public void addLast(@SeqGrowable ConcurrentSkipListSet this, E e) { throw new UnsupportedOperationException(); } @@ -486,7 +493,7 @@ public void addLast(E e) { * {@code toElement} is null * @throws IllegalArgumentException {@inheritDoc} */ - public @PolyGrowShrink @PolyNonEmpty NavigableSet subSet(@PolyGrowShrink @PolyNonEmpty ConcurrentSkipListSet this, E fromElement, + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty NavigableSet subSet(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentSkipListSet this, E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { @@ -501,7 +508,7 @@ public void addLast(E e) { * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty NavigableSet headSet(@PolyGrowShrink @PolyNonEmpty ConcurrentSkipListSet this, E toElement, boolean inclusive) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty NavigableSet headSet(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentSkipListSet this, E toElement, boolean inclusive) { return new ConcurrentSkipListSet(m.headMap(toElement, inclusive)); } @@ -511,7 +518,7 @@ public void addLast(E e) { * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty NavigableSet tailSet(@PolyGrowShrink @PolyNonEmpty ConcurrentSkipListSet this, E fromElement, boolean inclusive) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty NavigableSet tailSet(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentSkipListSet this, E fromElement, boolean inclusive) { return new ConcurrentSkipListSet(m.tailMap(fromElement, inclusive)); } @@ -522,7 +529,7 @@ public void addLast(E e) { * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty NavigableSet subSet(@PolyGrowShrink @PolyNonEmpty ConcurrentSkipListSet this, E fromElement, E toElement) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty NavigableSet subSet(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentSkipListSet this, E fromElement, E toElement) { return subSet(fromElement, true, toElement, false); } @@ -532,7 +539,7 @@ public void addLast(E e) { * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty NavigableSet headSet(@PolyGrowShrink @PolyNonEmpty ConcurrentSkipListSet this, E toElement) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty NavigableSet headSet(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentSkipListSet this, E toElement) { return headSet(toElement, false); } @@ -542,7 +549,7 @@ public void addLast(E e) { * @throws IllegalArgumentException {@inheritDoc} */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty NavigableSet tailSet(@PolyGrowShrink @PolyNonEmpty ConcurrentSkipListSet this, E fromElement) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty NavigableSet tailSet(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentSkipListSet this, E fromElement) { return tailSet(fromElement, true); } @@ -558,7 +565,7 @@ public void addLast(E e) { * * @return a reverse order view of this set */ - public @PolyGrowShrink @PolyNonEmpty NavigableSet descendingSet(@PolyGrowShrink @PolyNonEmpty ConcurrentSkipListSet this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty NavigableSet descendingSet(@PolyGrowShrink @PolyModifiable @PolyNonEmpty ConcurrentSkipListSet this) { return new ConcurrentSkipListSet(m.descendingMap()); } diff --git a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java index c51f830f2860..8e10a53e7342 100644 --- a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java +++ b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java @@ -37,6 +37,13 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Replaceable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -47,9 +54,11 @@ import org.checkerframework.checker.nullness.qual.PolyNull; import org.checkerframework.checker.signedness.qual.PolySigned; import org.checkerframework.checker.signedness.qual.UnknownSignedness; +import org.checkerframework.common.value.qual.StaticallyExecutable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.framework.qual.AnnotatedFor; import org.checkerframework.framework.qual.DoesNotUnrefineReceiver; import java.lang.invoke.VarHandle; @@ -114,6 +123,7 @@ * @author Doug Lea * @param the type of elements held in this list */ +@AnnotatedFor("modifiability") public class CopyOnWriteArrayList implements List, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; @@ -145,7 +155,7 @@ final void setArray(Object[] a) { /** * Creates an empty list. */ - public CopyOnWriteArrayList() { + public @Modifiable CopyOnWriteArrayList() { setArray(new Object[0]); } @@ -157,7 +167,7 @@ public CopyOnWriteArrayList() { * @param c the collection of initially held elements * @throws NullPointerException if the specified collection is null */ - public CopyOnWriteArrayList(Collection c) { + public @Modifiable CopyOnWriteArrayList(Collection c) { Object[] es; if (c.getClass() == CopyOnWriteArrayList.class) es = ((CopyOnWriteArrayList)c).getArray(); @@ -176,7 +186,7 @@ public CopyOnWriteArrayList(Collection c) { * internal array) * @throws NullPointerException if the specified array is null */ - public CopyOnWriteArrayList(E[] toCopyIn) { + public @Modifiable CopyOnWriteArrayList(E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); } @@ -262,6 +272,7 @@ public boolean contains(@GuardSatisfied @Nullable @UnknownSignedness Object o) { * {@inheritDoc} */ @Pure + @StaticallyExecutable public int indexOf(@GuardSatisfied @Nullable @UnknownSignedness Object o) { Object[] es = getArray(); return indexOfRange(o, es, 0, es.length); @@ -283,6 +294,7 @@ public int indexOf(@GuardSatisfied @Nullable @UnknownSignedness Object o) { * @throws IndexOutOfBoundsException if the specified index is negative */ @Pure + @StaticallyExecutable public int indexOf(E e, int index) { Object[] es = getArray(); return indexOfRange(e, es, index, es.length); @@ -292,6 +304,7 @@ public int indexOf(E e, int index) { * {@inheritDoc} */ @Pure + @StaticallyExecutable public int lastIndexOf(@GuardSatisfied @Nullable @UnknownSignedness Object o) { Object[] es = getArray(); return lastIndexOfRange(o, es, 0, es.length); @@ -314,6 +327,7 @@ public int lastIndexOf(@GuardSatisfied @Nullable @UnknownSignedness Object o) { * than or equal to the current size of this list */ @Pure + @StaticallyExecutable public int lastIndexOf(E e, int index) { Object[] es = getArray(); return lastIndexOfRange(e, es, 0, index + 1); @@ -326,7 +340,7 @@ public int lastIndexOf(E e, int index) { * @return a clone of this list */ @SideEffectFree - public Object clone() { + public @Modifiable Object clone() { try { @SuppressWarnings("unchecked") CopyOnWriteArrayList clone = @@ -475,7 +489,7 @@ public E getLast() { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E set(int index, E element) { + public E set(@Replaceable CopyOnWriteArrayList this, int index, E element) { synchronized (lock) { Object[] es = getArray(); E oldValue = elementAt(es, index); @@ -499,7 +513,7 @@ public E set(int index, E element) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable CopyOnWriteArrayList this, E e) { synchronized (lock) { Object[] es = getArray(); int len = es.length; @@ -520,7 +534,7 @@ public boolean add(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void add(int index, E element) { + public void add(@Growable CopyOnWriteArrayList this, int index, E element) { synchronized (lock) { Object[] es = getArray(); int len = es.length; @@ -549,7 +563,7 @@ public void add(int index, E element) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E e) { + public void addFirst(@SeqGrowable CopyOnWriteArrayList this, E e) { add(0, e); } @@ -561,7 +575,7 @@ public void addFirst(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E e) { + public void addLast(@SeqGrowable CopyOnWriteArrayList this, E e) { synchronized (lock) { add(getArray().length, e); } @@ -576,7 +590,7 @@ public void addLast(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@GuardSatisfied @CanShrink CopyOnWriteArrayList this, int index) { + public E remove(@Shrinkable @GuardSatisfied @CanShrink CopyOnWriteArrayList this, int index) { synchronized (lock) { Object[] es = getArray(); int len = es.length; @@ -604,7 +618,7 @@ public E remove(@GuardSatisfied @CanShrink CopyOnWriteArrayList this, int ind */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeFirst() { + public E removeFirst(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink CopyOnWriteArrayList this) { synchronized (lock) { if (getArray().length == 0) throw new NoSuchElementException(); @@ -621,7 +635,7 @@ public E removeFirst() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeLast() { + public E removeLast(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink CopyOnWriteArrayList this) { synchronized (lock) { int size = getArray().length; if (size == 0) @@ -645,7 +659,7 @@ public E removeLast() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink CopyOnWriteArrayList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink CopyOnWriteArrayList this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { Object[] snapshot = getArray(); int index = indexOfRange(o, snapshot, 0, snapshot.length); return index >= 0 && remove(o, snapshot, index); @@ -727,7 +741,7 @@ void removeRange(@GuardSatisfied @CanShrink CopyOnWriteArrayList this, int fr */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addIfAbsent(E e) { + public boolean addIfAbsent(@Growable CopyOnWriteArrayList this, E e) { Object[] snapshot = getArray(); return indexOfRange(e, snapshot, 0, snapshot.length) < 0 && addIfAbsent(e, snapshot); @@ -797,7 +811,7 @@ public boolean containsAll(Collection c) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeAll(@CanShrink CopyOnWriteArrayList this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink CopyOnWriteArrayList this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); } @@ -820,7 +834,7 @@ public boolean removeAll(@CanShrink CopyOnWriteArrayList this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink CopyOnWriteArrayList this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } @@ -838,7 +852,7 @@ public boolean retainAll(@GuardSatisfied @CanShrink CopyOnWriteArrayList this */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public int addAllAbsent(Collection c) { + public int addAllAbsent(@Growable CopyOnWriteArrayList this, Collection c) { Object[] cs = c.toArray(); if (c.getClass() != ArrayList.class) { cs = cs.clone(); @@ -871,7 +885,7 @@ public int addAllAbsent(Collection c) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink CopyOnWriteArrayList this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink CopyOnWriteArrayList this) { synchronized (lock) { setArray(new Object[0]); } @@ -889,7 +903,7 @@ public void clear(@GuardSatisfied @CanShrink CopyOnWriteArrayList this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(Collection c) { + public boolean addAll(@Growable CopyOnWriteArrayList this, Collection c) { Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ? ((CopyOnWriteArrayList)c).getArray() : c.toArray(); if (cs.length == 0) @@ -928,7 +942,7 @@ public boolean addAll(Collection c) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(int index, Collection c) { + public boolean addAll(@Growable CopyOnWriteArrayList this, int index, Collection c) { Object[] cs = c.toArray(); synchronized (lock) { Object[] es = getArray(); @@ -970,7 +984,7 @@ public void forEach(Consumer action) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink CopyOnWriteArrayList this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink CopyOnWriteArrayList this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -1028,7 +1042,7 @@ boolean bulkRemove(Predicate filter, int i, int end) { } @DoesNotUnrefineReceiver("modifiability") - public void replaceAll(UnaryOperator operator) { + public void replaceAll(@Replaceable CopyOnWriteArrayList this, UnaryOperator operator) { synchronized (lock) { replaceAllRange(operator, 0, getArray().length); } @@ -1045,7 +1059,7 @@ void replaceAllRange(UnaryOperator operator, int i, int end) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void sort(Comparator c) { + public void sort(@Replaceable CopyOnWriteArrayList this, Comparator c) { synchronized (lock) { sortRange(c, 0, getArray().length); } @@ -1187,7 +1201,7 @@ public int hashCode() { * @return an iterator over the elements in this list in proper sequence */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty CopyOnWriteArrayList this) { + public @Unmodifiable @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty CopyOnWriteArrayList this) { return new COWIterator(getArray(), 0); } @@ -1199,7 +1213,7 @@ public int hashCode() { * traversing the iterator. The iterator does NOT support the * {@code remove}, {@code set} or {@code add} methods. */ - public @PolyGrowShrink @PolyNonEmpty ListIterator listIterator(@PolyGrowShrink @PolyNonEmpty CopyOnWriteArrayList this) { + public @Unmodifiable @PolyGrowShrink @PolyNonEmpty ListIterator listIterator(@PolyGrowShrink @PolyNonEmpty CopyOnWriteArrayList this) { return new COWIterator(getArray(), 0); } @@ -1213,7 +1227,7 @@ public int hashCode() { * * @throws IndexOutOfBoundsException {@inheritDoc} */ - public @PolyGrowShrink ListIterator listIterator(@PolyGrowShrink CopyOnWriteArrayList this, int index) { + public @Unmodifiable @PolyGrowShrink ListIterator listIterator(@PolyGrowShrink CopyOnWriteArrayList this, int index) { Object[] es = getArray(); int len = es.length; if (index < 0 || index > len) @@ -1351,7 +1365,7 @@ public void forEachRemaining(Consumer action) { * @throws IndexOutOfBoundsException {@inheritDoc} */ @SideEffectFree - public @PolyGrowShrink List subList(@PolyGrowShrink CopyOnWriteArrayList this, int fromIndex, int toIndex) { + public @PolyGrowShrink @PolyModifiable List subList(@PolyGrowShrink @PolyModifiable CopyOnWriteArrayList this, int fromIndex, int toIndex) { synchronized (lock) { Object[] es = getArray(); int len = es.length; @@ -1438,6 +1452,7 @@ public Object[] toArray() { } @Pure + @StaticallyExecutable public int indexOf(Object o) { final Object[] es; final int offset; @@ -1452,6 +1467,7 @@ public int indexOf(Object o) { } @Pure + @StaticallyExecutable public int lastIndexOf(Object o) { final Object[] es; final int offset; @@ -1613,13 +1629,13 @@ public void add(int index, E element) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E e) { + public void addFirst(@SeqGrowable COWSubList this, E e) { add(0, e); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E e) { + public void addLast(@SeqGrowable COWSubList this, E e) { synchronized (lock) { add(size, e); } @@ -1895,7 +1911,7 @@ public void forEachRemaining(Consumer action) { * @since 21 */ @SideEffectFree - public List reversed() { + public @PolyGrowShrink @PolyModifiable List reversed(@PolyGrowShrink @PolyModifiable CopyOnWriteArrayList this) { return new Reversed<>(this, lock); } @@ -2128,6 +2144,7 @@ public T[] toArray(T[] a) { return toArray(i -> (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), i)); } + @SideEffectFree public T[] toArray(IntFunction generator) { return ArraysSupport.reverse(base.toArray(generator)); } @@ -2162,13 +2179,13 @@ public void add(int index, E element) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E e) { + public void addFirst(@SeqGrowable Reversed this, E e) { base.add(e); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E e) { + public void addLast(@SeqGrowable Reversed this, E e) { base.add(0, e); } @@ -2219,6 +2236,7 @@ public E getLast() { } @Pure + @StaticallyExecutable public int indexOf(Object o) { synchronized (lock) { int i = base.lastIndexOf(o); @@ -2227,6 +2245,7 @@ public int indexOf(Object o) { } @Pure + @StaticallyExecutable public int lastIndexOf(Object o) { synchronized (lock) { int i = base.indexOf(o); diff --git a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java index 89f6bb47af5e..613b30f312df 100644 --- a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java +++ b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java @@ -37,6 +37,10 @@ import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; +import org.checkerframework.checker.modifiability.qual.Unmodifiable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -50,6 +54,7 @@ import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.framework.qual.AnnotatedFor; import org.checkerframework.framework.qual.DoesNotUnrefineReceiver; import java.util.AbstractSet; @@ -112,6 +117,7 @@ * @author Doug Lea * @param the type of elements held in this set */ +@AnnotatedFor({"nullness", "modifiability"}) public class CopyOnWriteArraySet extends AbstractSet implements java.io.Serializable { private static final long serialVersionUID = 5457747651344034263L; @@ -121,7 +127,7 @@ public class CopyOnWriteArraySet extends AbstractSet /** * Creates an empty set. */ - public CopyOnWriteArraySet() { + public @Modifiable CopyOnWriteArraySet() { al = new CopyOnWriteArrayList(); } @@ -132,7 +138,7 @@ public CopyOnWriteArraySet() { * @param c the collection of elements to initially contain * @throws NullPointerException if the specified collection is null */ - public CopyOnWriteArraySet(Collection c) { + public @Modifiable CopyOnWriteArraySet(Collection c) { if (c.getClass() == CopyOnWriteArraySet.class) { @SuppressWarnings("unchecked") CopyOnWriteArraySet cc = (CopyOnWriteArraySet)c; @@ -251,7 +257,7 @@ public boolean contains(@GuardSatisfied @Nullable @UnknownSignedness Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear() { + public void clear(@Shrinkable CopyOnWriteArraySet this) { al.clear(); } @@ -268,7 +274,7 @@ public void clear() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable CopyOnWriteArraySet this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return al.remove(o); } @@ -287,7 +293,7 @@ public boolean remove(@GuardSatisfied @Nullable @UnknownSignedness Object o) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable CopyOnWriteArraySet this, E e) { return al.addIfAbsent(e); } @@ -359,7 +365,7 @@ private static int compareSets(Object[] snapshot, Set set) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(Collection c) { + public boolean addAll(@Growable CopyOnWriteArraySet this, Collection c) { return al.addAllAbsent(c) > 0; } @@ -382,7 +388,7 @@ public boolean addAll(Collection c) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeAll(Collection c) { + public boolean removeAll(@Shrinkable CopyOnWriteArraySet this, Collection c) { return al.removeAll(c); } @@ -407,7 +413,7 @@ public boolean removeAll(Collection c) { + public boolean retainAll(@Shrinkable CopyOnWriteArraySet this, Collection c) { return al.retainAll(c); } @@ -423,7 +429,7 @@ public boolean retainAll(Collection iterator(@PolyGrowShrink @PolyNonEmpty CopyOnWriteArraySet this) { + public @Unmodifiable @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty CopyOnWriteArraySet this) { return al.iterator(); } @@ -456,7 +462,7 @@ public boolean equals(@Nullable Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(Predicate filter) { + public boolean removeIf(@Shrinkable CopyOnWriteArraySet this, Predicate filter) { return al.removeIf(filter); } diff --git a/src/java.base/share/classes/java/util/concurrent/DelayQueue.java b/src/java.base/share/classes/java/util/concurrent/DelayQueue.java index 59fdf458755e..bd9927c4dc70 100644 --- a/src/java.base/share/classes/java/util/concurrent/DelayQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/DelayQueue.java @@ -38,6 +38,11 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -115,7 +120,7 @@ * @author Doug Lea * @param the type of elements held in this queue */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public class DelayQueue extends AbstractQueue implements BlockingQueue { @@ -150,7 +155,7 @@ public class DelayQueue extends AbstractQueue /** * Creates a new {@code DelayQueue} that is initially empty. */ - public DelayQueue() {} + public @Modifiable @IteratorPolyMod DelayQueue() {} /** * Creates a {@code DelayQueue} initially containing the elements of the @@ -160,7 +165,7 @@ public DelayQueue() {} * @throws NullPointerException if the specified collection or any * of its elements are null */ - public DelayQueue(Collection c) { + public @Modifiable @IteratorPolyMod DelayQueue(Collection c) { this.addAll(c); } @@ -174,7 +179,7 @@ public DelayQueue(Collection c) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable DelayQueue this, E e) { return offer(e); } @@ -187,7 +192,7 @@ public boolean add(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable DelayQueue this, E e) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -211,7 +216,7 @@ public boolean offer(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void put(E e) { + public void put(@Growable DelayQueue this, E e) { offer(e); } @@ -227,7 +232,7 @@ public void put(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e, long timeout, TimeUnit unit) { + public boolean offer(@Growable DelayQueue this, E e, long timeout, TimeUnit unit) { return offer(e); } @@ -241,7 +246,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink DelayQueue this) { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink DelayQueue this) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -264,7 +269,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E take(@GuardSatisfied @CanShrink DelayQueue this) throws InterruptedException { + public E take(@Shrinkable @GuardSatisfied @CanShrink DelayQueue this) throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { @@ -311,7 +316,7 @@ public E take(@GuardSatisfied @CanShrink DelayQueue this) throws InterruptedE */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink DelayQueue this, long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink DelayQueue this, long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); @@ -363,7 +368,7 @@ public E take(@GuardSatisfied @CanShrink DelayQueue this) throws InterruptedE */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove() { + public E remove(@Shrinkable DelayQueue this) { return super.remove(); } @@ -406,7 +411,7 @@ public int size() { */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink DelayQueue this, Collection c) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink DelayQueue this, @Growable Collection c) { return drainTo(c, Integer.MAX_VALUE); } @@ -418,7 +423,7 @@ public int drainTo(@GuardSatisfied @CanShrink DelayQueue this, Collection this, Collection c, int maxElements) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink DelayQueue this, @Growable Collection c, int maxElements) { Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); @@ -450,7 +455,7 @@ public int drainTo(@GuardSatisfied @CanShrink DelayQueue this, Collection this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink DelayQueue this) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -545,7 +550,7 @@ public int remainingCapacity() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink DelayQueue this, @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink DelayQueue this, @UnknownSignedness Object o) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -584,7 +589,7 @@ void removeEQ(Object o) { * @return an iterator over the elements in this queue */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty DelayQueue this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty DelayQueue this) { return new Itr(toArray()); } diff --git a/src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java b/src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java index b45bccd403e8..2106046e82cd 100644 --- a/src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java +++ b/src/java.base/share/classes/java/util/concurrent/LinkedBlockingDeque.java @@ -38,6 +38,12 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.SeqGrowable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -93,7 +99,7 @@ * @author Doug Lea * @param the type of elements held in this deque */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public class LinkedBlockingDeque extends AbstractQueue implements BlockingDeque, java.io.Serializable { @@ -189,7 +195,7 @@ static final class Node { * Creates a {@code LinkedBlockingDeque} with a capacity of * {@link Integer#MAX_VALUE}. */ - public LinkedBlockingDeque() { + public @Modifiable @IteratorPolyMod LinkedBlockingDeque() { this(Integer.MAX_VALUE); } @@ -199,7 +205,7 @@ public LinkedBlockingDeque() { * @param capacity the capacity of this deque * @throws IllegalArgumentException if {@code capacity} is less than 1 */ - public LinkedBlockingDeque(int capacity) { + public @Modifiable @IteratorPolyMod LinkedBlockingDeque(int capacity) { if (capacity <= 0) throw new IllegalArgumentException(); this.capacity = capacity; } @@ -214,7 +220,7 @@ public LinkedBlockingDeque(int capacity) { * @throws NullPointerException if the specified collection or any * of its elements are null */ - public LinkedBlockingDeque(Collection c) { + public @Modifiable @IteratorPolyMod LinkedBlockingDeque(Collection c) { this(Integer.MAX_VALUE); addAll(c); } @@ -336,7 +342,7 @@ void unlink(@CanShrink LinkedBlockingDeque this, Node x) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addFirst(E e) { + public void addFirst(@SeqGrowable LinkedBlockingDeque this, E e) { if (!offerFirst(e)) throw new IllegalStateException("Deque full"); } @@ -348,7 +354,7 @@ public void addFirst(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void addLast(E e) { + public void addLast(@SeqGrowable LinkedBlockingDeque this, E e) { if (!offerLast(e)) throw new IllegalStateException("Deque full"); } @@ -358,7 +364,7 @@ public void addLast(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerFirst(E e) { + public boolean offerFirst(@SeqGrowable LinkedBlockingDeque this, E e) { if (e == null) throw new NullPointerException(); Node node = new Node(e); final ReentrantLock lock = this.lock; @@ -375,7 +381,7 @@ public boolean offerFirst(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerLast(E e) { + public boolean offerLast(@SeqGrowable LinkedBlockingDeque this, E e) { if (e == null) throw new NullPointerException(); Node node = new Node(e); final ReentrantLock lock = this.lock; @@ -393,7 +399,7 @@ public boolean offerLast(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putFirst(E e) throws InterruptedException { + public void putFirst(@SeqGrowable LinkedBlockingDeque this, E e) throws InterruptedException { if (e == null) throw new NullPointerException(); Node node = new Node(e); final ReentrantLock lock = this.lock; @@ -412,7 +418,7 @@ public void putFirst(E e) throws InterruptedException { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void putLast(E e) throws InterruptedException { + public void putLast(@SeqGrowable LinkedBlockingDeque this, E e) throws InterruptedException { if (e == null) throw new NullPointerException(); Node node = new Node(e); final ReentrantLock lock = this.lock; @@ -431,8 +437,7 @@ public void putLast(E e) throws InterruptedException { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerFirst(E e, long timeout, TimeUnit unit) - throws InterruptedException { + public boolean offerFirst(@SeqGrowable LinkedBlockingDeque this, E e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) throw new NullPointerException(); Node node = new Node(e); long nanos = unit.toNanos(timeout); @@ -456,8 +461,7 @@ public boolean offerFirst(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offerLast(E e, long timeout, TimeUnit unit) - throws InterruptedException { + public boolean offerLast(@SeqGrowable LinkedBlockingDeque this, E e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) throw new NullPointerException(); Node node = new Node(e); long nanos = unit.toNanos(timeout); @@ -480,7 +484,7 @@ public boolean offerLast(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeFirst(@GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque this) { + public E removeFirst(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque this) { E x = pollFirst(); if (x == null) throw new NoSuchElementException(); return x; @@ -491,7 +495,7 @@ public E removeFirst(@GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E removeLast(@GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque this) { + public E removeLast(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque this) { E x = pollLast(); if (x == null) throw new NoSuchElementException(); return x; @@ -499,7 +503,7 @@ public E removeLast(@GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollFirst(@GuardSatisfied @CanShrink LinkedBlockingDeque this) { + public @Nullable E pollFirst(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -511,7 +515,7 @@ public E removeLast(@GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollLast(@GuardSatisfied @CanShrink LinkedBlockingDeque this) { + public @Nullable E pollLast(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -523,7 +527,7 @@ public E removeLast(@GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E takeFirst(@GuardSatisfied @CanShrink LinkedBlockingDeque this) throws InterruptedException { + public E takeFirst(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this) throws InterruptedException { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -538,7 +542,7 @@ public E takeFirst(@GuardSatisfied @CanShrink LinkedBlockingDeque this) throw @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E takeLast(@GuardSatisfied @CanShrink LinkedBlockingDeque this) throws InterruptedException { + public E takeLast(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this) throws InterruptedException { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -553,8 +557,7 @@ public E takeLast(@GuardSatisfied @CanShrink LinkedBlockingDeque this) throws @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollFirst(@GuardSatisfied @CanShrink LinkedBlockingDeque this, long timeout, TimeUnit unit) - throws InterruptedException { + public @Nullable E pollFirst(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this, long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); @@ -573,8 +576,7 @@ public E takeLast(@GuardSatisfied @CanShrink LinkedBlockingDeque this) throws @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E pollLast(@GuardSatisfied @CanShrink LinkedBlockingDeque this, long timeout, TimeUnit unit) - throws InterruptedException { + public @Nullable E pollLast(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this, long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); @@ -637,7 +639,7 @@ public E getLast(@NonEmpty LinkedBlockingDeque this) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeFirstOccurrence(@CanShrink LinkedBlockingDeque this, Object o) { + public boolean removeFirstOccurrence(@Shrinkable @CanShrink LinkedBlockingDeque this, Object o) { if (o == null) return false; final ReentrantLock lock = this.lock; lock.lock(); @@ -656,7 +658,7 @@ public boolean removeFirstOccurrence(@CanShrink LinkedBlockingDeque this, Obj @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeLastOccurrence(@CanShrink LinkedBlockingDeque this, Object o) { + public boolean removeLastOccurrence(@Shrinkable @CanShrink LinkedBlockingDeque this, Object o) { if (o == null) return false; final ReentrantLock lock = this.lock; lock.lock(); @@ -688,7 +690,7 @@ public boolean removeLastOccurrence(@CanShrink LinkedBlockingDeque this, Obje @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable LinkedBlockingDeque this, E e) { addLast(e); return true; } @@ -698,7 +700,7 @@ public boolean add(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable LinkedBlockingDeque this, E e) { return offerLast(e); } @@ -708,7 +710,7 @@ public boolean offer(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void put(E e) throws InterruptedException { + public void put(@Growable LinkedBlockingDeque this, E e) throws InterruptedException { putLast(e); } @@ -718,8 +720,7 @@ public void put(E e) throws InterruptedException { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e, long timeout, TimeUnit unit) - throws InterruptedException { + public boolean offer(@Growable LinkedBlockingDeque this, E e, long timeout, TimeUnit unit) throws InterruptedException { return offerLast(e, timeout, unit); } @@ -735,25 +736,25 @@ public boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E remove(@GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque this) { + public E remove(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque this) { return removeFirst(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink LinkedBlockingDeque this) { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this) { return pollFirst(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E take(@GuardSatisfied @CanShrink LinkedBlockingDeque this) throws InterruptedException { + public E take(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this) throws InterruptedException { return takeFirst(); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink LinkedBlockingDeque this, long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this, long timeout, TimeUnit unit) throws InterruptedException { return pollFirst(timeout, unit); } @@ -806,7 +807,7 @@ public int remainingCapacity() { */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink LinkedBlockingDeque this, Collection c) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this, @Growable Collection c) { return drainTo(c, Integer.MAX_VALUE); } @@ -818,7 +819,7 @@ public int drainTo(@GuardSatisfied @CanShrink LinkedBlockingDeque this, Colle */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink LinkedBlockingDeque this, Collection c, int maxElements) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this, @Growable Collection c, int maxElements) { Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); @@ -846,7 +847,7 @@ public int drainTo(@GuardSatisfied @CanShrink LinkedBlockingDeque this, Colle */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void push(E e) { + public void push(@SeqGrowable LinkedBlockingDeque this, E e) { addFirst(e); } @@ -855,7 +856,7 @@ public void push(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E pop(@GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque this) { + public E pop(@Shrinkable @GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque this) { return removeFirst(); } @@ -877,7 +878,7 @@ public E pop(@GuardSatisfied @NonEmpty @CanShrink LinkedBlockingDeque this) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink LinkedBlockingDeque this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink LinkedBlockingDeque this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return removeFirstOccurrence(o); } @@ -937,7 +938,7 @@ public boolean contains(@GuardSatisfied @Nullable @UnknownSignedness Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean addAll(Collection c) { + public boolean addAll(@Growable LinkedBlockingDeque this, Collection c) { if (c == this) // As historically specified in AbstractQueue#addAll throw new IllegalArgumentException(); @@ -1078,7 +1079,7 @@ public String toString() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink LinkedBlockingDeque this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -1119,7 +1120,7 @@ Node succ(Node p) { * @return an iterator over the elements in this deque in proper sequence */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty LinkedBlockingDeque this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty LinkedBlockingDeque this) { return new Itr(); } @@ -1134,7 +1135,7 @@ Node succ(Node p) { * @return an iterator over the elements in this deque in reverse order */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyNonEmpty LinkedBlockingDeque this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator descendingIterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty LinkedBlockingDeque this) { return new DescendingItr(); } @@ -1442,7 +1443,7 @@ void forEachFrom(Consumer action, Node p) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink LinkedBlockingDeque this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink LinkedBlockingDeque this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -1452,7 +1453,7 @@ public boolean removeIf(@CanShrink LinkedBlockingDeque this, Predicate this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink LinkedBlockingDeque this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); } @@ -1462,7 +1463,7 @@ public boolean removeAll(@CanShrink LinkedBlockingDeque this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingDeque this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } diff --git a/src/java.base/share/classes/java/util/concurrent/LinkedBlockingQueue.java b/src/java.base/share/classes/java/util/concurrent/LinkedBlockingQueue.java index 3864a0e36baa..1744b0885f81 100644 --- a/src/java.base/share/classes/java/util/concurrent/LinkedBlockingQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/LinkedBlockingQueue.java @@ -38,6 +38,11 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; @@ -95,7 +100,7 @@ * @author Doug Lea * @param the type of elements held in this queue */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public class LinkedBlockingQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { private static final long serialVersionUID = -6903933977591709194L; @@ -259,7 +264,7 @@ void fullyUnlock() { * Creates a {@code LinkedBlockingQueue} with a capacity of * {@link Integer#MAX_VALUE}. */ - public LinkedBlockingQueue() { + public @Modifiable @IteratorPolyMod LinkedBlockingQueue() { this(Integer.MAX_VALUE); } @@ -270,7 +275,7 @@ public LinkedBlockingQueue() { * @throws IllegalArgumentException if {@code capacity} is not greater * than zero */ - public LinkedBlockingQueue(int capacity) { + public @Modifiable @IteratorPolyMod LinkedBlockingQueue(int capacity) { if (capacity <= 0) throw new IllegalArgumentException(); this.capacity = capacity; last = head = new Node(null); @@ -286,7 +291,7 @@ public LinkedBlockingQueue(int capacity) { * @throws NullPointerException if the specified collection or any * of its elements are null */ - public LinkedBlockingQueue(Collection c) { + public @Modifiable @IteratorPolyMod LinkedBlockingQueue(Collection c) { this(Integer.MAX_VALUE); final ReentrantLock putLock = this.putLock; putLock.lock(); // Never contended, but necessary for visibility @@ -344,7 +349,7 @@ public int remainingCapacity() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void put(E e) throws InterruptedException { + public void put(@Growable LinkedBlockingQueue this, E e) throws InterruptedException { if (e == null) throw new NullPointerException(); final int c; final Node node = new Node(e); @@ -385,8 +390,7 @@ public void put(E e) throws InterruptedException { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e, long timeout, TimeUnit unit) - throws InterruptedException { + public boolean offer(@Growable LinkedBlockingQueue this, E e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) throw new NullPointerException(); long nanos = unit.toNanos(timeout); @@ -425,7 +429,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable LinkedBlockingQueue this, E e) { if (e == null) throw new NullPointerException(); final AtomicInteger count = this.count; if (count.get() == capacity) @@ -451,7 +455,7 @@ public boolean offer(E e) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E take(@GuardSatisfied @CanShrink LinkedBlockingQueue this) throws InterruptedException { + public E take(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingQueue this) throws InterruptedException { final E x; final int c; final AtomicInteger count = this.count; @@ -475,7 +479,7 @@ public E take(@GuardSatisfied @CanShrink LinkedBlockingQueue this) throws Int @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink LinkedBlockingQueue this, long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingQueue this, long timeout, TimeUnit unit) throws InterruptedException { final E x; final int c; long nanos = unit.toNanos(timeout); @@ -502,7 +506,7 @@ public E take(@GuardSatisfied @CanShrink LinkedBlockingQueue this) throws Int @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink LinkedBlockingQueue this) { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingQueue this) { final AtomicInteger count = this.count; if (count.get() == 0) return null; @@ -568,7 +572,7 @@ void unlink(Node p, Node pred) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink LinkedBlockingQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink LinkedBlockingQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { if (o == null) return false; fullyLock(); try { @@ -703,7 +707,7 @@ public String toString() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink LinkedBlockingQueue this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingQueue this) { fullyLock(); try { for (Node p, h = head; (p = h.next) != null; h = p) { @@ -727,7 +731,7 @@ public void clear(@GuardSatisfied @CanShrink LinkedBlockingQueue this) { */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink LinkedBlockingQueue this, Collection c) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingQueue this, @Growable Collection c) { return drainTo(c, Integer.MAX_VALUE); } @@ -739,7 +743,7 @@ public int drainTo(@GuardSatisfied @CanShrink LinkedBlockingQueue this, Colle */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink LinkedBlockingQueue this, Collection c, int maxElements) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingQueue this, @Growable Collection c, int maxElements) { Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); @@ -800,7 +804,7 @@ Node succ(Node p) { * @return an iterator over the elements in this queue in proper sequence */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty LinkedBlockingQueue this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty LinkedBlockingQueue this) { return new Itr(); } @@ -1071,7 +1075,7 @@ void forEachFrom(Consumer action, Node p) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink LinkedBlockingQueue this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink LinkedBlockingQueue this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -1081,7 +1085,7 @@ public boolean removeIf(@CanShrink LinkedBlockingQueue this, Predicate this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink LinkedBlockingQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); } @@ -1091,7 +1095,7 @@ public boolean removeAll(@CanShrink LinkedBlockingQueue this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink LinkedBlockingQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } diff --git a/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java b/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java index 311b7d9ef04f..508a014eb642 100644 --- a/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java @@ -38,6 +38,11 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -50,6 +55,7 @@ import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.SideEffectFree; import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.framework.qual.AnnotatedFor; import org.checkerframework.framework.qual.DoesNotUnrefineReceiver; import java.lang.invoke.MethodHandles; @@ -105,6 +111,7 @@ * @author Doug Lea * @param the type of elements held in this queue */ +@AnnotatedFor({"nullness", "modifiability"}) public class LinkedTransferQueue extends AbstractQueue implements TransferQueue, java.io.Serializable { private static final long serialVersionUID = -3223113410248163686L; @@ -1176,7 +1183,7 @@ else if (s == n) // stale /** * Creates an initially empty {@code LinkedTransferQueue}. */ - public LinkedTransferQueue() { + public @Modifiable @IteratorPolyMod LinkedTransferQueue() { head = tail = new Node(); } @@ -1189,7 +1196,7 @@ public LinkedTransferQueue() { * @throws NullPointerException if the specified collection or any * of its elements are null */ - public LinkedTransferQueue(Collection c) { + public @Modifiable @IteratorPolyMod LinkedTransferQueue(Collection c) { Node h = null, t = null; for (E e : c) { Node newNode = new Node(Objects.requireNonNull(e)); @@ -1212,7 +1219,7 @@ public LinkedTransferQueue(Collection c) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void put(E e) { + public void put(@Growable LinkedTransferQueue this, E e) { xfer(e, true, ASYNC, 0L); } @@ -1227,7 +1234,7 @@ public void put(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e, long timeout, TimeUnit unit) { + public boolean offer(@Growable LinkedTransferQueue this, E e, long timeout, TimeUnit unit) { xfer(e, true, ASYNC, 0L); return true; } @@ -1241,7 +1248,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable LinkedTransferQueue this, E e) { xfer(e, true, ASYNC, 0L); return true; } @@ -1257,7 +1264,7 @@ public boolean offer(E e) { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable LinkedTransferQueue this, E e) { xfer(e, true, ASYNC, 0L); return true; } @@ -1274,7 +1281,7 @@ public boolean add(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean tryTransfer(@GuardSatisfied @CanShrink LinkedTransferQueue this, E e) { + public boolean tryTransfer(@GuardSatisfied @Growable @CanShrink LinkedTransferQueue this, E e) { return xfer(e, true, NOW, 0L) == null; } @@ -1291,7 +1298,7 @@ public boolean tryTransfer(@GuardSatisfied @CanShrink LinkedTransferQueue thi */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void transfer(@GuardSatisfied @CanShrink LinkedTransferQueue this, E e) throws InterruptedException { + public void transfer(@GuardSatisfied @Growable @CanShrink LinkedTransferQueue this, E e) throws InterruptedException { if (xfer(e, true, SYNC, 0L) != null) { Thread.interrupted(); // failure possible only due to interrupt throw new InterruptedException(); @@ -1314,7 +1321,7 @@ public void transfer(@GuardSatisfied @CanShrink LinkedTransferQueue this, E e */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean tryTransfer(@GuardSatisfied @CanShrink LinkedTransferQueue this, E e, long timeout, TimeUnit unit) + public boolean tryTransfer(@GuardSatisfied @Growable @CanShrink LinkedTransferQueue this, E e, long timeout, TimeUnit unit) throws InterruptedException { if (xfer(e, true, TIMED, unit.toNanos(timeout)) == null) return true; @@ -1325,7 +1332,7 @@ public boolean tryTransfer(@GuardSatisfied @CanShrink LinkedTransferQueue thi @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E take(@GuardSatisfied @CanShrink LinkedTransferQueue this) throws InterruptedException { + public E take(@GuardSatisfied @Shrinkable @CanShrink LinkedTransferQueue this) throws InterruptedException { E e = xfer(null, false, SYNC, 0L); if (e != null) return e; @@ -1335,7 +1342,7 @@ public E take(@GuardSatisfied @CanShrink LinkedTransferQueue this) throws Int @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink LinkedTransferQueue this, long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(@GuardSatisfied @Shrinkable @CanShrink LinkedTransferQueue this, long timeout, TimeUnit unit) throws InterruptedException { E e = xfer(null, false, TIMED, unit.toNanos(timeout)); if (e != null || !Thread.interrupted()) return e; @@ -1344,7 +1351,7 @@ public E take(@GuardSatisfied @CanShrink LinkedTransferQueue this) throws Int @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink LinkedTransferQueue this) { + public @Nullable E poll(@GuardSatisfied @Shrinkable @CanShrink LinkedTransferQueue this) { return xfer(null, false, NOW, 0L); } @@ -1354,7 +1361,7 @@ public E take(@GuardSatisfied @CanShrink LinkedTransferQueue this) throws Int */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink LinkedTransferQueue this, Collection c) { + public int drainTo(@GuardSatisfied @Shrinkable @CanShrink LinkedTransferQueue this, @Growable Collection c) { Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); @@ -1370,7 +1377,7 @@ public int drainTo(@GuardSatisfied @CanShrink LinkedTransferQueue this, Colle */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink LinkedTransferQueue this, Collection c, int maxElements) { + public int drainTo(@GuardSatisfied @Shrinkable @CanShrink LinkedTransferQueue this, @Growable Collection c, int maxElements) { Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); @@ -1390,7 +1397,7 @@ public int drainTo(@GuardSatisfied @CanShrink LinkedTransferQueue this, Colle * @return an iterator over the elements in this queue in proper sequence */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty LinkedTransferQueue this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty LinkedTransferQueue this) { return new Itr(); } @@ -1476,7 +1483,7 @@ public int getWaitingConsumerCount() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink LinkedTransferQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink LinkedTransferQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { if (o == null) return false; restartFromHead: for (;;) { for (Node p = head, pred = null; p != null; ) { @@ -1598,7 +1605,7 @@ private void readObject(java.io.ObjectInputStream s) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink LinkedTransferQueue this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink LinkedTransferQueue this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -1608,7 +1615,7 @@ public boolean removeIf(@CanShrink LinkedTransferQueue this, Predicate this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink LinkedTransferQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); } @@ -1618,14 +1625,14 @@ public boolean removeAll(@CanShrink LinkedTransferQueue this, Collection this, Collection c) { + public boolean retainAll(@GuardSatisfied @Shrinkable @CanShrink LinkedTransferQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink LinkedTransferQueue this) { + public void clear(@GuardSatisfied @Shrinkable @CanShrink LinkedTransferQueue this) { bulkRemove(e -> true); } diff --git a/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java b/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java index b6f92502a465..e2a49e429f1e 100644 --- a/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java @@ -38,6 +38,11 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.IteratorPolyMod; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmpty; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.NonEmpty; @@ -129,7 +134,7 @@ * @author Doug Lea * @param the type of elements held in this queue */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) @SuppressWarnings("unchecked") public class PriorityBlockingQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { @@ -205,7 +210,7 @@ public class PriorityBlockingQueue extends AbstractQueue * initial capacity (11) that orders its elements according to * their {@linkplain Comparable natural ordering}. */ - public PriorityBlockingQueue() { + public @Modifiable @IteratorPolyMod PriorityBlockingQueue() { this(DEFAULT_INITIAL_CAPACITY, null); } @@ -218,7 +223,7 @@ public PriorityBlockingQueue() { * @throws IllegalArgumentException if {@code initialCapacity} is less * than 1 */ - public PriorityBlockingQueue(int initialCapacity) { + public @Modifiable @IteratorPolyMod PriorityBlockingQueue(int initialCapacity) { this(initialCapacity, null); } @@ -234,7 +239,7 @@ public PriorityBlockingQueue(int initialCapacity) { * @throws IllegalArgumentException if {@code initialCapacity} is less * than 1 */ - public PriorityBlockingQueue(int initialCapacity, + public @Modifiable @IteratorPolyMod PriorityBlockingQueue(int initialCapacity, @Nullable Comparator comparator) { if (initialCapacity < 1) throw new IllegalArgumentException(); @@ -258,7 +263,7 @@ public PriorityBlockingQueue(int initialCapacity, * @throws NullPointerException if the specified collection or any * of its elements are null */ - public PriorityBlockingQueue(Collection c) { + public @Modifiable @IteratorPolyMod PriorityBlockingQueue(Collection c) { boolean heapify = true; // true if not known to be in heap order boolean screen = true; // true if must screen for nulls if (c instanceof SortedSet) { @@ -467,7 +472,7 @@ private void heapify() { @EnsuresNonEmpty("this") @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean add(E e) { + public boolean add(@Growable PriorityBlockingQueue this, E e) { return offer(e); } @@ -484,7 +489,7 @@ public boolean add(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable PriorityBlockingQueue this, E e) { if (e == null) throw new NullPointerException(); final ReentrantLock lock = this.lock; @@ -519,7 +524,7 @@ public boolean offer(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void put(E e) { + public void put(@Growable PriorityBlockingQueue this, E e) { offer(e); // never need to block } @@ -540,13 +545,13 @@ public void put(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e, long timeout, TimeUnit unit) { + public boolean offer(@Growable PriorityBlockingQueue this, E e, long timeout, TimeUnit unit) { return offer(e); // never need to block } @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink PriorityBlockingQueue this) { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink PriorityBlockingQueue this) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -558,7 +563,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) { @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E take(@GuardSatisfied @CanShrink PriorityBlockingQueue this) throws InterruptedException { + public E take(@Shrinkable @GuardSatisfied @CanShrink PriorityBlockingQueue this) throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); E result; @@ -573,7 +578,7 @@ public E take(@GuardSatisfied @CanShrink PriorityBlockingQueue this) throws I @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink PriorityBlockingQueue this, long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(@Shrinkable @GuardSatisfied @CanShrink PriorityBlockingQueue this, long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); @@ -682,7 +687,7 @@ private void removeAt(int i) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink PriorityBlockingQueue this, @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink PriorityBlockingQueue this, @Nullable @UnknownSignedness Object o) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -750,7 +755,7 @@ public String toString() { */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink PriorityBlockingQueue this, Collection c) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink PriorityBlockingQueue this, @Growable Collection c) { return drainTo(c, Integer.MAX_VALUE); } @@ -762,7 +767,7 @@ public int drainTo(@GuardSatisfied @CanShrink PriorityBlockingQueue this, Col */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink PriorityBlockingQueue this, Collection c, int maxElements) { + public int drainTo(@Shrinkable @GuardSatisfied @CanShrink PriorityBlockingQueue this, @Growable Collection c, int maxElements) { Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); @@ -788,7 +793,7 @@ public int drainTo(@GuardSatisfied @CanShrink PriorityBlockingQueue this, Col */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink PriorityBlockingQueue this) { + public void clear(@Shrinkable @GuardSatisfied @CanShrink PriorityBlockingQueue this) { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -888,7 +893,7 @@ public void clear(@GuardSatisfied @CanShrink PriorityBlockingQueue this) { * @return an iterator over the elements in this queue */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty PriorityBlockingQueue this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty PriorityBlockingQueue this) { return new Itr(toArray()); } @@ -1067,7 +1072,7 @@ public Spliterator spliterator() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeIf(@CanShrink PriorityBlockingQueue this, Predicate filter) { + public boolean removeIf(@Shrinkable @CanShrink PriorityBlockingQueue this, Predicate filter) { Objects.requireNonNull(filter); return bulkRemove(filter); } @@ -1077,7 +1082,7 @@ public boolean removeIf(@CanShrink PriorityBlockingQueue this, Predicate this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink PriorityBlockingQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> c.contains(e)); } @@ -1087,7 +1092,7 @@ public boolean removeAll(@CanShrink PriorityBlockingQueue this, Collection this, Collection c) { + public boolean retainAll(@Shrinkable @GuardSatisfied @CanShrink PriorityBlockingQueue this, Collection c) { Objects.requireNonNull(c); return bulkRemove(e -> !c.contains(e)); } diff --git a/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java b/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java index f377f37b0864..8ea240e7bf86 100644 --- a/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java @@ -39,6 +39,10 @@ import org.checkerframework.checker.index.qual.CanShrink; import org.checkerframework.checker.index.qual.PolyGrowShrink; import org.checkerframework.checker.lock.qual.GuardSatisfied; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.checker.modifiability.qual.PolyModifiable; +import org.checkerframework.checker.modifiability.qual.Shrinkable; import org.checkerframework.checker.nonempty.qual.EnsuresNonEmptyIf; import org.checkerframework.checker.nonempty.qual.PolyNonEmpty; import org.checkerframework.checker.nullness.qual.NonNull; @@ -103,7 +107,7 @@ * @author Doug Lea and Bill Scherer and Michael Scott * @param the type of elements held in this queue */ -@AnnotatedFor({"nullness"}) +@AnnotatedFor({"nullness", "modifiability"}) public class SynchronousQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { private static final long serialVersionUID = -3223113410248163686L; @@ -830,7 +834,7 @@ void clean(QNode pred, QNode s) { /** * Creates a {@code SynchronousQueue} with nonfair access policy. */ - public SynchronousQueue() { + public @Modifiable SynchronousQueue() { this(false); } @@ -840,7 +844,7 @@ public SynchronousQueue() { * @param fair if true, waiting threads contend in FIFO order for * access; otherwise the order is unspecified. */ - public SynchronousQueue(boolean fair) { + public @Modifiable SynchronousQueue(boolean fair) { transferer = fair ? new TransferQueue() : new TransferStack(); } @@ -853,7 +857,7 @@ public SynchronousQueue(boolean fair) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void put(E e) throws InterruptedException { + public void put(@Growable SynchronousQueue this, E e) throws InterruptedException { if (e == null) throw new NullPointerException(); if (transferer.transfer(e, false, 0) == null) { Thread.interrupted(); @@ -872,7 +876,7 @@ public void put(E e) throws InterruptedException { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e, long timeout, TimeUnit unit) + public boolean offer(@Growable SynchronousQueue this, E e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) throw new NullPointerException(); if (transferer.transfer(e, true, unit.toNanos(timeout)) != null) @@ -893,7 +897,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean offer(E e) { + public boolean offer(@Growable SynchronousQueue this, E e) { if (e == null) throw new NullPointerException(); return transferer.transfer(e, true, 0) != null; } @@ -907,7 +911,7 @@ public boolean offer(E e) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public E take(@GuardSatisfied @CanShrink SynchronousQueue this) throws InterruptedException { + public E take(@GuardSatisfied @Shrinkable @CanShrink SynchronousQueue this) throws InterruptedException { E e = transferer.transfer(null, false, 0); if (e != null) return e; @@ -926,7 +930,7 @@ public E take(@GuardSatisfied @CanShrink SynchronousQueue this) throws Interr */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink SynchronousQueue this, long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(@GuardSatisfied @Shrinkable @CanShrink SynchronousQueue this, long timeout, TimeUnit unit) throws InterruptedException { E e = transferer.transfer(null, true, unit.toNanos(timeout)); if (e != null || !Thread.interrupted()) return e; @@ -942,7 +946,7 @@ public E take(@GuardSatisfied @CanShrink SynchronousQueue this) throws Interr */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public @Nullable E poll(@GuardSatisfied @CanShrink SynchronousQueue this) { + public @Nullable E poll(@GuardSatisfied @Shrinkable @CanShrink SynchronousQueue this) { return transferer.transfer(null, true, 0); } @@ -985,7 +989,7 @@ public int remainingCapacity() { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public void clear(@GuardSatisfied @CanShrink SynchronousQueue this) { + public void clear(@GuardSatisfied @Shrinkable @CanShrink SynchronousQueue this) { } /** @@ -1010,7 +1014,7 @@ public boolean contains(@GuardSatisfied @Nullable @UnknownSignedness Object o) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean remove(@CanShrink SynchronousQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { + public boolean remove(@Shrinkable @CanShrink SynchronousQueue this, @GuardSatisfied @Nullable @UnknownSignedness Object o) { return false; } @@ -1035,7 +1039,7 @@ public boolean containsAll(Collection c) { */ @SideEffectsOnly("this") @DoesNotUnrefineReceiver("modifiability") - public boolean removeAll(@CanShrink SynchronousQueue this, Collection c) { + public boolean removeAll(@Shrinkable @CanShrink SynchronousQueue this, Collection c) { return false; } @@ -1048,7 +1052,7 @@ public boolean removeAll(@CanShrink SynchronousQueue this, Collection this, Collection c) { + public boolean retainAll(@GuardSatisfied @Shrinkable @CanShrink SynchronousQueue this, Collection c) { return false; } @@ -1071,7 +1075,7 @@ public boolean retainAll(@GuardSatisfied @CanShrink SynchronousQueue this, Co * @return an empty iterator */ @SideEffectFree - public @PolyGrowShrink @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyNonEmpty SynchronousQueue this) { + public @PolyGrowShrink @PolyModifiable @PolyNonEmpty Iterator iterator(@PolyGrowShrink @PolyModifiable @PolyNonEmpty SynchronousQueue this) { return Collections.emptyIterator(); } @@ -1127,7 +1131,7 @@ public String toString() { */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink SynchronousQueue this, Collection c) { + public int drainTo(@GuardSatisfied @Shrinkable @CanShrink SynchronousQueue this, @Growable Collection c) { Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); @@ -1145,7 +1149,7 @@ public int drainTo(@GuardSatisfied @CanShrink SynchronousQueue this, Collecti */ @SideEffectsOnly({"this", "#1"}) @DoesNotUnrefineReceiver("modifiability") - public int drainTo(@GuardSatisfied @CanShrink SynchronousQueue this, Collection c, int maxElements) { + public int drainTo(@GuardSatisfied @Shrinkable @CanShrink SynchronousQueue this, @Growable Collection c, int maxElements) { Objects.requireNonNull(c); if (c == this) throw new IllegalArgumentException(); diff --git a/src/java.base/share/classes/java/util/concurrent/TransferQueue.java b/src/java.base/share/classes/java/util/concurrent/TransferQueue.java index 72a4108e61b8..44db151f8bc9 100644 --- a/src/java.base/share/classes/java/util/concurrent/TransferQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/TransferQueue.java @@ -35,6 +35,10 @@ package java.util.concurrent; +import org.checkerframework.checker.modifiability.qual.Growable; +import org.checkerframework.checker.modifiability.qual.Modifiable; +import org.checkerframework.framework.qual.AnnotatedFor; + /** * A {@link BlockingQueue} in which producers may wait for consumers * to receive elements. A {@code TransferQueue} may be useful for @@ -65,6 +69,7 @@ * @author Doug Lea * @param the type of elements held in this queue */ +@AnnotatedFor({"nullness", "modifiability"}) public interface TransferQueue extends BlockingQueue { /** * Transfers the element to a waiting consumer immediately, if possible. @@ -83,7 +88,7 @@ public interface TransferQueue extends BlockingQueue { * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this queue */ - boolean tryTransfer(E e); + boolean tryTransfer(@Growable TransferQueue this, E e); /** * Transfers the element to a consumer, waiting if necessary to do so. @@ -102,7 +107,7 @@ public interface TransferQueue extends BlockingQueue { * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this queue */ - void transfer(E e) throws InterruptedException; + void transfer(@Growable TransferQueue this, E e) throws InterruptedException; /** * Transfers the element to a consumer if it is possible to do so @@ -131,7 +136,7 @@ public interface TransferQueue extends BlockingQueue { * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this queue */ - boolean tryTransfer(E e, long timeout, TimeUnit unit) + boolean tryTransfer(@Growable TransferQueue this, E e, long timeout, TimeUnit unit) throws InterruptedException; /** diff --git a/src/java.base/share/classes/java/util/stream/DoublePipeline.java b/src/java.base/share/classes/java/util/stream/DoublePipeline.java index 9771440f8aa9..7b388c30bbb4 100644 --- a/src/java.base/share/classes/java/util/stream/DoublePipeline.java +++ b/src/java.base/share/classes/java/util/stream/DoublePipeline.java @@ -24,6 +24,7 @@ */ package java.util.stream; +import org.checkerframework.dataflow.qual.SideEffectFree; import java.util.DoubleSummaryStatistics; import java.util.Objects; import java.util.OptionalDouble; @@ -567,6 +568,7 @@ public final OptionalDouble findAny() { } @Override + @SideEffectFree public final double[] toArray() { return Nodes.flattenDouble((Node.OfDouble) evaluateToArrayNode(Double[]::new)) .asPrimitiveArray(); diff --git a/src/java.base/share/classes/java/util/stream/IntPipeline.java b/src/java.base/share/classes/java/util/stream/IntPipeline.java index f0d019c7d74a..41a93b7485ce 100644 --- a/src/java.base/share/classes/java/util/stream/IntPipeline.java +++ b/src/java.base/share/classes/java/util/stream/IntPipeline.java @@ -24,6 +24,7 @@ */ package java.util.stream; +import org.checkerframework.dataflow.qual.SideEffectFree; import java.util.IntSummaryStatistics; import java.util.Objects; import java.util.OptionalDouble; @@ -558,6 +559,7 @@ public final OptionalInt findAny() { } @Override + @SideEffectFree public final int[] toArray() { return Nodes.flattenInt((Node.OfInt) evaluateToArrayNode(Integer[]::new)) .asPrimitiveArray(); diff --git a/src/java.base/share/classes/java/util/stream/LongPipeline.java b/src/java.base/share/classes/java/util/stream/LongPipeline.java index 227a071100af..9dc393080759 100644 --- a/src/java.base/share/classes/java/util/stream/LongPipeline.java +++ b/src/java.base/share/classes/java/util/stream/LongPipeline.java @@ -24,6 +24,7 @@ */ package java.util.stream; +import org.checkerframework.dataflow.qual.SideEffectFree; import java.util.LongSummaryStatistics; import java.util.Objects; import java.util.OptionalDouble; @@ -541,6 +542,7 @@ public final OptionalLong findAny() { } @Override + @SideEffectFree public final long[] toArray() { return Nodes.flattenLong((Node.OfLong) evaluateToArrayNode(Long[]::new)) .asPrimitiveArray(); diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java b/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java index c4601ef3e6cc..552ce55f05b2 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java @@ -25,6 +25,7 @@ package jdk.internal.jimage; +import org.checkerframework.dataflow.qual.Pure; import java.io.UTFDataFormatException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -62,18 +63,22 @@ public int add(final String string) { throw new InternalError("Can not add strings at runtime"); } + @Pure public static int hashCode(String s) { return hashCode(s, HASH_MULTIPLIER); } + @Pure public static int hashCode(String s, int seed) { return unmaskedHashCode(s, seed) & POSITIVE_MASK; } + @Pure public static int hashCode(String module, String name) { return hashCode(module, name, HASH_MULTIPLIER); } + @Pure public static int hashCode(String module, String name, int seed) { seed = (seed * HASH_MULTIPLIER) ^ ('/'); seed = unmaskedHashCode(module, seed); diff --git a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java index 61be28519fc2..0a8deadac002 100644 --- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java +++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java @@ -24,6 +24,7 @@ */ package jdk.internal.util; +import org.checkerframework.dataflow.qual.Pure; import java.util.Arrays; import java.util.Collection; import jdk.internal.access.JavaLangAccess; @@ -219,6 +220,7 @@ private static int signedHashCode(int result, byte[] a, int fromIndex, int lengt return result; } + @Pure private static int hashCode(int result, byte[] a, int fromIndex, int length) { int end = fromIndex + length; for (int i = fromIndex; i < end; i++) { @@ -227,6 +229,7 @@ private static int hashCode(int result, byte[] a, int fromIndex, int length) { return result; } + @Pure private static int hashCode(int result, char[] a, int fromIndex, int length) { int end = fromIndex + length; for (int i = fromIndex; i < end; i++) { @@ -235,6 +238,7 @@ private static int hashCode(int result, char[] a, int fromIndex, int length) { return result; } + @Pure private static int hashCode(int result, short[] a, int fromIndex, int length) { int end = fromIndex + length; for (int i = fromIndex; i < end; i++) { @@ -243,6 +247,7 @@ private static int hashCode(int result, short[] a, int fromIndex, int length) { return result; } + @Pure private static int hashCode(int result, int[] a, int fromIndex, int length) { int end = fromIndex + length; for (int i = fromIndex; i < end; i++) { diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 5a4f09b96283..275a19414e0b 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -88,6 +88,7 @@ exports org.checkerframework.checker.initialization.qual; exports org.checkerframework.checker.interning.qual; exports org.checkerframework.checker.lock.qual; + exports org.checkerframework.checker.modifiability.qual; exports org.checkerframework.checker.mustcall.qual; exports org.checkerframework.checker.nonempty.qual; exports org.checkerframework.checker.nullness.qual; diff --git a/src/java.base/share/classes/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethodsOnException.java b/src/java.base/share/classes/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethodsOnException.java index 632e324305ab..850857676af5 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethodsOnException.java +++ b/src/java.base/share/classes/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethodsOnException.java @@ -48,7 +48,7 @@ String[] value(); // NOTE 2023/10/6: There seems to be a fundamental limitation in the dataflow framework that - // prevent us from supporting a custom set of exceptions. Specifically, in the following code: + // prevents us from supporting a custom set of exceptions. Specifically, in the following code: // // try { // m1(); @@ -68,8 +68,7 @@ // Class[] exceptions(); /** - * The methods guaranteed to be invoked on the expressions if the result of the method throws an - * exception. + * The methods guaranteed to be invoked on the expressions if the method throws an exception. * * @return the methods guaranteed to be invoked on the expressions if the method throws an * exception diff --git a/src/java.base/share/classes/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethodsVarargs.java b/src/java.base/share/classes/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethodsVarargs.java index 34586c6b1d3c..a88fc6d2608b 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethodsVarargs.java +++ b/src/java.base/share/classes/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethodsVarargs.java @@ -11,7 +11,7 @@ * *
  * @EnsuresCalledMethodsVarargs("m")
- * public void callMOnAll(S s, T t...) { ... }
+ * public void callMOnAll(S s, T... t) { ... }
  * 
* *

This method guarantees that {@code m()} is always called on every {@code T} object passed in diff --git a/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/FenumBottom.java b/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/FenumBottom.java index 5607bd081560..c9d041734397 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/FenumBottom.java +++ b/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/FenumBottom.java @@ -15,7 +15,7 @@ * *

Its relationships are set up via the FenumAnnotatedTypeFactory. * - * @checker_framework.manual #propkey-checker Property File Checker + * @checker_framework.manual #fenum-checker Fake Enum Checker * @checker_framework.manual #bottom-type the bottom type */ @Documented diff --git a/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/SwingTitleJustification.java b/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/SwingTitleJustification.java index ac51e16cc980..b395347cdff8 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/SwingTitleJustification.java +++ b/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/SwingTitleJustification.java @@ -8,7 +8,7 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * Vertical orientations for the title text of a {@link javax.swing.border.TitledBorder}. + * Left and right justifications for the title text of a {@link javax.swing.border.TitledBorder}. * * @see javax.swing.border.TitledBorder#DEFAULT_JUSTIFICATION * @see javax.swing.border.TitledBorder#LEFT diff --git a/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/SwingTitlePosition.java b/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/SwingTitlePosition.java index e3ca36f90b19..e8466734c45e 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/SwingTitlePosition.java +++ b/src/java.base/share/classes/org/checkerframework/checker/fenum/qual/SwingTitlePosition.java @@ -8,7 +8,7 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * Justifications for the title text of a {@link javax.swing.border.TitledBorder}. + * Vertical positions for the title text of a {@link javax.swing.border.TitledBorder}. * * @see javax.swing.border.TitledBorder#DEFAULT_POSITION * @see javax.swing.border.TitledBorder#ABOVE_TOP diff --git a/src/java.base/share/classes/org/checkerframework/checker/formatter/qual/ConversionCategory.java b/src/java.base/share/classes/org/checkerframework/checker/formatter/qual/ConversionCategory.java index 9d76da93e24e..3bf37fea0ab0 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/formatter/qual/ConversionCategory.java +++ b/src/java.base/share/classes/org/checkerframework/checker/formatter/qual/ConversionCategory.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Set; import java.util.StringJoiner; +import org.checkerframework.checker.modifiability.qual.Modifiable; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.framework.qual.AnnotatedFor; @@ -40,15 +41,15 @@ public enum ConversionCategory { GENERAL("bBhHsS", (Class[]) null /* everything */), /** - * Use if the parameter is of a basic types which represent Unicode characters: char, Character, + * Use if the parameter is of a basic type that represents Unicode characters: char, Character, * byte, Byte, short, and Short. This conversion may also be applied to the types int and Integer * when Character.isValidCodePoint(int) returns true. Applicable for conversions c, C. */ CHAR("cC", Character.class, Byte.class, Short.class, Integer.class), /** - * Use if the parameter is an integral type: byte, Byte, short, Short, int and Integer, long, - * Long, and BigInteger. Applicable for conversions d, o, x, X. + * Use if the parameter is an integral type: byte, Byte, short, Short, int, Integer, long, Long, + * and BigInteger. Applicable for conversions d, o, x, X. */ INT("doxX", Byte.class, Short.class, Integer.class, Long.class, BigInteger.class), @@ -59,7 +60,7 @@ public enum ConversionCategory { FLOAT("eEfgGaA", Float.class, Double.class, BigDecimal.class), /** - * Use if the parameter is a type which is capable of encoding a date or time: long, Long, + * Use if the parameter is a type that is capable of encoding a date or time: long, Long, * Calendar, and Date. Applicable for conversions t, T. */ @SuppressWarnings("JdkObsolete") @@ -75,15 +76,15 @@ public enum ConversionCategory { * format("Test %1$c %1$d", (int)42); * * - * In this example, the first parameter is interpreted as both a character and an int, therefore - * the parameter must be compatible with both conversion, and can therefore neither be char nor + * In this example, the first parameter is interpreted as both a character and an int; therefore + * the parameter must be compatible with both conversions, and can therefore be neither char nor * long. This intersection of conversions is called CHAR_AND_INT. * *

One other conversion intersection is interesting, namely the intersection of INT and TIME, * resulting in INT_AND_TIME. * - *

All other intersection either lead to an already existing type, or NULL, in which case it is - * illegal to pass object's of any type as parameter. + *

All other intersections either lead to an already existing type or to NULL, in which case it + * is illegal to pass objects of any type as a parameter. */ CHAR_AND_INT(null, Byte.class, Short.class, Integer.class), @@ -95,14 +96,14 @@ public enum ConversionCategory { INT_AND_TIME(null, Long.class), /** - * Use if no object of any type can be passed as parameter. In this case, the only legal value is - * null. This is seldom needed, and indicates an error in most cases. For example: + * Use if no object of any type can be passed as a parameter. In this case, the only legal value + * is null. This is seldom needed, and indicates an error in most cases. For example: * *

    *   format("Test %1$f %1$d", null);
    * 
* - * Only null can be legally passed, passing a value such as 4 or 4.2 would lead to an exception. + * Only null can be legally passed; passing a value such as 4 or 4.2 would lead to an exception. */ NULL(null), @@ -111,10 +112,10 @@ public enum ConversionCategory { * in most cases. For example: * *
-   *   format("Test %1$s %3$s", "a","unused","b");
+   *   format("Test %1$s %3$s", "a", "unused", "b");
    * 
* - * Only the first "a" and third "b" parameters are used, the second "unused" parameter is ignored. + * Only the first "a" and third "b" parameters are used; the second "unused" parameter is ignored. */ UNUSED(null, (Class[]) null /* everything */); @@ -185,7 +186,7 @@ public enum ConversionCategory { /** * The conversion categories that have a corresponding conversion character. This lacks UNUSED, - * TIME_AND_INT, etc. + * INT_AND_TIME, etc. */ private static final ConversionCategory[] conversionCategoriesWithChar = { GENERAL, CHAR, INT, FLOAT, TIME @@ -218,7 +219,7 @@ public static ConversionCategory fromConversionChar(char c) { * @param the type of array and set elements * @return a set containing the array's elements */ - private static Set arrayToSet(E[] a) { + private static @Modifiable Set arrayToSet(E[] a) { return new HashSet<>(Arrays.asList(a)); } @@ -307,7 +308,7 @@ public static ConversionCategory union(ConversionCategory a, ConversionCategory } if ((a == CHAR_AND_INT && b == INT_AND_TIME) || (a == INT_AND_TIME && b == CHAR_AND_INT)) { // This is special-cased because the union of a.types and b.types - // does not include BigInteger.class, whereas the types for INT does. + // does not include BigInteger.class, whereas the types for INT do. // Returning INT here to prevent returning GENERAL below. return INT; } @@ -352,7 +353,7 @@ public boolean isAssignableFrom(Class argType) { return false; } - /** Returns a pretty printed {@link ConversionCategory}. */ + /** Returns a pretty-printed {@link ConversionCategory}. */ @Pure @Override public String toString() { diff --git a/src/java.base/share/classes/org/checkerframework/checker/guieffect/qual/SafeType.java b/src/java.base/share/classes/org/checkerframework/checker/guieffect/qual/SafeType.java index a1ced023066d..f3bc6a169bfe 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/guieffect/qual/SafeType.java +++ b/src/java.base/share/classes/org/checkerframework/checker/guieffect/qual/SafeType.java @@ -8,7 +8,7 @@ /** * Class declaration annotation to make methods default to {@code @AlwaysSafe}. While the normal - * default is already {@code @AlwaysSafe} methods, this is useful for a type inside a package marked + * default is already {@code @AlwaysSafe}, this is useful for a type inside a package marked * {@code @UIPackage}. * * @checker_framework.manual #guieffect-checker GUI Effect Checker diff --git a/src/java.base/share/classes/org/checkerframework/checker/i18n/qual/LocalizableKey.java b/src/java.base/share/classes/org/checkerframework/checker/i18n/qual/LocalizableKey.java index ce74615098ac..4bbb2b835458 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/i18n/qual/LocalizableKey.java +++ b/src/java.base/share/classes/org/checkerframework/checker/i18n/qual/LocalizableKey.java @@ -9,7 +9,7 @@ /** * Indicates that the {@code String} is a key into a property file or resource bundle containing - * Localized Strings. + * localized strings. * * @checker_framework.manual #i18n-checker Internationalization Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nConversionCategory.java b/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nConversionCategory.java index 3d99894418eb..165eba213374 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nConversionCategory.java +++ b/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nConversionCategory.java @@ -5,11 +5,12 @@ import java.util.HashSet; import java.util.Set; import java.util.StringJoiner; +import org.checkerframework.checker.modifiability.qual.Modifiable; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.framework.qual.AnnotatedFor; /** - * Elements of this enumeration are used in a {@link I18nFormat} annotation to indicate the valid + * Elements of this enumeration are used in an {@link I18nFormat} annotation to indicate the valid * types that may be passed as a format parameter. For example: * *
{@literal @}I18nFormat({GENERAL, NUMBER}) String f = "{0}{1, number}";
@@ -67,7 +68,7 @@ public enum I18nConversionCategory {
   private static final I18nConversionCategory[] namedCategories = {DATE, NUMBER};
 
   /**
-   * Creates a conversion cagetogry from a string name.
+   * Creates a conversion category from a string name.
    *
    * 
    * I18nConversionCategory.stringToI18nConversionCategory("number") == I18nConversionCategory.NUMBER;
@@ -96,7 +97,7 @@ public static I18nConversionCategory stringToI18nConversionCategory(String strin
    * @param  the type of array and set elements
    * @return a set containing the array's elements
    */
-  private static  Set arrayToSet(E[] a) {
+  private static  @Modifiable Set arrayToSet(E[] a) {
     return new HashSet<>(Arrays.asList(a));
   }
 
@@ -159,7 +160,7 @@ public static I18nConversionCategory intersect(
    * Returns the union of the two given I18nConversionCategories.
    *
    * 
-   * I18nConversionCategory.intersect(DATE, NUMBER) == DATE;
+   * I18nConversionCategory.union(DATE, NUMBER) == DATE;
    * 
*/ public static I18nConversionCategory union(I18nConversionCategory a, I18nConversionCategory b) { @@ -196,7 +197,7 @@ public boolean isAssignableFrom(Class argType) { return false; } - /** Returns a pretty printed {@link I18nConversionCategory}. */ + /** Returns a pretty-printed {@link I18nConversionCategory}. */ @Override public String toString() { StringBuilder sb = new StringBuilder(this.name()); diff --git a/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nFormat.java b/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nFormat.java index 5e29edcc2d91..635bf1606b9d 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nFormat.java +++ b/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nFormat.java @@ -36,7 +36,9 @@ /** * An array of {@link I18nConversionCategory}, indicating the types of legal remaining arguments * when a value of the annotated type is used as the first argument to {@link - * java.text.MessageFormat#format(String, Object...) Message.format}. + * java.text.MessageFormat#format(String, Object...) MessageFormat.format}. + * + * @return the legal remaining arguments */ I18nConversionCategory[] value(); } diff --git a/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nMakeFormat.java b/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nMakeFormat.java index a9d66415a896..f9da77a244f3 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nMakeFormat.java +++ b/src/java.base/share/classes/org/checkerframework/checker/i18nformatter/qual/I18nMakeFormat.java @@ -7,10 +7,10 @@ import java.lang.annotation.Target; /** - * This annotation is used internally to annotate {@link java.util.ResourceBundle#getString} - * indicating the checker to check if the given key exist in the translation file and annotate the - * result string with the correct format annotation according to the corresponding key's value. This - * is done in {@link org.checkerframework.checker.i18nformatter.I18nFormatterTransfer} + * This annotation is used internally to annotate {@link java.util.ResourceBundle#getString}. It + * tells the checker to check whether the given key exists in the translation file, and to annotate + * the result string with the correct format annotation according to the corresponding key's value. + * This is done in {@link org.checkerframework.checker.i18nformatter.I18nFormatterTransfer}. * * @checker_framework.manual #i18n-formatter-checker Internationalization Format String Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/EnsuresLTLengthOf.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/EnsuresLTLengthOf.java index 3c8474f49b8d..4af8883d5dbe 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/EnsuresLTLengthOf.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/EnsuresLTLengthOf.java @@ -68,7 +68,7 @@ String[] value(); /** - * Sequences, each of which is longer than the each of the expressions' value on successful method + * Sequences, each of which is longer than each of the expressions' values on successful method * termination. */ @JavaExpression @@ -77,7 +77,7 @@ /** * This expression plus each of the value expressions is less than the length of the sequence on - * successful method termination. The {@code offset} element must ether be empty or the same + * successful method termination. The {@code offset} element must either be empty or the same * length as {@code targetValue}. * * @return the offset expressions diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/EnsuresLTLengthOfIf.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/EnsuresLTLengthOfIf.java index 28e3dea84794..0493772912bd 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/EnsuresLTLengthOfIf.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/EnsuresLTLengthOfIf.java @@ -76,8 +76,8 @@ String[] expression(); /** - * Sequences, each of which is longer than each of the expressions' value after the method returns - * the given result. + * Sequences, each of which is longer than each of the expressions' values after the method + * returns the given result. */ @JavaExpression @QualifierArgument("value") @@ -85,7 +85,7 @@ /** * This expression plus each of the expressions is less than the length of the sequence after the - * method returns the given result. The {@code offset} element must ether be empty or the same + * method returns the given result. The {@code offset} element must either be empty or the same * length as {@code targetValue}. * * @return the offset expressions diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/GTENegativeOne.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/GTENegativeOne.java index 034dc325d925..79609a95bf26 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/GTENegativeOne.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/GTENegativeOne.java @@ -9,7 +9,7 @@ /** * The annotated expression evaluates to an integer greater than or equal to -1. ("GTE" stands for - * ``Greater Than or Equal to''.) + * "Greater Than or Equal to".) * *

As an example use case, consider the definition of the read() method in java.io.InputStream: * diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/HasSubsequence.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/HasSubsequence.java index 4ef51a04538e..a778b7b6ccd2 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/HasSubsequence.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/HasSubsequence.java @@ -42,7 +42,7 @@ *

  • {@code start} is {@code @NonNegative}. *
  • {@code end} is {@code @LTEqLengthOf("a")}. *
  • {@code start} is {@code @LessThan("end + 1")}. - *
  • the value of {@code this} equals {@code array[start..end-1]} + *
  • The value of {@code this} equals {@code array[start..end-1]}. * * * The Index Checker verifies the first 3 facts, but always issues a warning because it cannot prove diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/IndexOrHigh.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/IndexOrHigh.java index 3fecd1d288b1..69dc3cb950bf 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/IndexOrHigh.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/IndexOrHigh.java @@ -22,11 +22,11 @@ * *

    Writing {@code @IndexOrHigh("arr")} is equivalent to writing {@link NonNegative @NonNegative} * {@link LTEqLengthOf @LTEqLengthOf("arr")}, and that is how it is treated internally by the - * checker. Thus, if you write an {@code @IndexFor("arr")} annotation, you might see warnings about - * {@code @NonNegative} or {@code @LTEqLengthOf}. + * checker. Thus, if you write an {@code @IndexOrHigh("arr")} annotation, you might see warnings + * about {@code @NonNegative} or {@code @LTEqLengthOf}. * * @see NonNegative - * @see LTLengthOf + * @see LTEqLengthOf * @checker_framework.manual #index-checker Index Checker */ @Documented diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTEqLengthOf.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTEqLengthOf.java index 1035cee72575..d2fafa19288a 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTEqLengthOf.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTEqLengthOf.java @@ -12,8 +12,8 @@ * The annotated expression evaluates to an integer whose value is less than or equal to the lengths * of all the given sequences. ("LTEq" stands for "Less than or equal to".) * - *

    For example, an expression with type {@code @LTLengthOf({"a", "b"})} is less than or equal to - * both {@code a.length} and {@code b.length}. The sequences {@code a} and {@code b} might have + *

    For example, an expression with type {@code @LTEqLengthOf({"a", "b"})} is less than or equal + * to both {@code a.length} and {@code b.length}. The sequences {@code a} and {@code b} might have * different lengths. * *

    {@code @LTEqLengthOf({"a"})} = {@code @LTLengthOf(value={"a"}, offset=-1)}, and
    diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTLengthOf.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTLengthOf.java index 770e19a788e2..2b00fa0e2fc2 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTLengthOf.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTLengthOf.java @@ -42,10 +42,10 @@ /** * This expression plus the annotated expression is less than the length of the sequence. The - * {@code offset} element must ether be empty or the same length as {@code value}. + * {@code offset} element must either be empty or the same length as {@code value}. * *

    The expressions in {@code offset} may be addition/subtraction of any number of Java - * expressions. For example, {@code @LessThanLengthOf(value = "a", offset = "x + y + 2"}}. + * expressions. For example, {@code @LTLengthOf(value = "a", offset = "x + y + 2")}. */ @JavaExpression String[] offset() default {}; diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTOMLengthOf.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTOMLengthOf.java index 43a545d2ec60..872428367fe1 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTOMLengthOf.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LTOMLengthOf.java @@ -12,8 +12,8 @@ * The annotated expression evaluates to an integer whose value is at least 2 less than the lengths * of all the given sequences. * - *

    For example, an expression with type {@code @LTLengthOf({"a", "b"})} is less than or equal to - * both {@code a.length-2} and {@code b.length-2}. Equivalently, it is less than both {@code + *

    For example, an expression with type {@code @LTOMLengthOf({"a", "b"})} is less than or equal + * to both {@code a.length-2} and {@code b.length-2}. Equivalently, it is less than both {@code * a.length-1} and {@code b.length-1}. The sequences {@code a} and {@code b} might have different * lengths. * @@ -31,7 +31,7 @@ @SubtypeOf(LTLengthOf.class) public @interface LTOMLengthOf { /** - * Sequences, each of whose lengths is at least 1 larger than the annotated expression's value. + * Sequences, each of whose lengths is at least 2 larger than the annotated expression's value. */ @JavaExpression public String[] value(); diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LessThan.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LessThan.java index dd608315c514..d5b2f5c4efb0 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LessThan.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LessThan.java @@ -28,18 +28,18 @@ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @SubtypeOf({LessThanUnknown.class}) -// TODO: I chose to implement less than rather than greater than because in most of the case studies -// false positives, the bigger value is final or effectively final, so it can appear in a dependent -// annotation without causing soundness issues. +// TODO: I chose to implement less than rather than greater than because in most of the false +// positives in the case studies, the bigger value is final or effectively final, so it can +// appear in a dependent annotation without causing soundness issues. public @interface LessThan { /** * The annotated expression's value is less than this expression. * *

    The expressions in {@code value} may be addition/subtraction of any number of Java - * expressions. For example, {@code @LessThan(value = "x + y + 2"}}. + * expressions. For example, {@code @LessThan(value = "x + y + 2")}. * - *

    The expression in {@code value} must be final or constant or the addition/subtract of final - * or constant expressions. + *

    The expression in {@code value} must be final or constant, or the addition/subtraction of + * final or constant expressions. */ @JavaExpression String[] value(); diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LowerBoundBottom.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LowerBoundBottom.java index 3cfe92bdd597..5a3aaea9e494 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LowerBoundBottom.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LowerBoundBottom.java @@ -10,7 +10,7 @@ import org.checkerframework.framework.qual.TypeUseLocation; /** - * The bottom type of the lower bound type system. A variable annotated with this value cannot take + * The bottom type of the lower bound type system. A variable annotated with this type cannot take * on any integer values. * * @checker_framework.manual #index-checker Index Checker diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LowerBoundUnknown.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LowerBoundUnknown.java index 9025c8efa06b..f51841f9b47b 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/LowerBoundUnknown.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/LowerBoundUnknown.java @@ -10,7 +10,7 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * The annotated expression evaluates to value that might be -2 or lower. This is the top type for + * The annotated expression evaluates to a value that might be -2 or lower. This is the top type for * the Lower Bound type system. It should not have to be written by a programmer. * * @checker_framework.manual #index-checker Index Checker diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/PolyGrowShrink.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/PolyGrowShrink.java index 6bb99e1af4fc..0cd145a4aa90 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/PolyGrowShrink.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/PolyGrowShrink.java @@ -8,7 +8,7 @@ import org.checkerframework.framework.qual.PolymorphicQualifier; /** - * A polymorphic qualifier for @GrowOnly type system. + * A polymorphic qualifier for the GrowShrink type system. * * @checker_framework.manual #index-checker Index Checker * @checker_framework.manual #qualifier-polymorphism Qualifier polymorphism diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/PolyIndex.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/PolyIndex.java index fa5c811ee6c8..d8c2124e7403 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/PolyIndex.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/PolyIndex.java @@ -12,7 +12,7 @@ * *

    Writing {@code @PolyIndex} is equivalent to writing {@link PolyUpperBound @PolyUpperBound} * {@link PolyLowerBound @PolyLowerBound}, and that is how it is treated internally by the checker. - * Thus, if you write an {@code @PolyIndex} annotation, you might see warnings about + * Thus, if you write a {@code @PolyIndex} annotation, you might see warnings about * {@code @PolyUpperBound} or {@code @PolyLowerBound}. * * @checker_framework.manual #index-checker Index Checker diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/Positive.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/Positive.java index 7321a024340f..d781ec1379f5 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/Positive.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/Positive.java @@ -18,8 +18,8 @@ * } * }

  • * - * Without the knowing that {@code arr.length} is positive, the Index Checker cannot verify that - * accessing the last element of the array is safe - there might not be a last element! + * Without knowing that {@code arr.length} is positive, the Index Checker cannot verify that + * accessing the last element of the array is safe -- there might not be a last element! * * @checker_framework.manual #index-checker Index Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/SearchIndexFor.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/SearchIndexFor.java index 832adae5c7e1..183f93fe4362 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/SearchIndexFor.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/SearchIndexFor.java @@ -9,8 +9,8 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * The annotated expression evaluates to an integer whose length is between {@code -a.length - 1} - * and {@code a.length - 1}, inclusive, for all sequences {@code a} listed in the annotation. + * The annotated expression evaluates to an integer whose value is between {@code -a.length - 1} and + * {@code a.length - 1}, inclusive, for all sequences {@code a} listed in the annotation. * *

    This is the return type of {@link java.util.Arrays#binarySearch(Object[],Object) * Arrays.binarySearch} in the JDK. diff --git a/src/java.base/share/classes/org/checkerframework/checker/index/qual/SubstringIndexFor.java b/src/java.base/share/classes/org/checkerframework/checker/index/qual/SubstringIndexFor.java index 235d0fad5659..0eb75eb9a490 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/index/qual/SubstringIndexFor.java +++ b/src/java.base/share/classes/org/checkerframework/checker/index/qual/SubstringIndexFor.java @@ -26,7 +26,7 @@ * length of the sequence passed as the first argument. * *

    The name of this annotation, "substring index for", is intended to mean that the annotated - * expression is a index of a substring (returned by {@code indexOf} or similar methods) for the + * expression is an index of a substring (returned by {@code indexOf} or similar methods) for the * specified sequence. * * @checker_framework.manual #index-substringindex Index Checker diff --git a/src/java.base/share/classes/org/checkerframework/checker/initialization/qual/UnderInitialization.java b/src/java.base/share/classes/org/checkerframework/checker/initialization/qual/UnderInitialization.java index 5f3c43e00686..edc899742610 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/initialization/qual/UnderInitialization.java +++ b/src/java.base/share/classes/org/checkerframework/checker/initialization/qual/UnderInitialization.java @@ -21,7 +21,7 @@ * {@code @UnderInitialization(B.class)}. * *

    Code is allowed to store potentially not-fully-initialized objects in the fields of a - * partially-initialized object, as long as all initialization is complete by the end of the + * partially initialized object, as long as all initialization is complete by the end of the * constructor. * *

    What type qualifiers on the field are considered depends on the checker; for instance, the diff --git a/src/java.base/share/classes/org/checkerframework/checker/initialization/qual/UnknownInitialization.java b/src/java.base/share/classes/org/checkerframework/checker/initialization/qual/UnknownInitialization.java index 0924bcec7c78..033091934b75 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/initialization/qual/UnknownInitialization.java +++ b/src/java.base/share/classes/org/checkerframework/checker/initialization/qual/UnknownInitialization.java @@ -34,7 +34,7 @@ * *

    @NonNull Object f;
    * - * In a partially-initialized object, field {@code f} might be {@code null} despite its + * In a partially initialized object, field {@code f} might be {@code null} despite its * {@literal @}{@link NonNull} type annotation. * *

    What type qualifiers on the field are considered depends on the checker; for instance, the diff --git a/src/java.base/share/classes/org/checkerframework/checker/interning/qual/InternedDistinct.java b/src/java.base/share/classes/org/checkerframework/checker/interning/qual/InternedDistinct.java index 80b80e37cfaf..804f7ffd300f 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/interning/qual/InternedDistinct.java +++ b/src/java.base/share/classes/org/checkerframework/checker/interning/qual/InternedDistinct.java @@ -11,7 +11,7 @@ /** * Indicates that no other value is {@code equals()} to the given value. Therefore, it is correct to - * use == to test an InternedDistinct value for equality against any other value. + * use {@code ==} to test an InternedDistinct value for equality against any other value. * *

    This is a stronger property than {@link Interned}, but a weaker property than every value of a * Java type being interned. diff --git a/src/java.base/share/classes/org/checkerframework/checker/interning/qual/UnknownInterned.java b/src/java.base/share/classes/org/checkerframework/checker/interning/qual/UnknownInterned.java index c1a8b79cf09e..58fa55baa6a3 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/interning/qual/UnknownInterned.java +++ b/src/java.base/share/classes/org/checkerframework/checker/interning/qual/UnknownInterned.java @@ -11,7 +11,7 @@ /** * The top qualifier for the Interning Checker. It indicates lack of knowledge about whether values - * are interned or not. It is not written by programmers, but is used internally by the type system. + * are interned. It is not written by programmers, but is used internally by the type system. * * @see org.checkerframework.checker.interning.InterningChecker * @checker_framework.manual #interning-checker Interning Checker diff --git a/src/java.base/share/classes/org/checkerframework/checker/interning/qual/UsesObjectEquals.java b/src/java.base/share/classes/org/checkerframework/checker/interning/qual/UsesObjectEquals.java index 003e3cdd53e2..056c91ed755a 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/interning/qual/UsesObjectEquals.java +++ b/src/java.base/share/classes/org/checkerframework/checker/interning/qual/UsesObjectEquals.java @@ -8,8 +8,8 @@ import java.lang.annotation.Target; /** - * Class declaration to indicate the class does not override {@code equals(Object)}, and therefore - * {@code a.equals(b)} and {@code a == b} behave identically. + * Class declaration annotation to indicate that the class does not override {@code equals(Object)}, + * and therefore {@code a.equals(b)} and {@code a == b} behave identically. * *

    A class may be annotated @UsesObjectEquals if neither it, nor any of its supertypes or * subtypes, overrides {@code equals}. Therefore, it cannot be written on {@code Object} itself. It diff --git a/src/java.base/share/classes/org/checkerframework/checker/lock/qual/GuardSatisfied.java b/src/java.base/share/classes/org/checkerframework/checker/lock/qual/GuardSatisfied.java index 460ec9e7c1b7..7b66a9d993ef 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/lock/qual/GuardSatisfied.java +++ b/src/java.base/share/classes/org/checkerframework/checker/lock/qual/GuardSatisfied.java @@ -37,7 +37,7 @@ public @interface GuardSatisfied { /** * The index on the GuardSatisfied polymorphic qualifier, if any. Defaults to -1 so that, if the - * user writes 0, that is different than writing no index. Writing no index is the usual case. + * user writes 0, that is different from writing no index. Writing no index is the usual case. * * @return the index on the GuardSatisfied polymorphic qualifier, or -1 if none */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/lock/qual/GuardedBy.java b/src/java.base/share/classes/org/checkerframework/checker/lock/qual/GuardedBy.java index e5bc9c9af78d..0d9996b38cf2 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/lock/qual/GuardedBy.java +++ b/src/java.base/share/classes/org/checkerframework/checker/lock/qual/GuardedBy.java @@ -40,7 +40,7 @@ @SubtypeOf(GuardedByUnknown.class) @DefaultQualifierInHierarchy // These are required because the default for local variables is @GuardedByUnknown, but if the local -// variable is one of these type kinds, the default should be @GuardedByUnknown. +// variable is one of these type kinds, the default should be @GuardedBy. @DefaultFor( value = {TypeUseLocation.EXCEPTION_PARAMETER, TypeUseLocation.UPPER_BOUND}, typeKinds = { diff --git a/src/java.base/share/classes/org/checkerframework/checker/lock/qual/NewObject.java b/src/java.base/share/classes/org/checkerframework/checker/lock/qual/NewObject.java index d52adec2862a..2381d56dc1fe 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/lock/qual/NewObject.java +++ b/src/java.base/share/classes/org/checkerframework/checker/lock/qual/NewObject.java @@ -13,7 +13,7 @@ import org.checkerframework.framework.qual.TypeUseLocation; /** - * A type that represents a newly-constructed object. It can be treated as having any + * A type that represents a newly constructed object. It can be treated as having any * {@code @}{@link GuardedBy} type. Typically, it is only written on factory method return types. * * @checker_framework.manual #lock-checker Lock Checker diff --git a/src/java.base/share/classes/org/checkerframework/checker/lock/qual/ReleasesNoLocks.java b/src/java.base/share/classes/org/checkerframework/checker/lock/qual/ReleasesNoLocks.java index dc43239f906f..086c9fa36cc9 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/lock/qual/ReleasesNoLocks.java +++ b/src/java.base/share/classes/org/checkerframework/checker/lock/qual/ReleasesNoLocks.java @@ -10,7 +10,7 @@ import org.checkerframework.framework.qual.InheritedAnnotation; /** - * The method maintains a strictly nondecreasing lock held count on the current thread for any locks + * The method maintains a strictly nondecreasing lock-held count on the current thread for any locks * that were held prior to the method call. The same property must in general be true of all the * methods it calls, which should themselves be annotated as {@code @ReleasesNoLocks} or a stronger * annotation such as {@code @}{@link SideEffectFree}. diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomGrowable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomGrowable.java new file mode 100644 index 000000000000..74583859f068 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomGrowable.java @@ -0,0 +1,21 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * The bottom qualifier in the Grow hierarchy. + * + *

    Programmers should rarely write this qualifier. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf({Growable.class, Ungrowable.class}) +public @interface BottomGrowable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomReplaceable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomReplaceable.java new file mode 100644 index 000000000000..13bcf09aa392 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomReplaceable.java @@ -0,0 +1,21 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * The bottom qualifier in the Replace hierarchy. + * + *

    Programmers should rarely write this qualifier. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf({Replaceable.class, Unreplaceable.class}) +public @interface BottomReplaceable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomSeqGrowable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomSeqGrowable.java new file mode 100644 index 000000000000..4c7f8a15713f --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomSeqGrowable.java @@ -0,0 +1,21 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * The bottom qualifier in the sequenced-grow hierarchy. + * + *

    Programmers should rarely write this qualifier. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf({SeqGrowable.class, SeqUngrowable.class}) +public @interface BottomSeqGrowable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomShrinkable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomShrinkable.java new file mode 100644 index 000000000000..b27c14d30ad7 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/BottomShrinkable.java @@ -0,0 +1,21 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * The bottom qualifier in the Shrink hierarchy. + * + *

    Programmers should rarely write this qualifier. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf({Shrinkable.class, Unshrinkable.class}) +public @interface BottomShrinkable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Growable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Growable.java new file mode 100644 index 000000000000..4ad15fd4f8be --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Growable.java @@ -0,0 +1,22 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * Calling grow operations such as {@code add}, {@code addAll}, etc. on this collection will not + * throw {@link UnsupportedOperationException}. + * + *

    No guarantees are made about shrink or replace operations. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf(MaybeGrowable.class) +public @interface Growable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/IteratorPolyMod.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/IteratorPolyMod.java new file mode 100644 index 000000000000..0fd6d04fcfd4 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/IteratorPolyMod.java @@ -0,0 +1,30 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * If a collection's type is {@code @IteratorPolyMod}, then its {@code iterator()} and {@code + * listIterator()} methods preserve the ability to call modification methods in its iterator. + * + *

    For example, if collection {@code c} has type {@code @Shrinkable}, then {@code c.iterator()} + * also has type {@code @Shrinkable}. If list {@code l} has type {@code @Replaceable}, then {@code + * l.listIterator()} also has type {@code @Replaceable}. + * + *

    If the collection itself is {@code @Unmodifiable}, then its iterator is {@code @Unshrinkable}. + * For any collection whose type is {@code @MaybeIteratorPolyMod}, its iterator is + * {@code @MaybeShrinkable}. + * + *

    This annotation is used in GrowChecker, ShrinkChecker, and ReplaceChecker. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf(MaybeIteratorPolyMod.class) +public @interface IteratorPolyMod {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeGrowable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeGrowable.java new file mode 100644 index 000000000000..c4914c496803 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeGrowable.java @@ -0,0 +1,25 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.DefaultQualifierInHierarchy; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * The top qualifier in the Grow hierarchy. The collection may or may not support grow operations + * such as {@code add}. The checker conservatively issues an error wherever a grow operation is + * called on a {@code @MaybeGrowable} expression. + * + *

    This is the default qualifier for unannotated types in the Grow hierarchy. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf({}) +@DefaultQualifierInHierarchy +public @interface MaybeGrowable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeIteratorPolyMod.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeIteratorPolyMod.java new file mode 100644 index 000000000000..d7a6ea8b5a49 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeIteratorPolyMod.java @@ -0,0 +1,24 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.DefaultQualifierInHierarchy; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * For the annotated collection, the return type of {@code iterator()} is {@code @MaybeShrinkable}. + * + *

    This is the default qualifier for unannotated types. + * + * @see IteratorPolyMod + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf({}) +@DefaultQualifierInHierarchy +public @interface MaybeIteratorPolyMod {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeModifiable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeModifiable.java new file mode 100644 index 000000000000..eafb5cce1344 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeModifiable.java @@ -0,0 +1,24 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Convenience alias meaning + * {@code @MaybeGrowable @MaybeSeqGrowable @MaybeShrinkable @MaybeReplaceable}. Represents an + * unknown or arbitrary modifiability capability; the checker cannot determine whether the + * collection is growable, sequenced-growable, shrinkable, or replaceable. + * + *

    This annotation is not part of the type hierarchy; the Modifiability Checker expands it to + * {@code @MaybeGrowable @MaybeSeqGrowable @MaybeShrinkable @MaybeReplaceable} on each annotated + * type. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +public @interface MaybeModifiable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeReplaceable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeReplaceable.java new file mode 100644 index 000000000000..1b1aa7978b95 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeReplaceable.java @@ -0,0 +1,25 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.DefaultQualifierInHierarchy; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * The top qualifier in the Replace hierarchy. The collection may or may not support replace + * operations such as {@code set} and {@code replaceAll}. The checker conservatively issues an error + * wherever a replace operation is called on a {@code @MaybeReplaceable} expression. + * + *

    This is the default qualifier for unannotated types in the Replace hierarchy. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf({}) +@DefaultQualifierInHierarchy +public @interface MaybeReplaceable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeSeqGrowable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeSeqGrowable.java new file mode 100644 index 000000000000..27e7feb3dc8c --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeSeqGrowable.java @@ -0,0 +1,26 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.DefaultQualifierInHierarchy; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * The top qualifier in the sequenced-grow hierarchy. The collection or map may or may not support + * positional operations such as {@code addFirst}, {@code addLast}, {@code putFirst}, and {@code + * putLast}. The checker conservatively issues an error wherever a sequenced-grow operation is + * called on a {@code @MaybeSeqGrowable} expression. + * + *

    This is the default qualifier for unannotated types in the sequenced-grow hierarchy. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf({}) +@DefaultQualifierInHierarchy +public @interface MaybeSeqGrowable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeShrinkable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeShrinkable.java new file mode 100644 index 000000000000..2175b8951d5f --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/MaybeShrinkable.java @@ -0,0 +1,26 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.DefaultQualifierInHierarchy; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * The top qualifier in the Shrink hierarchy. The collection may or may not support shrink + * operations such as {@code remove}, {@code, removeAll}, and {@code clear}. The checker + * conservatively issues an error wherever a shrink operation is called on a + * {@code @MaybeShrinkable} expression. + * + *

    This is the default qualifier for unannotated types in the Shrink hierarchy. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf({}) +@DefaultQualifierInHierarchy +public @interface MaybeShrinkable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Modifiable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Modifiable.java new file mode 100644 index 000000000000..978239c8b045 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Modifiable.java @@ -0,0 +1,23 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Convenience alias usually meaning {@code @Growable @Shrinkable @Replaceable @SeqGrowable}. + * Calling a mutating operation (growing, shrinking, replacing, or sequenced-growing) on this + * collection will not throw {@link UnsupportedOperationException}. + * + *

    As an exception, {@code @Modifiable} means {@code @Maybe*} if the type does not support a + * given category of operation; for example, {@code @Modifiable Iterator} means + * {@code @MaybeGrowable @Shrinkable @MaybeReplaceable @MaybeSeqGrowable Iterator}. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +public @interface Modifiable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyGrowable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyGrowable.java new file mode 100644 index 000000000000..fe6467671535 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyGrowable.java @@ -0,0 +1,23 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.PolymorphicQualifier; + +/** + * A polymorphic qualifier for the Grow hierarchy. + * + *

    When used on a method, the Grow capability of the return type matches the Grow capability of + * the argument or receiver annotated with {@code @PolyGrowable}. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + * @checker_framework.manual #qualifier-polymorphism Qualifier polymorphism + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@PolymorphicQualifier(MaybeGrowable.class) +public @interface PolyGrowable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyIteratorPolyMod.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyIteratorPolyMod.java new file mode 100644 index 000000000000..75c43c0090cb --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyIteratorPolyMod.java @@ -0,0 +1,24 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.PolymorphicQualifier; + +/** + * A polymorphic qualifier for the Iterator hierarchy. + * + *

    When used on a method, whether the return type has {@code @IteratorPolyMod} matches whether + * the argument or receiver annotated with {@code @PolyIteratorPolyMod} has + * {@code @IteratorPolyMod}. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + * @checker_framework.manual #qualifier-polymorphism Qualifier polymorphism + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@PolymorphicQualifier(MaybeIteratorPolyMod.class) +public @interface PolyIteratorPolyMod {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyModifiable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyModifiable.java new file mode 100644 index 000000000000..9467ac599891 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyModifiable.java @@ -0,0 +1,30 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Convenience alias meaning + * {@code @PolyGrowable @PolyReplaceable @PolyShrinkable @PolySeqGrowable @PolyIteratorPolyMod}. A + * polymorphic qualifier for all five modifiability hierarchies. + * + *

    Write {@code @PolyModifiable} on methods that preserve or transfer modifiability, such as + * {@code List.subList()}. + * + *

    For example: + * + *

    
    + * class Example {
    + *   @PolyModifiable List<E> subList(@PolyModifiable List<E> this, int from, int to);
    + * }
    + * 
    + * + * At each call site, the return type is equal to the receiver type. If the receiver type is + * {@code @Growable}, the return type is {@code @Growable}. If the receiver type is + * {@code @Ungrowable}, the return type is {@code @Ungrowable}. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + * @checker_framework.manual #qualifier-polymorphism Qualifier polymorphism + */ +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +public @interface PolyModifiable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyReplaceable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyReplaceable.java new file mode 100644 index 000000000000..b9f7b0fa5815 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyReplaceable.java @@ -0,0 +1,23 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.PolymorphicQualifier; + +/** + * A polymorphic qualifier for the Replace hierarchy. + * + *

    When used on a method, the Replace capability of the return type matches the Replace + * capability of the argument or receiver annotated with {@code @PolyReplaceable}. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + * @checker_framework.manual #qualifier-polymorphism Qualifier polymorphism + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@PolymorphicQualifier(MaybeReplaceable.class) +public @interface PolyReplaceable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolySeqGrowable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolySeqGrowable.java new file mode 100644 index 000000000000..5edd895d7900 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolySeqGrowable.java @@ -0,0 +1,20 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.PolymorphicQualifier; + +/** + * A polymorphic qualifier for the sequenced-grow hierarchy. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + * @checker_framework.manual #qualifier-polymorphism Qualifier polymorphism + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@PolymorphicQualifier(MaybeSeqGrowable.class) +public @interface PolySeqGrowable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyShrinkable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyShrinkable.java new file mode 100644 index 000000000000..9ed1fef83e6b --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PolyShrinkable.java @@ -0,0 +1,22 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.PolymorphicQualifier; + +/** + * A polymorphic qualifier for the Shrink hierarchy that preserves the shrink capability. + * + *

    Use on methods that preserve shrinkability — for example, {@code Map.keySet()}. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + * @checker_framework.manual #qualifier-polymorphism Qualifier polymorphism + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@PolymorphicQualifier(MaybeShrinkable.class) +public @interface PolyShrinkable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PreservesModifiability.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PreservesModifiability.java new file mode 100644 index 000000000000..c5f414ad7693 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/PreservesModifiability.java @@ -0,0 +1,27 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The annotated method preserves modifiability capabilities from its first argument to its return + * value. + * + *

    For example, if the first argument has a positive modifiability capability, such as + * {@code @Growable}, {@code @SeqGrowable}, {@code @Shrinkable}, or {@code @Replaceable}, then the + * return type has that same capability. If the first argument is {@code @IteratorPolyMod}, then the + * return type is also {@code @IteratorPolyMod}. If the first argument has any other qualifier in a + * capability hierarchy, then the return type is the top qualifier in that hierarchy. + * + *

    If the annotated method has no parameters or returns {@code void}, then this annotation has no + * effect on the method. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface PreservesModifiability {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Replaceable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Replaceable.java new file mode 100644 index 000000000000..58a79dbd5b7a --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Replaceable.java @@ -0,0 +1,22 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * Calling replace operations such as {@code set}, {@code replaceAll}, etc. on this collection will + * not throw {@link UnsupportedOperationException}. + * + *

    No guarantees are made about grow or shrink operations. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf(MaybeReplaceable.class) +public @interface Replaceable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/SeqGrowable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/SeqGrowable.java new file mode 100644 index 000000000000..3714fe5909d9 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/SeqGrowable.java @@ -0,0 +1,23 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * Calling sequenced-grow operations such as {@code addFirst}, {@code addLast}, {@code offerFirst}, + * {@code offerLast}, {@code push}, {@code putFirst}, and {@code putLast} on this collection or map + * will not throw {@link UnsupportedOperationException}. + * + *

    No guarantees are made about ordinary grow, shrink, or replace operations. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf(MaybeSeqGrowable.class) +public @interface SeqGrowable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/SeqUngrowable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/SeqUngrowable.java new file mode 100644 index 000000000000..ff6c4008ed1f --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/SeqUngrowable.java @@ -0,0 +1,23 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * Calling sequenced-grow operations such as {@code addFirst}, {@code addLast}, {@code offerFirst}, + * {@code offerLast}, {@code push}, {@code putFirst}, and {@code putLast} on this collection or map + * will throw {@link UnsupportedOperationException}. + * + *

    No guarantees are made about ordinary grow, shrink, or replace operations. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf(MaybeSeqGrowable.class) +public @interface SeqUngrowable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Shrinkable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Shrinkable.java new file mode 100644 index 000000000000..39bb42266427 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Shrinkable.java @@ -0,0 +1,22 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * Calling shrink operations such as {@code remove}, {@code removeAll}, {@code clear}, etc. on this + * collection will not throw {@link UnsupportedOperationException}. + * + *

    No guarantees are made about grow or replace operations. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf(MaybeShrinkable.class) +public @interface Shrinkable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Ungrowable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Ungrowable.java new file mode 100644 index 000000000000..fba98d025e2f --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Ungrowable.java @@ -0,0 +1,22 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * Calling grow operations such as {@code add}, {@code addAll}, etc. on this collection will throw + * {@link UnsupportedOperationException}. + * + *

    No guarantees are made about shrink or replace operations. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf(MaybeGrowable.class) +public @interface Ungrowable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Unmodifiable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Unmodifiable.java new file mode 100644 index 000000000000..9c4e5fb1dba6 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Unmodifiable.java @@ -0,0 +1,24 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Convenience alias usually meaning + * {@code @Ungrowable @SeqUngrowable @Unshrinkable @Unreplaceable}. Calling a mutating operation + * (growing, sequenced-growing, shrinking, or replacing) will throw {@link + * UnsupportedOperationException}. + * + *

    As an exception, {@code @Unmodifiable} means {@code @Maybe*} if the type does not support a + * given category of operation; for example, {@code @Unmodifiable Iterator} means + * {@code @MaybeGrowable @MaybeSeqGrowable @Unshrinkable @MaybeReplaceable Iterator}. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +public @interface Unmodifiable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/UnmodifiableParam.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/UnmodifiableParam.java new file mode 100644 index 000000000000..4c3390762cdc --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/UnmodifiableParam.java @@ -0,0 +1,23 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Convenience alias that is syntactic sugar for {@code @}{@link MaybeModifiable}. It may only be + * written within a formal parameter type. + * + *

    You can write {@code @UnmodifiableParam} to indicate that a method does not change its + * parameter. From the method's point of view, the parameter is unmodifiable. This is reference + * unmodifiability, because another alias can change the value, unlike {@code @}{@link Unmodifiable} + * which means object immutability: no alias can change the value. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +public @interface UnmodifiableParam {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Unreplaceable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Unreplaceable.java new file mode 100644 index 000000000000..eec0205d2c50 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Unreplaceable.java @@ -0,0 +1,22 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * Calling replace operations such as {@code set}, {@code replaceAll}, etc. on this collection will + * throw {@link UnsupportedOperationException}. + * + *

    No guarantees are made about grow or shrink operations. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf(MaybeReplaceable.class) +public @interface Unreplaceable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Unshrinkable.java b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Unshrinkable.java new file mode 100644 index 000000000000..eb32a3949516 --- /dev/null +++ b/src/java.base/share/classes/org/checkerframework/checker/modifiability/qual/Unshrinkable.java @@ -0,0 +1,22 @@ +package org.checkerframework.checker.modifiability.qual; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * Calling shrink operations such as {@code remove}, {@code removeAll}, {@code clear}, etc. on this + * collection will throw {@link UnsupportedOperationException}. + * + *

    No guarantees are made about grow or replace operations. + * + * @checker_framework.manual #modifiability-checker Modifiability Checker + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@SubtypeOf(MaybeShrinkable.class) +public @interface Unshrinkable {} diff --git a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/CreatesMustCallFor.java b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/CreatesMustCallFor.java index 989613f0210c..95aa2e8d1a33 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/CreatesMustCallFor.java +++ b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/CreatesMustCallFor.java @@ -30,7 +30,7 @@ * never to missed alarms (lack of warnings at incorrect code). * *

    As an example, consider the following code, which uses a {@code @CreatesMustCallFor} - * annotation to indicate that the {@code reset()} method re-assigns the {@code socket} field: + * annotation to indicate that the {@code reset()} method reassigns the {@code socket} field: * *

      * @MustCall("stop")
    @@ -78,7 +78,7 @@
        * Returns the expression whose must-call type is reset after a call to a method with this
        * annotation. The expression must be visible in the scope immediately before each call site, so
        * it can only refer to fields, the method's parameters (which should be referenced via the "#X"
    -   * syntax, where "#1" is the first argument, #2 is the second, etc.), or {@code "this"}. The
    +   * syntax, where "#1" is the first argument, "#2" is the second, etc.), or {@code "this"}. The
        * default is {@code "this"}. At call-sites, the viewpoint-adapted referent of expression must be
        * owning (an owning field, a local variable tracked in a resource alias set, etc.) or a {@code
        * reset.not.owning} error is issued.
    diff --git a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/InheritableMustCall.java b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/InheritableMustCall.java
    index 08839790d5fb..9c98a7b65b2b 100644
    --- a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/InheritableMustCall.java
    +++ b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/InheritableMustCall.java
    @@ -8,7 +8,7 @@
     
     /**
      * This annotation is an alias for {@link MustCall} that applies to the type on which it is written
    - * and all of its subtypes. It prevents the need to annotate each subtype with an {@link
    + * and all of its subtypes. It avoids the need to annotate each subtype with a {@link
      * MustCall} annotation. This annotation may only be written on a class declaration.
      *
      * @checker_framework.manual #must-call-checker Must Call Checker
    diff --git a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/MustCallAlias.java b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/MustCallAlias.java
    index f99eee8d4391..954086c7c343 100644
    --- a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/MustCallAlias.java
    +++ b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/MustCallAlias.java
    @@ -29,11 +29,11 @@
      *
      * 

    Type system semantics

    * - * Within the Must Call Checker's type system, {@code @MustCallAlias} annotations have a semantics - * different from a standard polymorphic annotation, in that the relevant actual parameter type and - * return type at a call site are not equated in all cases. Given an actual parameter {@code p} - * passed in a {@code @MustCallAlias} position at a call site, the return type of the call is - * defined as follows: + * Within the Must Call Checker's type system, {@code @MustCallAlias} annotations have semantics + * different from those of a standard polymorphic annotation, in that the relevant actual parameter + * type and return type at a call site are not equated in all cases. Given an actual parameter + * {@code p} passed in a {@code @MustCallAlias} position at a call site, the return type of the call + * is defined as follows: * *
      *
    • If the base return type has a non-empty {@code @InheritableMustCall("m")} annotation on its @@ -42,8 +42,8 @@ *
    • In all other cases, the return type has the same {@code @MustCall} type as {@code p}. *
    * - * {@link PolyMustCall} has an identical type system semantics. This special treatment is required - * to allow for a wrapper object to have a must-call method with a different name than the must-call + * {@link PolyMustCall} has identical type system semantics. This special treatment is required to + * allow for a wrapper object to have a must-call method with a different name than the must-call * method name for the wrapped object. * *

    Verifying {@code @MustCallAlias} annotations

    @@ -53,7 +53,8 @@ *

    For a constructor: * *

      - *
    • The constructor must always write p into exactly one field {@code f} of the new object. + *
    • The constructor must always write {@code p} into exactly one field {@code f} of the new + * object. *
    • Field {@code f} must be annotated {@code @}{@link Owning}. *
    * @@ -62,15 +63,15 @@ *
      *
    • All return sites must be calls to other methods or constructors with {@code @MustCallAlias} * return types, and this method's {@code @MustCallAlias} parameter must be passed in the - * {@code MustCallAlias} position to that method or constructor (i.e., the calls must pass + * {@code MustCallAlias} position to that method or constructor (i.e., the calls must pass the * {@code @MustCallAlias} parameter through a chain of {@code @MustCallAlias}-annotated * parameters and returns). *
    * - * When the -AnoResourceAliases command-line argument is passed to the checker, this annotation is - * treated identically to {@link PolyMustCall}. That is, the annotation still impacts {@link - * MustCall} types as a polymorphic annotation (see "Type system semantics" above), but it is not - * used by the Resource Leak Checker to more precisely reason about when obligations have been + * When the {@code -AnoResourceAliases} command-line argument is passed to the checker, this + * annotation is treated identically to {@link PolyMustCall}. That is, the annotation still impacts + * {@link MustCall} types as a polymorphic annotation (see "Type system semantics" above), but it is + * not used by the Resource Leak Checker to more precisely reason about when obligations have been * satisfied. * * @checker_framework.manual #resource-leak-checker Resource Leak Checker diff --git a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/MustCallUnknown.java b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/MustCallUnknown.java index 398051fcdd7e..1b9c631c5bc7 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/MustCallUnknown.java +++ b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/MustCallUnknown.java @@ -13,7 +13,7 @@ * *

    The Resource Leak Checker cannot verify that the property represented by this annotation is * enforced; that is, the Resource Leak Checker will always issue a warning when the value of an - * expression with this type might be de-allocated. + * expression with this type might be deallocated. * * @checker_framework.manual #must-call-checker Must Call Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/Owning.java b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/Owning.java index dd2473fcfc20..e3578eb8bc56 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/Owning.java +++ b/src/java.base/share/classes/org/checkerframework/checker/mustcall/qual/Owning.java @@ -18,7 +18,7 @@ * logically belong to any type hierarchy. Logically, it is a directive to the Resource Leak Checker * that informs it whether it is necessary to check that a value's must-call obligations have been * satisfied. In that way, it can be viewed as an annotation expressing an aliasing relationship: - * passing a object with a non-empty must-call obligation to a method with an owning parameter + * passing an object with a non-empty must-call obligation to a method with an owning parameter * resolves that object's must-call obligation, because the ownership annotation expresses that the * object at the call site and the parameter in the method's body are aliases, and so checking only * one of the two is required. diff --git a/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/EnsuresNonEmptyIf.java b/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/EnsuresNonEmptyIf.java index 56be2b326f4d..10a24ed8b566 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/EnsuresNonEmptyIf.java +++ b/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/EnsuresNonEmptyIf.java @@ -9,7 +9,7 @@ import org.checkerframework.framework.qual.InheritedAnnotation; /** - * Indicates that the specific expressions are non-empty, if the method returns the given result + * Indicates that the specified expressions are non-empty, if the method returns the given result * (either true or false). * *

    Here are ways this conditional postcondition annotation can be used: @@ -22,7 +22,7 @@ *

    * * because, if {@code isLengthGreaterThanZero} returns true, then {@code items} was non-empty. Note - * that you can write more than one {@code @EnsuresNonEmptyIf} annotations on a single method. + * that you can write more than one {@code @EnsuresNonEmptyIf} annotation on a single method. * *

    Fields: The value expression can refer to fields, even private ones. For example: * diff --git a/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/NonEmpty.java b/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/NonEmpty.java index dafeb3b2788d..b8632f43c6c5 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/NonEmpty.java +++ b/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/NonEmpty.java @@ -9,7 +9,7 @@ /** * The {@link java.util.Collection Collection}, {@link java.util.Iterator Iterator}, {@link Iterable - * Iterable}, or {@link java.util.Map Map}, or {@link java.util.stream.Stream Stream} is definitely + * Iterable}, {@link java.util.Map Map}, or {@link java.util.stream.Stream Stream} is definitely * non-empty. */ // Reinstate when the Non-Empty Checker is publicized. diff --git a/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/RequiresNonEmpty.java b/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/RequiresNonEmpty.java index d60f89f4a121..63b74c7805a0 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/RequiresNonEmpty.java +++ b/src/java.base/share/classes/org/checkerframework/checker/nonempty/qual/RequiresNonEmpty.java @@ -9,9 +9,9 @@ import org.checkerframework.framework.qual.PreconditionAnnotation; /** - * Indicates a method precondition: the specified expressions that may be a {@link + * Indicates a method precondition: the specified expressions, each of which may be a {@link * java.util.Collection collection}, {@link java.util.Iterator iterator}, {@link java.lang.Iterable - * iterable}, or {@link java.util.Map map} must be non-empty when the annotated method is invoked. + * iterable}, or {@link java.util.Map map}, must be non-empty when the annotated method is invoked. * *

    For example: * @@ -48,7 +48,7 @@ * testClass.list1 = l1; * testClass.m1(); // OK * - * testClass.m2(); // Compile-time error: m2 requires that list2 is @NonEmpty + * testClass.m2(); // Compile-time error: m2 requires that list2 is @NonEmpty. * * testClass.list2 = l2; * testClass.m2(); // OK @@ -74,10 +74,11 @@ public @interface RequiresNonEmpty { /** - * The Java {@link java.util.Collection collection}, {@link java.util.Iterator iterator}, {@link - * java.lang.Iterable iterable}, or {@link java.util.Map map} that must be non-empty. + * The Java expressions, each a {@link java.util.Collection collection}, {@link java.util.Iterator + * iterator}, {@link java.lang.Iterable iterable}, or {@link java.util.Map map}, that must be + * non-empty. * - * @return the Java expression that must be non-empty + * @return the Java expressions that must be non-empty */ String[] value(); diff --git a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/EnsuresNonNull.java b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/EnsuresNonNull.java index 95f194b05507..7900a4d966e3 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/EnsuresNonNull.java +++ b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/EnsuresNonNull.java @@ -26,7 +26,7 @@ *

    * * It can also be used for a method that fails if a given expression is null, indicating that the - * argument is null if the method returns normally: + * argument is non-null if the method returns normally: * *
    
      *  /** Throws an exception if the argument is null. */
    diff --git a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/EnsuresNonNullIf.java b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/EnsuresNonNullIf.java
    index 5d0fcf215b95..a366b2660c70 100644
    --- a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/EnsuresNonNullIf.java
    +++ b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/EnsuresNonNullIf.java
    @@ -10,7 +10,7 @@
     import org.checkerframework.framework.qual.InheritedAnnotation;
     
     // TODO: In a fix for https://tinyurl.com/cfissue/1917, add the text:  Every prefix expression is
    -// also non-null; for example, {@code @EnsuresNonNullIf(expression="a.b.c", results=true)} implies
    +// also non-null; for example, {@code @EnsuresNonNullIf(expression="a.b.c", result=true)} implies
     // that both {@code a.b} and {@code a.b.c} are non-null if the method returns {@code true}.
     /**
      * Indicates that the given expressions are non-null, if the method returns the given result (either
    @@ -90,7 +90,7 @@
       String[] expression();
     
       /**
    -   * * A wrapper annotation that makes the {@link EnsuresNonNullIf} annotation repeatable.
    +   * A wrapper annotation that makes the {@link EnsuresNonNullIf} annotation repeatable.
        *
        * 

    Programmers generally do not need to write this. It is created by Java when a programmer * writes more than one {@link EnsuresNonNullIf} annotation at the same location. diff --git a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/KeyFor.java b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/KeyFor.java index 396f7160a43a..7fad2f91eace 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/KeyFor.java +++ b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/KeyFor.java @@ -29,8 +29,8 @@ *

    You do not usually need to write {@code @KeyFor} on the key type in a map. That is, you can * declare variable {@code Map myMap;} and the Nullness Checker will apply * {@code @KeyFor} as appropriate. If you redundantly write {@code @KeyFor}, as in {@code - * Map<@KeyFor("myMap") String, Integer> myMap;}, then your code is more verbose, and more seriously - * the Nullness Checker will issue errors when calling methods such as {@code Map.put}. + * Map<@KeyFor("myMap") String, Integer> myMap;}, then your code is more verbose, and, more + * seriously, the Nullness Checker will issue errors when calling methods such as {@code Map.put}. * * @see EnsuresKeyFor * @see EnsuresKeyForIf diff --git a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/MonotonicNonNull.java b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/MonotonicNonNull.java index 8608c15fc15f..fded77bb15ac 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/MonotonicNonNull.java +++ b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/MonotonicNonNull.java @@ -19,7 +19,7 @@ * *

      *
    1. The field may be assigned only non-null values. - *
    2. The field may be re-assigned as often as desired. + *
    3. The field may be reassigned as often as desired. *
    * *

    When the field is first read within a method, the field cannot be assumed to be non-null. diff --git a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/Nullable.java b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/Nullable.java index 2333f2cb8ae2..0d21af7bd9e6 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/Nullable.java +++ b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/Nullable.java @@ -14,7 +14,7 @@ * {@link Nullable} is a type annotation that makes no commitments about whether the value is {@code * null}. Equivalently, the type includes the {@code null} value. * - *

    The Nullness Checker issues an error if {@code null} is assigned an expression of {@link + *

    The Nullness Checker issues an error if {@code null} is assigned to an expression of {@link * NonNull} type. * *

    Programmers typically write {@code @Nullable} to indicate that the value is not known to be diff --git a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/PolyKeyFor.java b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/PolyKeyFor.java index 6a89836561b6..e5df3ece6cd7 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/PolyKeyFor.java +++ b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/PolyKeyFor.java @@ -14,7 +14,7 @@ * one in which every instance of {@code @PolyKeyFor} has been replaced by {@code @}{@link * UnknownKeyFor}, one in which every instance of {@code @}{@link PolyKeyFor} has been replaced by * {@code @}{@link KeyForBottom}, and ones in which every instance of {@code @PolyKeyFor} has been - * replaced by {@code @}{@code KeyFor}, for every possible combination of map arguments. + * replaced by {@code @}{@link KeyFor}, for every possible combination of map arguments. * * @checker_framework.manual #nullness-checker Nullness Checker * @checker_framework.manual #qualifier-polymorphism Qualifier polymorphism diff --git a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/RequiresNonNull.java b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/RequiresNonNull.java index 37acd2f59774..9dddbd1f3650 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/RequiresNonNull.java +++ b/src/java.base/share/classes/org/checkerframework/checker/nullness/qual/RequiresNonNull.java @@ -55,7 +55,7 @@ * * Do not use this annotation for formal parameters (instead, give them a {@code @NonNull} type, * which is the default and need not be written). The {@code @RequiresNonNull} annotation is - * intended for non-parameter, such as field accesses or method calls. + * intended for non-parameter expressions, such as field accesses or method calls. * * @checker_framework.manual #nullness-checker Nullness Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/EnsuresPresentIf.java b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/EnsuresPresentIf.java index 1d094ae66c4b..95deb7168483 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/EnsuresPresentIf.java +++ b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/EnsuresPresentIf.java @@ -38,10 +38,10 @@ * that a method establishes a property, even though client code cannot directly affect the field. * *

    Method postconditions: Suppose that if a method {@code isRectangle()} returns true, - * then {@code getRectangle()} will return a present Optional. You an express this relationship as: + * then {@code getRectangle()} will return a present Optional. You can express this relationship as: * *

    {@code  @EnsuresPresentIf(result=true, expression="getRectangle()")
    - * public @Pure isRectangle() { ... }}
    + * public @Pure boolean isRectangle() { ... }}
    * * @see Present * @see EnsuresPresent diff --git a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalCreator.java b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalCreator.java index c7344bdccc99..5d6fafebab7b 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalCreator.java +++ b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalCreator.java @@ -7,7 +7,7 @@ import java.lang.annotation.Target; import org.checkerframework.framework.qual.InheritedAnnotation; -/** An method annotation for methods that create an {@link java.util.Optional} */ +/** A method annotation for methods that create an {@link java.util.Optional}. */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) diff --git a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalEliminator.java b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalEliminator.java index d1825468aa30..386577a49f5a 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalEliminator.java +++ b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalEliminator.java @@ -7,7 +7,10 @@ import java.lang.annotation.Target; import org.checkerframework.framework.qual.InheritedAnnotation; -/** Methods whose receiver is an {@link java.util.Optional} and return a non-optional. */ +/** + * A method annotation for methods whose receiver is an {@link java.util.Optional} and that return a + * non-{@code Optional} value. + */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) diff --git a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalPropagator.java b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalPropagator.java index 322a1c420459..85bde9d43931 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalPropagator.java +++ b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/OptionalPropagator.java @@ -7,7 +7,10 @@ import java.lang.annotation.Target; import org.checkerframework.framework.qual.InheritedAnnotation; -/** Methods whose receiver is an {@link java.util.Optional} and return an {@code Optional}. */ +/** + * A method annotation for methods whose receiver is an {@link java.util.Optional} and that return + * an {@code Optional}. + */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) diff --git a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/RequiresPresent.java b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/RequiresPresent.java index 1ad847e9e0ae..caa7614ea53c 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/optional/qual/RequiresPresent.java +++ b/src/java.base/share/classes/org/checkerframework/checker/optional/qual/RequiresPresent.java @@ -25,33 +25,33 @@ * *   @RequiresPresent({"optId1", "#1.optId1"}) * void method1(MyClass other) { - * optId1.get().length() // OK, this.optID1 is known to be present. - * optId2.get().length() // error, might throw NoSuchElementException. + * optId1.get().length(); // OK, this.optId1 is known to be present. + * optId2.get().length(); // error, might throw NoSuchElementException. * - * other.optId1.get().length() // OK, this.optID1 is known to be present. - * other.optId2.get().length() // error, might throw NoSuchElementException. + * other.optId1.get().length(); // OK, other.optId1 is known to be present. + * other.optId2.get().length(); // error, might throw NoSuchElementException. * } * * void method2() { * MyClass other = new MyClass(); * * optId1 = Optional.of("abc"); - * other.optId1 = Optional.of("def") + * other.optId1 = Optional.of("def"); * method1(other); // OK, satisfies method precondition. * * optId1 = Optional.empty(); - * other.optId1 = Optional.empty("abc"); + * other.optId1 = Optional.of("abc"); * method1(other); // error, does not satisfy this.optId1 method precondition. * - * optId1 = Optional.empty("abc"); + * optId1 = Optional.of("abc"); * other.optId1 = Optional.empty(); - * method1(other); // error. does not satisfy other.optId1 method precondition. + * method1(other); // error, does not satisfy other.optId1 method precondition. * } * } * * * Do not use this annotation for formal parameters (instead, give them a {@code @Present} type). - * The {@code @RequiresNonNull} annotation is intended for non-parameter expressions, such as field + * The {@code @RequiresPresent} annotation is intended for non-parameter expressions, such as field * accesses or method calls. * * @checker_framework.manual #optional-checker Optional Checker diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/ArrayWithoutPackage.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/ArrayWithoutPackage.java index 880dcc449ee1..eaba50571fb7 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/ArrayWithoutPackage.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/ArrayWithoutPackage.java @@ -10,7 +10,7 @@ /** * An identifier or primitive type, followed by any number of array square brackets. * - *

    Example: Foobar[][] Example: Baz22 + *

    Examples: Foobar[][], Baz22 * * @checker_framework.manual #signature-checker Signature Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/BinaryName.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/BinaryName.java index 72e3272bdc50..3f6fbd87c71e 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/BinaryName.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/BinaryName.java @@ -24,11 +24,9 @@ * the binary names for the two types are org.checkerframework.checker.signature.SignatureChecker * and org.checkerframework.checker.signature.SignatureChecker$Inner. * - *

    Binary names and {@linkplain InternalForm internal form} only differ by the use of '.' vs. '/' - * as package separator. - * *

    The binary name should not be confused with the {@linkplain InternalForm internal form}, which - * is a variant of the binary name that actually appears in the class file. + * is a variant of the binary name that actually appears in the class file. The two differ only by + * the use of '.' vs. '/' as the package separator. * * @checker_framework.manual #signature-checker Signature Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/CanonicalName.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/CanonicalName.java index 946f216518d2..5c96f8269758 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/CanonicalName.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/CanonicalName.java @@ -8,8 +8,8 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * Canonical names have the same syntactic form as {@link FullyQualifiedName fully-qualified name}s. - * Every canonical name is a fully-qualified name, but not every fully-qualified name is a canonical + * Canonical names have the same syntactic form as {@link FullyQualifiedName fully qualified name}s. + * Every canonical name is a fully qualified name, but not every fully qualified name is a canonical * name. * *

    JLS section @@ -28,9 +28,9 @@ * {@code I}, but only {@code p.O1.I} is its canonical name. * * - * Given a character sequence that is a fully-qualified name, there is no way to know whether or not - * it is a canonical name, without examining the program it refers to. Type-checking determines that - * a string is a {@code CanonicalName} based on provenance (what method produced the string), rather + * Given a character sequence that is a fully qualified name, there is no way to know whether it is + * a canonical name, without examining the program it refers to. Type-checking determines that a + * string is a {@code CanonicalName} based on provenance (what method produced the string), rather * than the contents of the string. * * @see FullyQualifiedName diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/CanonicalNameAndBinaryName.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/CanonicalNameAndBinaryName.java index c096a3fec001..33f244433ecb 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/CanonicalNameAndBinaryName.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/CanonicalNameAndBinaryName.java @@ -13,7 +13,7 @@ * org.checkerframework.checker.signature.qual.BinaryName binary name}. It represents a non-array, * non-inner, non-primitive class. * - *

    Examples: int, MyClass, java.lang, java.lang.Integer + *

    Examples: MyClass, java.lang, java.lang.Integer * * @checker_framework.manual #signature-checker Signature Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/ClassGetSimpleName.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/ClassGetSimpleName.java index ca75e879b7fc..a93a7280bbc8 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/ClassGetSimpleName.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/ClassGetSimpleName.java @@ -9,7 +9,7 @@ /** * The format produced by the {@link Class#getSimpleName()} method. It is an identifier or the empty - * string (for an anonymous class), followed by an number of array square brackets. + * string (for an anonymous class), followed by any number of array square brackets. * * @checker_framework.manual #signature-checker Signature Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/DotSeparatedIdentifiers.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/DotSeparatedIdentifiers.java index bbc2227e5f15..c1d2ccea61c7 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/DotSeparatedIdentifiers.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/DotSeparatedIdentifiers.java @@ -13,7 +13,7 @@ * {@linkplain org.checkerframework.checker.signature.qual.BinaryName binary name}. It represents a * non-array, non-inner, non-primitive class: dot-separated identifiers. * - *

    Examples: int, MyClass, java.lang, java.lang.Integer + *

    Examples: MyClass, java.lang, java.lang.Integer * * @checker_framework.manual #signature-checker Signature Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FieldDescriptor.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FieldDescriptor.java index 8d32fdecf723..526e89c80745 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FieldDescriptor.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FieldDescriptor.java @@ -21,9 +21,9 @@ * } * * - * the field descriptors for the two types are - * Lorg/checkerframework/checker/signature/SignatureChecker; and - * Lorg/checkerframework/checker/signature/SignatureChecker$Inner; . + * the field descriptors for the two types are {@code + * Lorg/checkerframework/checker/signature/SignatureChecker;} and {@code + * Lorg/checkerframework/checker/signature/SignatureChecker$Inner;}. * * @checker_framework.manual #signature-checker Signature Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FieldDescriptorWithoutPackage.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FieldDescriptorWithoutPackage.java index 3b2d18ed8f5f..4fc796c0043a 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FieldDescriptorWithoutPackage.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FieldDescriptorWithoutPackage.java @@ -11,7 +11,7 @@ * Represents a {@link FieldDescriptor field descriptor} for a primitive or for an array whose base * type is primitive or in the unnamed package. * - *

    Examples: I [[J MyClass MyClass$22 [LMyClass; [LMyClass$22 + *

    Examples: I [[J MyClass MyClass$22 [LMyClass; [LMyClass$22; * *

    Field descriptor (JVM type format) is defined in the Java Virtual diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FqBinaryName.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FqBinaryName.java index 32af0d6646ee..4d37958795fe 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FqBinaryName.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FqBinaryName.java @@ -8,8 +8,8 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * An extension of binary name format to represent primitives and arrays. It is just like - * fully-qualified name format, but uses "$" rather than "." to indicate a nested class. + * An extension of binary name format to represent primitives and arrays. It is just like fully + * qualified name format, but uses "$" rather than "." to indicate a nested class. * *

    Examples include * diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FullyQualifiedName.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FullyQualifiedName.java index 6d2fcd7b7c5c..9763ae0b96e8 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FullyQualifiedName.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/FullyQualifiedName.java @@ -9,7 +9,7 @@ /** * A sequence of dot-separated identifiers, followed by any number of array square brackets. - * Represents a fully-qualified name as defined in the Java Language * Specification, section 6.7. * @@ -24,7 +24,7 @@ * java.lang.Integer[][][] * } * - *

    in + *

    For example, in * *

      *  package org.checkerframework.checker.signature;
    @@ -33,7 +33,7 @@
      *  }
      * 
    * - * the fully-qualified names for the two types are + * the fully qualified names for the two types are * org.checkerframework.checker.signature.SignatureChecker and * org.checkerframework.checker.signature.SignatureChecker.Inner. * diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/MethodDescriptor.java b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/MethodDescriptor.java index 28aedd800774..30d0fc776226 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/MethodDescriptor.java +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/MethodDescriptor.java @@ -23,7 +23,8 @@ * } * * - * In this example method descriptor for method 'method': (Ljava/lang/Object;I)Z + * In this example, the method descriptor for method {@code method} is {@code + * (Ljava/lang/Object;I)V}. * * @checker_framework.manual #signature-checker Signature Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/README.md b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/README.md index 6d12616e023f..6dbf1ce71523 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/signature/qual/README.md +++ b/src/java.base/share/classes/org/checkerframework/checker/signature/qual/README.md @@ -1,6 +1,6 @@ -# No `@QualifierForLiteral` annotations +# No `@QualifierForLiterals` annotations -These type qualifiers have no `@QualifierForLiteral` annotations. Instead, they +These type qualifiers have no `@QualifierForLiterals` annotations. Instead, they use the regular expressions in [reflection-util](https://github.com/plume-lib/reflection-util/)'s [`SignatureRegexes` diff --git a/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlEvenQuotes.java b/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlEvenQuotes.java index 20da1db881f5..f325e958d219 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlEvenQuotes.java +++ b/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlEvenQuotes.java @@ -12,7 +12,7 @@ * i.e., there must be either zero or an even number of {@code '} characters in a SqlEvenQuotes * String that are not preceded immediately by another {@code '} character. (Thus, all SqlEvenQuotes * Strings ultimately contain an even number of {@code '} characters.) A SqlEvenQuotes String is - * syntactical to be passed to query execution methods and is guaranteed not to affect whether + * syntactically valid to pass to query execution methods and is guaranteed not to affect whether * subsequent concatenations are interpreted as SQL command code or as SQL query values. * *

    Common use cases include: SQL query fragments, such as "SELECT * FROM"; properly sanitized diff --git a/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlOddQuotes.java b/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlOddQuotes.java index 05a6c99d42f0..8a2ee0a56e2c 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlOddQuotes.java +++ b/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlOddQuotes.java @@ -9,10 +9,10 @@ /** * Denotes a String that contains an odd number of unescaped single quotes -- i.e., there must be an - * odd number of ' characters in a SqlOddQuotes String that are not preceded immediately by another - * ' character. (Thus, all SqlOddQuotes Strings ultimately contain an odd number of single quotes, - * escaped or otherwise.) SqlOddQuotes Strings are not syntactical to be passed to query execution - * methods. + * odd number of {@code '} characters in a SqlOddQuotes String that are not preceded immediately by + * another {@code '} character. (Thus, all SqlOddQuotes strings ultimately contain an odd number of + * single quotes, escaped or otherwise.) SqlOddQuotes strings are not syntactically valid to pass to + * query execution methods. * *

    Common use cases include: SQL query fragments to be concatenated with user input, such as * "SELECT * FROM table WHERE field = '"; SQL query fragments containing user input but missing an diff --git a/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlQuotesUnknown.java b/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlQuotesUnknown.java index ad3cb4ff049d..06d0f0ffc7d1 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlQuotesUnknown.java +++ b/src/java.base/share/classes/org/checkerframework/checker/sqlquotes/qual/SqlQuotesUnknown.java @@ -10,7 +10,7 @@ /** * Represents the top of the SQL Quotes qualifier hierarchy. Used to denote a String of which the - * quoting is unknown. Using a SqlQuotesUnknown Strings within a SQL query may be, or lead to, a SQL + * quoting is unknown. Using a SqlQuotesUnknown String within a SQL query may be, or lead to, a SQL * injection vulnerability. * *

    Common use cases include unsanitized user input. diff --git a/src/java.base/share/classes/org/checkerframework/checker/tainting/qual/Tainted.java b/src/java.base/share/classes/org/checkerframework/checker/tainting/qual/Tainted.java index b0ad26dc49e4..749e55973706 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/tainting/qual/Tainted.java +++ b/src/java.base/share/classes/org/checkerframework/checker/tainting/qual/Tainted.java @@ -9,7 +9,7 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * Denotes a possibly-tainted value: at run time, the value might be tainted or might be untainted. + * Denotes a possibly tainted value: at run time, the value might be tainted or might be untainted. * * @see Untainted * @see org.checkerframework.checker.tainting.TaintingChecker diff --git a/src/java.base/share/classes/org/checkerframework/checker/tainting/qual/Untainted.java b/src/java.base/share/classes/org/checkerframework/checker/tainting/qual/Untainted.java index 83d48f5186b1..c162656a9b78 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/tainting/qual/Untainted.java +++ b/src/java.base/share/classes/org/checkerframework/checker/tainting/qual/Untainted.java @@ -12,7 +12,7 @@ import org.checkerframework.framework.qual.TypeUseLocation; /** - * Denotes a reference that is untainted, i.e. can be trusted. + * Denotes a reference that is untainted, i.e., can be trusted. * * @checker_framework.manual #tainting-checker Tainting Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/units/qual/Area.java b/src/java.base/share/classes/org/checkerframework/checker/units/qual/Area.java index 14dcf9388d26..82ac156cf6aa 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/units/qual/Area.java +++ b/src/java.base/share/classes/org/checkerframework/checker/units/qual/Area.java @@ -8,7 +8,7 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * Units of areas. + * Units of area. * * @checker_framework.manual #units-checker Units Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/checker/units/qual/m2.java b/src/java.base/share/classes/org/checkerframework/checker/units/qual/m2.java index dc4473d006d9..ec01caaff106 100644 --- a/src/java.base/share/classes/org/checkerframework/checker/units/qual/m2.java +++ b/src/java.base/share/classes/org/checkerframework/checker/units/qual/m2.java @@ -18,6 +18,11 @@ @SubtypeOf(Area.class) @SuppressWarnings("checkstyle:typename") public @interface m2 { - // does this make sense? Is it multiple of (m^2)? Or (multiple of m)^2? + // Does this make sense? Is it a multiple of (m^2)? Or (multiple of m)^2? + /** + * The coefficient that multiplies the unit. + * + * @return the coefficient that multiplies the unit + */ Prefix value() default Prefix.one; } diff --git a/src/java.base/share/classes/org/checkerframework/common/aliasing/qual/LeakedToResult.java b/src/java.base/share/classes/org/checkerframework/common/aliasing/qual/LeakedToResult.java index a9043d47ce4d..39b417298580 100644 --- a/src/java.base/share/classes/org/checkerframework/common/aliasing/qual/LeakedToResult.java +++ b/src/java.base/share/classes/org/checkerframework/common/aliasing/qual/LeakedToResult.java @@ -22,7 +22,7 @@ */ // This is a type qualifier because of a Checker Framework limitation (Issue 383), but its hierarchy -// is ignored. Once the stub parser gets updated to read non-type-qualifiers annotations on stub +// is ignored. Once the stub parser gets updated to read non-type-qualifier annotations on stub // files, this annotation won't be a type qualifier anymore. @Documented diff --git a/src/java.base/share/classes/org/checkerframework/common/aliasing/qual/NonLeaked.java b/src/java.base/share/classes/org/checkerframework/common/aliasing/qual/NonLeaked.java index 5686d79b5b3d..1f04c613fc65 100644 --- a/src/java.base/share/classes/org/checkerframework/common/aliasing/qual/NonLeaked.java +++ b/src/java.base/share/classes/org/checkerframework/common/aliasing/qual/NonLeaked.java @@ -8,7 +8,7 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * This annotation is used on a formal parameter to indicate that the parameter is not leaked + * This annotation is used on a formal parameter to indicate that the parameter is neither leaked * (stored in a location that could be accessed later) nor returned by the method body. * *

    For example, the parameter of {@link String#String(String s)} is {@code @NonLeaked}, because diff --git a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/ClassVal.java b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/ClassVal.java index a6d22b0bc4ce..c89949bbd980 100644 --- a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/ClassVal.java +++ b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/ClassVal.java @@ -20,7 +20,7 @@ @SubtypeOf({UnknownClass.class}) public @interface ClassVal { /** - * The name of the type that this Class object represents. The name is a "fully-qualified binary + * The name of the type that this Class object represents. The name is a "fully qualified binary * name" ({@link org.checkerframework.checker.signature.qual.FqBinaryName}): a primitive or binary name, * possibly followed by some number of array brackets. diff --git a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/GetClass.java b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/GetClass.java index bd97c1aae91f..65e1c0ebda1c 100644 --- a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/GetClass.java +++ b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/GetClass.java @@ -7,7 +7,7 @@ import java.lang.annotation.Target; /** - * Annotation for methods like {@code Object.getClassName}. Their signature is:
    + * Annotation for methods like {@code Object.getClass}. Their signature is:
    * {@code @}{@link ClassBound}{@code ("ReceiverType") Class method(ReceiverType this) {...}} * * @checker_framework.manual #reflection-resolution Reflection resolution diff --git a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/GetMethod.java b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/GetMethod.java index f1aba337deae..1978d7fc8601 100644 --- a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/GetMethod.java +++ b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/GetMethod.java @@ -9,8 +9,8 @@ /** * Annotation for methods like {@code Class.getMethod} and {@code Class.getDeclaredMethod}, whose * signature is:
    - * {@code {@link MethodVal}(classname=c, methodname=m, params=p) Method getMyMethod(Class this, - * String m, Object... params)} + * {@code @}{@link MethodVal}{@code (classname=c, methodname=m, params=p) Method + * getMyMethod(Class this, String m, Object... params)} * * @checker_framework.manual #reflection-resolution Reflection resolution */ diff --git a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/Invoke.java b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/Invoke.java index 431631cc0e17..37997ccd912e 100644 --- a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/Invoke.java +++ b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/Invoke.java @@ -8,8 +8,8 @@ /** * Annotation for methods like {@code Method.invoke}, whose signature is:
    - * {@code Object method({@link MethodVal}(classname=c, methodname=m, params=p) Method this, Object - * obj, Object... args)} + * {@code Object method(@}{@link MethodVal}{@code (classname=c, methodname=m, params=p) Method this, + * Object obj, Object... args)} * * @checker_framework.manual #reflection-resolution Reflection resolution */ diff --git a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/MethodVal.java b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/MethodVal.java index 792119ad09e7..346085e98e11 100644 --- a/src/java.base/share/classes/org/checkerframework/common/reflection/qual/MethodVal.java +++ b/src/java.base/share/classes/org/checkerframework/common/reflection/qual/MethodVal.java @@ -12,7 +12,7 @@ * java.lang.reflect.Constructor Constructor} values. If an expression's type has * {@code @MethodVal}, then the expression's run-time value is one of those values. * - *

    Each of {@code @MethodVal}'s argument lists must be of equal length, and { className[i], + *

    All of {@code @MethodVal}'s argument lists must be of equal length, and { className[i], * methodName[i], params[i] } represents one of the {@code Method} or {@code Constructor} values in * the set. * diff --git a/src/java.base/share/classes/org/checkerframework/common/returnsreceiver/qual/This.java b/src/java.base/share/classes/org/checkerframework/common/returnsreceiver/qual/This.java index 4d30a59c72a7..c86aade1c69f 100644 --- a/src/java.base/share/classes/org/checkerframework/common/returnsreceiver/qual/This.java +++ b/src/java.base/share/classes/org/checkerframework/common/returnsreceiver/qual/This.java @@ -33,7 +33,7 @@ * * *

    While it would be natural to make {@code @This} the default annotation for receivers, it leads - * to false positives warnings due to https://github.com/typetools/checker-framework/issues/2931, * so this defaulting is currently elided. * diff --git a/src/java.base/share/classes/org/checkerframework/common/subtyping/qual/Bottom.java b/src/java.base/share/classes/org/checkerframework/common/subtyping/qual/Bottom.java index 4e3f318ea79c..71b8245ff0a9 100644 --- a/src/java.base/share/classes/org/checkerframework/common/subtyping/qual/Bottom.java +++ b/src/java.base/share/classes/org/checkerframework/common/subtyping/qual/Bottom.java @@ -12,16 +12,16 @@ * It should not be used! Instead, each type system should define its own dedicated bottom type. * *

    This qualifier is used automatically if the existing qualifiers do not have a bottom type. - * This only works the user never runs two type systems together. Furthermore, because it has no - * {@code @RetentionPolicy} meta-annotation, this qualifier will not be stored in bytecode. So, only - * use this qualifier during prototyping of very simple type systems. For realistic systems, - * introduce a top and bottom qualifier that gets stored in bytecode. + * This only works if the user never runs two type systems together. Furthermore, because it has no + * {@code @Retention} meta-annotation, this qualifier will not be stored in bytecode. So, only use + * this qualifier during prototyping of very simple type systems. For realistic systems, introduce a + * top and bottom qualifier that gets stored in bytecode. * *

    To use this qualifier, the type system designer needs to use methods like {@code * ImplicitsTreeAnnotator.addTreeKind()} to add default annotations and needs to manually add the * bottom qualifier to the qualifier hierarchy. * - *

    See {@code QualifierHierarchy.getBottomAnnotations()} + *

    See {@code QualifierHierarchy.getBottomAnnotations()}. * * @checker_framework.manual #subtyping-checker Subtyping Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/common/util/count/report/qual/ReportCall.java b/src/java.base/share/classes/org/checkerframework/common/util/count/report/qual/ReportCall.java index c75f028528fb..ef81cd21fac2 100644 --- a/src/java.base/share/classes/org/checkerframework/common/util/count/report/qual/ReportCall.java +++ b/src/java.base/share/classes/org/checkerframework/common/util/count/report/qual/ReportCall.java @@ -11,7 +11,7 @@ * this method. Note that calls through a supertype, where the method is not annotated, cannot be * reported. * - *

    For example, assume three classes A, B, and C, that each implement/override a method m and A + *

    For example, assume three classes A, B, and C that each implement/override a method m and A * <: B <: C. Assume that B.m is annotated as ReportCall. Calls of A.m and B.m will then be * reported, but calls of C.m will not be reported, even though the C reference might point to a B * object. Therefore, add the ReportCall annotation high enough in the subtype hierarchy. diff --git a/src/java.base/share/classes/org/checkerframework/common/value/qual/BoolVal.java b/src/java.base/share/classes/org/checkerframework/common/value/qual/BoolVal.java index 66f5a7fcbeb2..efa96fd190bb 100644 --- a/src/java.base/share/classes/org/checkerframework/common/value/qual/BoolVal.java +++ b/src/java.base/share/classes/org/checkerframework/common/value/qual/BoolVal.java @@ -8,7 +8,7 @@ import org.checkerframework.framework.qual.SubtypeOf; /** - * An annotation indicating the possible values for a bool type. If an expression's type has this + * An annotation indicating the possible values for a boolean type. If an expression's type has this * annotation, then at run time, the expression evaluates to one of the annotation's arguments. * * @checker_framework.manual #constant-value-checker Constant Value Checker diff --git a/src/java.base/share/classes/org/checkerframework/common/value/qual/DoubleVal.java b/src/java.base/share/classes/org/checkerframework/common/value/qual/DoubleVal.java index 1bfab7a21d78..cd7571430043 100644 --- a/src/java.base/share/classes/org/checkerframework/common/value/qual/DoubleVal.java +++ b/src/java.base/share/classes/org/checkerframework/common/value/qual/DoubleVal.java @@ -12,8 +12,6 @@ * has this annotation, then at run time, the expression evaluates to one of the annotation's * arguments. * - *

    Annotation for values - * * @checker_framework.manual #constant-value-checker Constant Value Checker */ @Documented diff --git a/src/java.base/share/classes/org/checkerframework/common/value/qual/EnsuresMinLenIf.java b/src/java.base/share/classes/org/checkerframework/common/value/qual/EnsuresMinLenIf.java index 976d7e1b15f8..ef4bfcc8618b 100644 --- a/src/java.base/share/classes/org/checkerframework/common/value/qual/EnsuresMinLenIf.java +++ b/src/java.base/share/classes/org/checkerframework/common/value/qual/EnsuresMinLenIf.java @@ -28,9 +28,9 @@ @Repeatable(EnsuresMinLenIf.List.class) public @interface EnsuresMinLenIf { /** - * Returns the return value of the method under which the postcondition to hold. + * Returns the return value of the method under which the postcondition holds. * - * @return the return value of the method under which the postcondition to hold + * @return the return value of the method under which the postcondition holds */ boolean result(); diff --git a/src/java.base/share/classes/org/checkerframework/common/value/qual/IntRange.java b/src/java.base/share/classes/org/checkerframework/common/value/qual/IntRange.java index 74c99b478156..42fd4b6c2e84 100644 --- a/src/java.base/share/classes/org/checkerframework/common/value/qual/IntRange.java +++ b/src/java.base/share/classes/org/checkerframework/common/value/qual/IntRange.java @@ -27,9 +27,9 @@ @SubtypeOf(UnknownVal.class) public @interface IntRange { /** - * Largest value in the range, inclusive. + * Smallest value in the range, inclusive. * - * @return the largest value in the range, inclusive + * @return the smallest value in the range, inclusive */ long from() default Long.MIN_VALUE; diff --git a/src/java.base/share/classes/org/checkerframework/common/value/qual/MinLen.java b/src/java.base/share/classes/org/checkerframework/common/value/qual/MinLen.java index a05c8431bbff..33f3a9a08586 100644 --- a/src/java.base/share/classes/org/checkerframework/common/value/qual/MinLen.java +++ b/src/java.base/share/classes/org/checkerframework/common/value/qual/MinLen.java @@ -8,8 +8,9 @@ /** * The value of the annotated expression is a sequence containing at least the given number of - * elements. An alias for an {@link ArrayLenRange} annotation with a {@code from} field and the - * maximum possible value for an array length ({@code Integer.MAX_VALUE}) as its {@code to} field. + * elements. An alias for an {@link ArrayLenRange} annotation with this number as its {@code from} + * field and the maximum possible value for an array length ({@code Integer.MAX_VALUE}) as its + * {@code to} field. * *

    This annotation is used extensively by the Index Checker. * diff --git a/src/java.base/share/classes/org/checkerframework/common/value/qual/UnknownVal.java b/src/java.base/share/classes/org/checkerframework/common/value/qual/UnknownVal.java index 1ae28c0f75ba..53b5316ab9c2 100644 --- a/src/java.base/share/classes/org/checkerframework/common/value/qual/UnknownVal.java +++ b/src/java.base/share/classes/org/checkerframework/common/value/qual/UnknownVal.java @@ -11,7 +11,7 @@ /** * UnknownVal is a type annotation indicating that the expression's value is not known at compile - * type. + * time. * * @checker_framework.manual #constant-value-checker Constant Value Checker */ diff --git a/src/java.base/share/classes/org/checkerframework/dataflow/qual/AssertMethod.java b/src/java.base/share/classes/org/checkerframework/dataflow/qual/AssertMethod.java index d57400c40bbe..fe3a86f5b7c5 100644 --- a/src/java.base/share/classes/org/checkerframework/dataflow/qual/AssertMethod.java +++ b/src/java.base/share/classes/org/checkerframework/dataflow/qual/AssertMethod.java @@ -15,7 +15,7 @@ * {@code Assertions.assertTrue} is annotated as follows: * *

    {@code @AssertMethod(value = AssertionFailedError.class)
    - * public static void assertFalse(boolean condition);
    + * public static void assertTrue(boolean condition);
      * }
    * * Then, in the code below, the Optional Checker can determine that {@code optional} has a value and @@ -55,15 +55,15 @@ /** * Returns true if this method asserts that the boolean expression is false. * - *

    For example, Junit's For example, JUnit's Assertions.assertFalse(...) - * throws an exception if the first argument is false. So it is annotated as follows: + * throws an exception if the first argument is not false. So it is annotated as follows: * *

    {@code @AssertMethod(value = AssertionFailedError.class, isAssertFalse = true)
        * public static void assertFalse(boolean condition);
        * }
    * - * @return the value for {@link #parameter} on which the method throws an exception + * @return true if this method asserts that the boolean expression is false */ boolean isAssertFalse() default false; } diff --git a/src/java.base/share/classes/org/checkerframework/dataflow/qual/Deterministic.java b/src/java.base/share/classes/org/checkerframework/dataflow/qual/Deterministic.java index c56484f0dd67..dbd5cd8afb1c 100644 --- a/src/java.base/share/classes/org/checkerframework/dataflow/qual/Deterministic.java +++ b/src/java.base/share/classes/org/checkerframework/dataflow/qual/Deterministic.java @@ -41,13 +41,13 @@ * *
      *
    1. Assignment to any expression, except for local variables and method parameters.
      - * (Note that storing into an array element, such a {@code a[i] = x}, is not an assignment to + * (Note that storing into an array element, such as {@code a[i] = x}, is not an assignment to * a variable and is therefore forbidden.) *
    2. A method invocation of a method that is not {@link Deterministic}. *
    3. Construction of a new object. *
    4. Catching any exceptions. This restriction prevents a method from obtaining a reference to a - * newly-created exception object and using these objects (or some property thereof) to change - * the method's return value. For instance, the following method must be forbidden. + * newly created exception object and using these objects (or some property thereof) to change + * the method's return value. For instance, the following method is forbidden. * *
      @Deterministic
        * int f() {
      diff --git a/src/java.base/share/classes/org/checkerframework/dataflow/qual/SideEffectFree.java b/src/java.base/share/classes/org/checkerframework/dataflow/qual/SideEffectFree.java
      index db55f24f792b..4a91c2e2330a 100644
      --- a/src/java.base/share/classes/org/checkerframework/dataflow/qual/SideEffectFree.java
      +++ b/src/java.base/share/classes/org/checkerframework/dataflow/qual/SideEffectFree.java
      @@ -7,11 +7,11 @@
       import java.lang.annotation.Target;
       
       /**
      - * A method is called side-effect-free if it has no visible side-effects, such as setting a
      + * A method is called side-effect-free if it has no visible side effects, such as setting a
        * field of an object that existed before the method was called.
        *
        * 

      Only the visible side effects are important. The method is allowed to cache the answer to a - * computationally expensive query, for instance. It is also allowed to modify newly-created + * computationally expensive query, for instance. It is also allowed to modify newly created * objects. A constructor is side-effect-free if it does not modify any objects that existed before * it was called in ways that are externally visible. * @@ -30,7 +30,7 @@ * *

        *
      1. Assignment to any expression, except for local variables and method parameters.
        - * (Note that storing into an array element, such a {@code a[i] = x}, is not an assignment to + * (Note that storing into an array element, such as {@code a[i] = x}, is not an assignment to * a variable and is therefore forbidden.) *
      2. A method invocation of a method that is not {@code @SideEffectFree}. *
      3. Construction of a new object where the constructor is not {@code @SideEffectFree}. diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/AnnotatedFor.java b/src/java.base/share/classes/org/checkerframework/framework/qual/AnnotatedFor.java index 8c2844ee9925..72e0f1565466 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/AnnotatedFor.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/AnnotatedFor.java @@ -12,8 +12,8 @@ * annotations such as {@code @Nullable} and {@code @Regex}. The argument to {@code AnnotatedFor} is * not an annotation name, but a checker name. * - *

        You should only use this annotation in a partially-annotated library. There is no point to - * using it in a fully-annotated library nor in an application that does not export APIs for + *

        You should only use this annotation in a partially annotated library. There is no point to + * using it in a fully annotated library nor in an application that does not export APIs for * clients. * *

        This annotation has no effect unless the {@code @@ -31,7 +31,7 @@ public @interface AnnotatedFor { /** * Returns the type systems for which the class has been annotated. Legal arguments are any string - * that may be passed to the {@code -processor} command-line argument: the fully-qualified class + * that may be passed to the {@code -processor} command-line argument: the fully qualified class * name for the checker, or a shorthand for built-in checkers. Using the annotation with no * arguments, as in {@code @AnnotatedFor({})}, has no effect. * diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/ConditionalPostconditionAnnotation.java b/src/java.base/share/classes/org/checkerframework/framework/qual/ConditionalPostconditionAnnotation.java index 0a158a141c1f..c3746fa8726c 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/ConditionalPostconditionAnnotation.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/ConditionalPostconditionAnnotation.java @@ -31,7 +31,7 @@ * names may be used in E and P, if a {@link QualifierArgument} in E gives the name of the * corresponding element in P. * - *

        For example, the following code declares a postcondition annotation for the {@link + *

        For example, the following code declares a conditional postcondition annotation for the {@link * org.checkerframework.common.value.qual.MinLen} qualifier: * *

        
        @@ -45,11 +45,11 @@
          * }
          * 
        * - * The {@code expression} element holds the expressions to which the qualifier applies and {@code + * The {@code expression} element holds the expressions to which the qualifier applies, and {@code * targetValue} holds the value for the {@code value} argument of {@link * org.checkerframework.common.value.qual.MinLen}. * - *

        The following code then uses the annotation on a method that ensures {@code field} to be + *

        The following code then uses the annotation on a method that ensures that {@code field} is * {@code @MinLen(4)} upon returning {@code true}. * *

        
        diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/Covariant.java b/src/java.base/share/classes/org/checkerframework/framework/qual/Covariant.java
        index 1599464f6ac3..dd284a0c8a5e 100644
        --- a/src/java.base/share/classes/org/checkerframework/framework/qual/Covariant.java
        +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/Covariant.java
        @@ -10,10 +10,10 @@
          * A marker annotation, written on a class declaration, that signifies that one or more of the
          * class's type parameters can be treated covariantly. For example, if {@code MyClass} has a single
          * type parameter that is treated covariantly, and if {@code B} is a subtype of {@code A}, then
        - * {@code SomeClass} is a subtype of {@code SomeClass}.
        + * {@code MyClass} is a subtype of {@code MyClass}.
          *
        - * 

        Ordinarily, Java treats type parameters invariantly: {@code SomeClass} is unrelated to - * (neither a subtype nor a supertype of) {@code SomeClass}. + *

        Ordinarily, Java treats type parameters invariantly: {@code MyClass} is unrelated to + * (neither a subtype nor a supertype of) {@code MyClass}. * *

        It is only safe to mark a type parameter as covariant if clients use the type parameter in a * read-only way: clients read values of that type but never modify them. diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/DefaultFor.java b/src/java.base/share/classes/org/checkerframework/framework/qual/DefaultFor.java index f1c0fde504dc..5744a17cf44e 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/DefaultFor.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/DefaultFor.java @@ -42,7 +42,7 @@ /** * Returns {@link Class}es for which an annotation should be applied. For example, if - * {@code @MyAnno} is meta-annotated with {@code @DefaultFor(classes=String.class)}, then every + * {@code @MyAnno} is meta-annotated with {@code @DefaultFor(types=String.class)}, then every * occurrence of {@code String} is actually {@code @MyAnno String}. * *

        Only the given types, not their subtypes, receive the default. For instance, if the {@code diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/DoesNotUnrefineReceiver.java b/src/java.base/share/classes/org/checkerframework/framework/qual/DoesNotUnrefineReceiver.java index 33e94710d019..10e7b9de7f5d 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/DoesNotUnrefineReceiver.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/DoesNotUnrefineReceiver.java @@ -24,7 +24,7 @@ * The name of the checker(s) that this annotation affects. For example, "modifiability" or * "nullness". Use "allcheckers" to affect all checkers. * - * @return the name of the checker that this annotation affects + * @return the names of the checkers that this annotation affects */ String[] value(); } diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/IgnoreInWholeProgramInference.java b/src/java.base/share/classes/org/checkerframework/framework/qual/IgnoreInWholeProgramInference.java index 588bf67a145b..c00e52272ef0 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/IgnoreInWholeProgramInference.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/IgnoreInWholeProgramInference.java @@ -17,7 +17,7 @@ * then no type qualifier will be inferred for any field annotated by {@code @Inject}. * *

        This is appropriate for fields that are set reflectively, so there are no calls in client code - * that type inference can learn from. Examples of qualifiers that should be meta-annotated with + * that type inference can learn from. Examples of annotations that should be meta-annotated with * {@code @IgnoreInWholeProgramInference} include {@code @Inject}, {@code @Singleton}, @@ -28,7 +28,7 @@ * annotates. * *

        See - * org.checkerframework.common.wholeprograminference.WholeProgramInferenceScenes#updateFromFieldAssignment + * org.checkerframework.common.wholeprograminference.WholeProgramInferenceScenes#updateFromFieldAssignment. * * @checker_framework.manual #whole-program-inference-ignores-some-code Whole-program inference * ignores some code diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/InheritedAnnotation.java b/src/java.base/share/classes/org/checkerframework/framework/qual/InheritedAnnotation.java index 7f82c0176e03..0f344439a65f 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/InheritedAnnotation.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/InheritedAnnotation.java @@ -12,7 +12,7 @@ * meta-annotation causes the declaration annotation to be inherited even if it is used to annotate * something other than a class. * - *

        If an annotation type is meta-annotation with {@link java.lang.annotation.Inherited}, that + *

        If an annotation type is meta-annotated with {@link java.lang.annotation.Inherited}, that * annotation will only be inherited when written on a class. */ @Documented diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/InvisibleQualifier.java b/src/java.base/share/classes/org/checkerframework/framework/qual/InvisibleQualifier.java index 148b0c98831d..45b0f3684c8d 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/InvisibleQualifier.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/InvisibleQualifier.java @@ -15,13 +15,13 @@ * and default ones (which far outnumber the ones explicitly written by the programmer). Being * explicit helps users understand the effective annotations, which the Checker Framework operates * upon. However, the output can be verbose, and it can show annotations that a user should not - * write. For example, a Format String Checker warning message might contain + * write. For example, a Compiler Message Key Checker warning message might contain * "{@code @UnknownCompilerMessageKey Map<@CompilerMessageKey String, @UnknownCompilerMessageKey * String>}". * *

        When an annotation is meta-annotated with {@code @InvisibleQualifier}, then the Checker * Framework does not print the given qualifier when printing types. For the above example, the - * Format String Checker prints "{@code Map<@CompilerMessageKey String, String>}". + * Compiler Message Key Checker prints "{@code Map<@CompilerMessageKey String, String>}". * *

        If a user runs the Checker Framework with command-line argument {@code -AprintAllQualifiers}, * then invisible qualifiers are output. diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/LiteralKind.java b/src/java.base/share/classes/org/checkerframework/framework/qual/LiteralKind.java index 7b3b291007b6..688d7b74b0ad 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/LiteralKind.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/LiteralKind.java @@ -8,8 +8,8 @@ * Specifies kinds of literal trees. * *

        These correspond to the *_LITERAL constants in {@link com.sun.source.tree.Tree.Kind}. However, - * that enum is in the tools.jar which is not on the user's classpath by default. This enum is used - * by meta-annotations, such as {@link QualifierForLiterals}, instead. + * that enum is in tools.jar, which is not on the user's classpath by default. This enum is used by + * meta-annotations, such as {@link QualifierForLiterals}, instead. */ // https://docs.oracle.com/javase/8/docs/technotes/tools/findingclasses.html#bootclass public enum LiteralKind { diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/MonotonicQualifier.java b/src/java.base/share/classes/org/checkerframework/framework/qual/MonotonicQualifier.java index 73b2fde809aa..948683516d83 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/MonotonicQualifier.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/MonotonicQualifier.java @@ -8,13 +8,13 @@ import java.lang.annotation.Target; /** - * A meta-annotation that indicates that a qualifier indicates that an expression goes monotonically + * A meta-annotation that indicates that a qualifier signifies that an expression goes monotonically * from a type qualifier {@code T} to another qualifier {@code S}. The annotation {@code S} is * called the target of the monotonic qualifier, and has to be indicated by {@link * MonotonicQualifier#value()}. * *

        This meta-annotation can be used on the declaration of the monotonic qualifier used for the - * type-system at hand, and is often called {@code MonoT} if the target is {@code T}. The subtyping + * type system at hand, and is often called {@code MonoT} if the target is {@code T}. The subtyping * hierarchy has to be defined as follows: * *

        {@code
        @@ -23,8 +23,8 @@
          *
          * where {@code <:} indicates the subtyping relation.
          *
        - * 

        An expression of a monotonic type can only be assigned expressions of the target type {@code - * T}. This means that an expression of the monotonic type {@code MonoT} cannot be assigned to a + *

        A variable of a monotonic type can only be assigned expressions of the target type {@code T}. + * This means that an expression of the monotonic type {@code MonoT} cannot be assigned to a * variable of the same type. * *

        Reading an expression of a monotonic type {@code MonoT} might always yield an expression of @@ -32,7 +32,7 @@ * the monotonic property ensures that it will stay of type {@code T} for the rest of the program * execution. This is even true if arbitrary other code is executed. * - *

        Note that variables of a monotonic type can be re-assigned arbitrarily often, but only with + *

        Note that variables of a monotonic type can be reassigned arbitrarily often, but only with * expressions of the target type. */ @Documented diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/PostconditionAnnotation.java b/src/java.base/share/classes/org/checkerframework/framework/qual/PostconditionAnnotation.java index 2017eccd81f3..ab8f40b3f15f 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/PostconditionAnnotation.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/PostconditionAnnotation.java @@ -37,13 +37,14 @@ * String[] value(); * {@literal @}QualifierArgument("value") * int targetValue() default 0; + * } *

        * - * The {@code value} element holds the expressions to which the qualifier applies and {@code + * The {@code value} element holds the expressions to which the qualifier applies, and {@code * targetValue} holds the value for the {@code value} argument of {@link * org.checkerframework.common.value.qual.MinLen}. * - *

        The following code then uses the annotation on a method that ensures {@code field} to be + *

        The following code then uses the annotation on a method that ensures that {@code field} is * {@code @MinLen(2)} upon return. * *

        
        diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/PreconditionAnnotation.java b/src/java.base/share/classes/org/checkerframework/framework/qual/PreconditionAnnotation.java
        index ce2df1af281b..33b7043cc058 100644
        --- a/src/java.base/share/classes/org/checkerframework/framework/qual/PreconditionAnnotation.java
        +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/PreconditionAnnotation.java
        @@ -33,9 +33,10 @@
          *    String[] value();
          *    {@literal @}QualifierArgument("value")
          *    int targetValue() default 0;
        + *  }
          * 
        * - * The {@code value} element holds the expressions to which the qualifier applies and {@code + * The {@code value} element holds the expressions to which the qualifier applies, and {@code * targetValue} holds the value for the {@code value} argument of {@link * org.checkerframework.common.value.qual.MinLen}. * diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/QualifierArgument.java b/src/java.base/share/classes/org/checkerframework/framework/qual/QualifierArgument.java index e56a2800ccc9..30da309d7949 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/QualifierArgument.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/QualifierArgument.java @@ -14,7 +14,7 @@ * taking the arguments. * *

        For example, the following code declares a postcondition annotation for the {@link - * org.checkerframework.common.value.qual.MinLen} qualifier, allowing to specify its value: + * org.checkerframework.common.value.qual.MinLen} qualifier, allowing its value to be specified: * *

        
          * {@literal @}PostconditionAnnotation(qualifier = MinLen.class)
        @@ -23,9 +23,10 @@
          *   String[] value();
          *   {@literal @}QualifierArgument("value")
          *   int targetValue() default 0;
        + * }
          * 
        * - * The {@code value} element holds the expressions to which the qualifier applies and {@code + * The {@code value} element holds the expressions to which the qualifier applies, and {@code * targetValue} holds the value for the {@code value} argument of {@link * org.checkerframework.common.value.qual.MinLen}. * @@ -38,7 +39,7 @@ @Target(ElementType.METHOD) public @interface QualifierArgument { /** - * Specifies the name of the argument of the qualifier, that is passed the values held in the + * Specifies the name of the argument of the qualifier that is passed the values held in the * annotated element. If the value is omitted or is empty, then the name of the annotated element * is used as the argument name. */ diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/QualifierForLiterals.java b/src/java.base/share/classes/org/checkerframework/framework/qual/QualifierForLiterals.java index 72cf42edf453..46dd0a22e4b8 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/QualifierForLiterals.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/QualifierForLiterals.java @@ -9,7 +9,7 @@ /** * A meta-annotation that indicates what qualifier should be given to literals. * {@code @QualifierForLiterals} (without any annotation elements) is equivalent to - * {@code @QualifierForLiterals(LiteralKind.ALL)} + * {@code @QualifierForLiterals(LiteralKind.ALL)}. */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/RequiresQualifier.java b/src/java.base/share/classes/org/checkerframework/framework/qual/RequiresQualifier.java index 5b6bbab76628..b502bf1866d8 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/RequiresQualifier.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/RequiresQualifier.java @@ -11,7 +11,7 @@ /** * A precondition annotation to indicate that a method requires certain expressions to have a * certain qualifier at the time of the call to the method. The expressions for which the annotation - * must hold after the method's execution are indicated by {@code expression} and are specified + * must hold before the method's execution are indicated by {@code expression} and are specified * using a string. The qualifier is specified by {@code qualifier}. * * @checker_framework.manual #java-expressions-as-arguments Syntax of Java expressions @@ -22,9 +22,9 @@ @Repeatable(RequiresQualifier.List.class) public @interface RequiresQualifier { /** - * Returns the Java expressions for which the annotation need to be present. + * Returns the Java expressions for which the annotation needs to be present. * - * @return the Java expressions for which the annotation need to be present + * @return the Java expressions for which the annotation needs to be present * @checker_framework.manual #java-expressions-as-arguments Syntax of Java expressions */ String[] expression(); diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/TargetLocations.java b/src/java.base/share/classes/org/checkerframework/framework/qual/TargetLocations.java index 704566ab8ce7..e432c38964f5 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/TargetLocations.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/TargetLocations.java @@ -18,7 +18,7 @@ * the qualifier can be written on any type use. * *

        This enables a type system designer to permit a qualifier to be written only in certain - * locations. For example, some type systems' top and bottom qualifier (such as {@link + * locations. For example, some type systems' top and bottom qualifiers (such as {@link * org.checkerframework.checker.nullness.qual.KeyForBottom}) should only be written on an explicit * wildcard upper or lower bound. This meta-annotation is a declarative, coarse-grained approach to * enable that. For finer-grained control, override {@code visit*} methods that visit trees in diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/TypeUseLocation.java b/src/java.base/share/classes/org/checkerframework/framework/qual/TypeUseLocation.java index 53039f19960b..387ddc667905 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/TypeUseLocation.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/TypeUseLocation.java @@ -42,14 +42,14 @@ public enum TypeUseLocation { CONSTRUCTOR_RESULT, /** - * Apply default annotations to unannotated lower bounds for type variables and wildcards both - * explicit ones in {@code extends} clauses, and implicit upper bounds when no explicit {@code + * Apply default annotations to unannotated lower bounds for type variables and wildcards: both + * explicit ones in {@code super} clauses, and implicit lower bounds when no explicit {@code * extends} or {@code super} clause is present. */ LOWER_BOUND, /** - * Apply default annotations to unannotated, but explicit lower bounds: {@code } + * Apply default annotations to unannotated, but explicit lower bounds: {@code }. */ EXPLICIT_LOWER_BOUND, @@ -63,7 +63,7 @@ public enum TypeUseLocation { * clauses, and implicit upper bounds when no explicit {@code extends} or {@code super} clause is * present. * - *

        Especially useful for parametrized classes that provide a lot of static methods with the + *

        Especially useful for parameterized classes that provide a lot of static methods with the * same generic parameters as the class. */ UPPER_BOUND, diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/Unused.java b/src/java.base/share/classes/org/checkerframework/framework/qual/Unused.java index e431e76dc328..43f1a13f0d66 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/Unused.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/Unused.java @@ -14,7 +14,7 @@ *

        This property is verified by the checker that type-checks the {@code when} element value * qualifier. * - *

        Example Consider a class, {@code Table}, with a locking field, {@code lock}. The lock + *

        Example: Consider a class, {@code Table}, with a locking field, {@code lock}. The lock * is used when a {@code Table} instance is shared across threads. When running in a local thread, * the {@code lock} field ought not to be used. * diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/UpperBoundFor.java b/src/java.base/share/classes/org/checkerframework/framework/qual/UpperBoundFor.java index f1f334242e74..22dbca461dd6 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/UpperBoundFor.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/UpperBoundFor.java @@ -18,7 +18,7 @@ * An example is the declaration * *

        
        - * {@literal @}DefaultFor(classes=String.class)
        + * {@literal @}UpperBoundFor(types=String.class)
          * {@literal @}interface MyAnno {}
          * 
        * diff --git a/src/java.base/share/classes/org/checkerframework/framework/qual/package-info.java b/src/java.base/share/classes/org/checkerframework/framework/qual/package-info.java index 8fc168e53856..fb6ab94b5ed2 100644 --- a/src/java.base/share/classes/org/checkerframework/framework/qual/package-info.java +++ b/src/java.base/share/classes/org/checkerframework/framework/qual/package-info.java @@ -2,8 +2,8 @@ * Contains the basic annotations to be used by all type systems and meta-annotations to qualify * annotations (qualifiers). * - *

        They may serve as documentation for the type qualifiers, and aid the Checker Framework to - * infer the relations between the type qualifiers. + *

        They may serve as documentation for the type qualifiers, and aid the Checker Framework in + * inferring the relations between the type qualifiers. * * @checker_framework.manual #creating-a-checker Writing a checker */ diff --git a/src/java.base/unix/classes/java/lang/ProcessEnvironment.java b/src/java.base/unix/classes/java/lang/ProcessEnvironment.java index a7502015f3c9..7ee4804b67c4 100644 --- a/src/java.base/unix/classes/java/lang/ProcessEnvironment.java +++ b/src/java.base/unix/classes/java/lang/ProcessEnvironment.java @@ -56,7 +56,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.dataflow.qual.Pure; -// import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.dataflow.qual.SideEffectsOnly; import java.util.*; @@ -345,7 +345,7 @@ public Iterator> iterator() { Iterator> i = s.iterator(); @Pure public boolean hasNext() {return i.hasNext();} - // // @SideEffectsOnly("this") + @SideEffectsOnly("this") public Map.Entry next() { return new StringEntry(i.next()); } @@ -392,7 +392,7 @@ public Iterator iterator() { Iterator i = c.iterator(); @Pure public boolean hasNext() {return i.hasNext();} - // // @SideEffectsOnly("this") + @SideEffectsOnly("this") public String next() {return i.next().toString();} public void remove() {i.remove();} }; @@ -424,7 +424,7 @@ public Iterator iterator() { Iterator i = s.iterator(); @Pure public boolean hasNext() {return i.hasNext();} - // // @SideEffectsOnly("this") + @SideEffectsOnly("this") public String next() {return i.next().toString();} public void remove() { i.remove();} }; diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixDirectoryStream.java b/src/java.base/unix/classes/sun/nio/fs/UnixDirectoryStream.java index bad17d050f46..4ef0d19fc3c1 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixDirectoryStream.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixDirectoryStream.java @@ -26,7 +26,7 @@ package sun.nio.fs; import org.checkerframework.dataflow.qual.Pure; -// import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.dataflow.qual.SideEffectsOnly; import java.nio.file.*; import java.util.Iterator; @@ -204,7 +204,7 @@ public synchronized boolean hasNext() { } @Override - // @SideEffectsOnly("this") + @SideEffectsOnly("this") public synchronized Path next() { Path result; if (nextEntry == null && !atEof) { diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixFileSystem.java b/src/java.base/unix/classes/sun/nio/fs/UnixFileSystem.java index 94b6e46d217b..6ddc73690aa4 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixFileSystem.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixFileSystem.java @@ -26,7 +26,7 @@ package sun.nio.fs; import org.checkerframework.dataflow.qual.Pure; -// import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.dataflow.qual.SideEffectsOnly; import java.nio.ByteBuffer; import java.nio.file.AtomicMoveNotSupportedException; @@ -259,7 +259,7 @@ public synchronized boolean hasNext() { } @Override - // @SideEffectsOnly("this") + @SideEffectsOnly("this") public synchronized FileStore next() { if (next == null) next = readNext(); diff --git a/src/java.base/windows/classes/java/lang/ProcessEnvironment.java b/src/java.base/windows/classes/java/lang/ProcessEnvironment.java index e70c0e1235ba..3f620c4a1644 100644 --- a/src/java.base/windows/classes/java/lang/ProcessEnvironment.java +++ b/src/java.base/windows/classes/java/lang/ProcessEnvironment.java @@ -65,7 +65,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.dataflow.qual.Pure; -// import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.dataflow.qual.SideEffectsOnly; import java.util.*; @@ -147,7 +147,7 @@ public Iterator> iterator() { Iterator> i = s.iterator(); @Pure public boolean hasNext() { return i.hasNext();} - // @SideEffectsOnly("this") + @SideEffectsOnly("this") public Map.Entry next() { return new CheckedEntry(i.next()); } diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsDirectoryStream.java b/src/java.base/windows/classes/sun/nio/fs/WindowsDirectoryStream.java index f9c17db80cb5..0916f4587990 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsDirectoryStream.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsDirectoryStream.java @@ -26,7 +26,7 @@ package sun.nio.fs; import org.checkerframework.dataflow.qual.Pure; -// import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.dataflow.qual.SideEffectsOnly; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; @@ -215,7 +215,7 @@ public synchronized boolean hasNext() { } @Override - // @SideEffectsOnly("this") + @SideEffectsOnly("this") public synchronized Path next() { Path result = null; if (nextEntry == null && !atEof) { diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystem.java b/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystem.java index 30018a9e05b9..faf07b454b9c 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystem.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystem.java @@ -26,7 +26,7 @@ package sun.nio.fs; import org.checkerframework.dataflow.qual.Pure; -// import org.checkerframework.dataflow.qual.SideEffectsOnly; +import org.checkerframework.dataflow.qual.SideEffectsOnly; import java.nio.file.*; import java.nio.file.attribute.*; @@ -170,7 +170,7 @@ public synchronized boolean hasNext() { } @Override - // @SideEffectsOnly("this") + @SideEffectsOnly("this") public synchronized FileStore next() { if (next == null) next = readNext(); diff --git a/src/java.desktop/share/classes/javax/swing/DefaultListModel.java b/src/java.desktop/share/classes/javax/swing/DefaultListModel.java index 9d519927b677..059d0463d2d7 100644 --- a/src/java.desktop/share/classes/javax/swing/DefaultListModel.java +++ b/src/java.desktop/share/classes/javax/swing/DefaultListModel.java @@ -25,6 +25,8 @@ package javax.swing; +import org.checkerframework.dataflow.qual.SideEffectFree; + import java.util.Vector; import java.util.Collection; import java.util.Enumeration; @@ -268,6 +270,7 @@ public int lastIndexOf(Object elem, int index) { * @see #get(int) * @see Vector#elementAt(int) */ + @SideEffectFree public E elementAt(int index) { return delegate.elementAt(index); } diff --git a/test/jdk/java/util/SequencedCollection/SimpleDeque.java b/test/jdk/java/util/SequencedCollection/SimpleDeque.java index c1ed07210fba..4d2e3fa6dc91 100644 --- a/test/jdk/java/util/SequencedCollection/SimpleDeque.java +++ b/test/jdk/java/util/SequencedCollection/SimpleDeque.java @@ -22,6 +22,7 @@ */ import java.util.*; +import org.checkerframework.dataflow.qual.SideEffectFree; /** * A complete Deque implementation that inherits the reversed() method @@ -178,6 +179,7 @@ public E poll() { return deque.poll(); } + @SideEffectFree public E element() { return deque.element(); }