From ba27da4db259c0676b67ad70fb705d061be3d673 Mon Sep 17 00:00:00 2001 From: Chris Povirk Date: Mon, 7 Oct 2024 10:57:10 -0400 Subject: [PATCH 1/3] Try adding the JSpecify annotations to see if it helps with CI. https://github.com/jspecify/jdk/issues/99 --- src/java.base/share/classes/module-info.java | 1 + .../org/jspecify/annotations/NonNull.java | 96 ++++++++++++ .../org/jspecify/annotations/NullMarked.java | 112 ++++++++++++++ .../jspecify/annotations/NullUnmarked.java | 82 ++++++++++ .../org/jspecify/annotations/Nullable.java | 141 ++++++++++++++++++ .../jspecify/annotations/package-info.java | 55 +++++++ 6 files changed, 487 insertions(+) create mode 100644 src/java.base/share/classes/org/jspecify/annotations/NonNull.java create mode 100644 src/java.base/share/classes/org/jspecify/annotations/NullMarked.java create mode 100644 src/java.base/share/classes/org/jspecify/annotations/NullUnmarked.java create mode 100644 src/java.base/share/classes/org/jspecify/annotations/Nullable.java create mode 100644 src/java.base/share/classes/org/jspecify/annotations/package-info.java diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 4e43245e520..65c11cd5fad 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -135,6 +135,7 @@ exports javax.security.auth.spi; exports javax.security.auth.x500; exports javax.security.cert; + exports org.jspecify.annotations; // additional qualified exports may be inserted at build time diff --git a/src/java.base/share/classes/org/jspecify/annotations/NonNull.java b/src/java.base/share/classes/org/jspecify/annotations/NonNull.java new file mode 100644 index 00000000000..31bcb076d95 --- /dev/null +++ b/src/java.base/share/classes/org/jspecify/annotations/NonNull.java @@ -0,0 +1,96 @@ +/* + * Copyright 2022 The JSpecify Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jspecify.annotations; + +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Indicates that the annotated type + * usage (commonly a parameter type or return type) is considered to exclude {@code null} + * as a value; rarely needed within {@linkplain NullMarked null-marked} code. + * + *

This annotation serves two primary purposes: + * + *

+ * + *

For a comprehensive introduction to JSpecify, please see jspecify.org. + * + *

Non-null projection

+ * + *

In the following example, {@code MyOptional}'s type parameter {@code T} accepts only non-null + * type arguments, but {@code MyList}'s type parameter {@code E} will accept either a non-null or + * nullable type argument. + * + *

{@code
+ * // All the below is null-marked code
+ *
+ * class MyOptional { … }
+ *
+ * interface MyList {
+ *   // Returns the first non-null element, if such element exists.
+ *   MyOptional firstNonNull() { … } // problem here!
+ * }
+ *
+ * MyList<@Nullable String> maybeNulls = …
+ * MyList nonNulls = …
+ * }
+ * + *

Because {@code MyOptional} accepts only non-null type arguments, we need both {@code + * maybeNulls.firstNonNull()} and {@code nonNulls.firstNonNull()} to produce the same return type: + * {@code MyOptional!} (see notation). + * However, as specified above, they won't do that. In fact, there is a problem with the {@code + * firstNonNull} signature, since the type argument {@code String?} would not meet the requirements + * of {@code MyOptional}'s type parameter. + * + *

The solution is to project the type argument to its non-null counterpart: + * + *

{@code
+ * // Returns the first non-null element, if such element exists.
+ * MyOptional<@NonNull E> firstNonNull() { … } // problem fixed!
+ * }
+ * + *

Here, {@code @NonNull E} selects the non-null form of the type argument, whether it was + * already non-null or not, which is just what we need in this scenario. + * + *

If {@code E} has a non-null upper bound, then the apparent projection {@code @NonNull E} is + * redundant but harmless. + * + *

Nullable projection serves the equivalent purpose in + * the opposite direction, and is far more commonly useful. + * + *

If a type variable has all its usages being projected in one direction or the other, it + * should be given a non-null upper bound, and any non-null projections can then be removed. + * + *

Where it is applicable

+ * + *

{@code @NonNull} is applicable in all the same + * locations as {@link Nullable}. + */ +@Documented +@Target(TYPE_USE) +@Retention(RUNTIME) +public @interface NonNull {} diff --git a/src/java.base/share/classes/org/jspecify/annotations/NullMarked.java b/src/java.base/share/classes/org/jspecify/annotations/NullMarked.java new file mode 100644 index 00000000000..44e95104874 --- /dev/null +++ b/src/java.base/share/classes/org/jspecify/annotations/NullMarked.java @@ -0,0 +1,112 @@ +/* + * Copyright 2018-2020 The JSpecify Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jspecify.annotations; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.MODULE; +import static java.lang.annotation.ElementType.PACKAGE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Indicates that the annotated element and the code transitively {@linkplain + * javax.lang.model.element.Element#getEnclosedElements() enclosed} within it are null-marked + * code: there, type usages are generally considered to exclude {@code null} as a value unless + * specified otherwise. Using this annotation avoids the need to write {@link NonNull @NonNull} many + * times throughout your code. + * + *

For a comprehensive introduction to JSpecify, please see jspecify.org. + * + *

Effects of being null-marked

+ * + *

Within null-marked code, as a general rule, a type usage is considered non-null (to + * exclude {@code null} as a value) unless explicitly annotated as {@link Nullable}. However, there + * are several special cases to address. + * + *

Special cases

+ * + *

Within null-marked code: + * + *

+ * + *

Where it can be used

+ * + * {@code @NullMarked} and {@link NullUnmarked @NullUnmarked} can be used on any package, class, + * method, or constructor declaration; {@code @NullMarked} can be used on a module declaration as + * well. Special considerations: + * + * + */ +@Documented +@Target({MODULE, PACKAGE, TYPE, METHOD, CONSTRUCTOR}) +@Retention(RUNTIME) +public @interface NullMarked {} diff --git a/src/java.base/share/classes/org/jspecify/annotations/NullUnmarked.java b/src/java.base/share/classes/org/jspecify/annotations/NullUnmarked.java new file mode 100644 index 00000000000..f2da1df9ce6 --- /dev/null +++ b/src/java.base/share/classes/org/jspecify/annotations/NullUnmarked.java @@ -0,0 +1,82 @@ +/* + * Copyright 2022 The JSpecify Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jspecify.annotations; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PACKAGE; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the annotated element and the code transitively {@linkplain + * javax.lang.model.element.Element#getEnclosedElements() enclosed} within it is null-unmarked + * code: there, type usages generally have unspecified nullness unless explicitly + * annotated otherwise. + * + *

This annotation's purpose is to ease migration of a large existing codebase to null-marked + * status. It makes it possible to "flip the default" for new code added to a class or package even + * before that class or package has been fully migrated. Since new code is the most important code + * to analyze, this is strongly recommended as a temporary measure whenever necessary. However, once + * a codebase has been fully migrated it would be appropriate to ban use of this annotation. + * + *

For a comprehensive introduction to JSpecify, please see jspecify.org. + * + *

Null-marked and null-unmarked code

+ * + *

{@link NullMarked} and this annotation work as a pair to include and exclude sections of code + * from null-marked status (respectively). Specifically, code is considered null-marked if the most + * narrowly enclosing element annotated with either of these two annotations exists and is annotated + * with {@code @NullMarked}. + * + *

Otherwise it is considered null-unmarked. This can happen in two ways: either it is more + * narrowly enclosed by a {@code @NullUnmarked}-annotated element than by any + * {@code @NullMarked}-annotated element, or neither annotation is present on any enclosing element. + * No distinction is made between these cases. + * + *

The effects of being null-marked are described in the Effects section of {@code NullMarked}. + * + *

Unspecified nullness

+ * + *

Within null-unmarked code, a type usage with no nullness annotation has unspecified + * nullness (Why?). This means that, while there is always + * some correct way to annotate it for nullness, that information is missing: we do not + * know whether it includes or excludes {@code null} as a value. In such a case, tools can vary + * widely in how strict or lenient their enforcement is, or might make it configurable. + * + *

For more, please see this more comprehensive + * discussion of unspecified nullness. + * + *

There is no way for an individual type usage within null-marked code to have unspecified + * nullness. (Why?) + * + *

Where it can be used

+ * + * The information in the Where it can be used section of {@code + * NullMarked} applies as well to this annotation. + */ +// TODO(kevinb9n): word the middle section better with good words +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({PACKAGE, TYPE, METHOD, CONSTRUCTOR}) +public @interface NullUnmarked {} diff --git a/src/java.base/share/classes/org/jspecify/annotations/Nullable.java b/src/java.base/share/classes/org/jspecify/annotations/Nullable.java new file mode 100644 index 00000000000..a87d30da269 --- /dev/null +++ b/src/java.base/share/classes/org/jspecify/annotations/Nullable.java @@ -0,0 +1,141 @@ +/* + * Copyright 2018-2020 The JSpecify Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jspecify.annotations; + +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Indicates that the annotated type + * usage (commonly a parameter type or return type) is considered to include {@code null} as a + * value. + * + *

Example usages: + * + *

{@code
+ * @Nullable String field;
+ *
+ * @Nullable String getField() { return field; }
+ *
+ * void setField(@Nullable String value) { field = value; }
+ *
+ * List<@Nullable String> getList() { … }
+ * }
+ * + *

For a comprehensive introduction to JSpecify, please see jspecify.org. + * + *

Meaning per each kind of type usage

+ * + *

The essential meaning of this annotation is always the same: the type it annotates is + * considered to include {@code null} as a value. But this may affect your code a little differently + * based on the kind of type usage involved. + * + *

+ * + *

Where it is applicable

+ * + *

This annotation and {@link NonNull} are applicable to any type usage except the + * following cases, where they have no defined meaning: + * + *

+ * + * Whether the code is {@link NullMarked} also has no consequence in the above locations. + * + *

Unannotated type usages

+ * + *

For a type usage where nullness annotations are applicable but + * not present, its nullness depends on whether it appears within {@linkplain NullMarked + * null-marked} code; see that class for details. Note in particular that nullness information from + * a superclass is never automatically "inherited". + */ +@Documented +@Target(TYPE_USE) +@Retention(RUNTIME) +public @interface Nullable {} diff --git a/src/java.base/share/classes/org/jspecify/annotations/package-info.java b/src/java.base/share/classes/org/jspecify/annotations/package-info.java new file mode 100644 index 00000000000..bcc0e4da7e2 --- /dev/null +++ b/src/java.base/share/classes/org/jspecify/annotations/package-info.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 The JSpecify Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * JSpecify annotations. See jspecify.org for general information. + * + *

What's here?

+ * + * This package will contain annotations supporting a variety of static analyses. For now it + * supports just nullness analysis. + * + *

Nullness

+ * + * The primary annotations of interest are {@link NullMarked} and {@link Nullable}. Together they + * provide declarative, use-site nullness for Java types. Less frequently, their negations + * may be useful: {@link NullUnmarked} and {@link NonNull}, respectively. + * + *

For a comprehensive introduction to JSpecify, please see jspecify.org. + * + *

Note on tool behavior

+ * + *

Each of these annotations defines a single meaning shared by all compatible tools (and + * libraries). JSpecify documentation aims to provide unambiguous, tool-independent answers for how + * to properly annotate your APIs in all circumstances. However, tools are permitted to + * respond to the information you provide however they see fit (or not at all). JSpecify + * compatibility does not require that any particular finding must or must not be issued to the + * user, let alone its message or severity. + * + *

In fact, it's important to remember that declarative annotations are merely one source + * of information an analyzer may consult in concluding an expression is safely non-null. Just like + * one analyzer might determine that an {@code int} expression can take on only positive values, + * another might likewise determine that a declaratively nullable expression can take on only + * non-null values. In both cases the declarative knowledge is correct, but the inferred + * knowledge is both correct and more specific. + * + *

On the other end, the tools might even enforce nothing at all. In particular, your + * annotated code (or other code dependent on its annotated APIs) might be compiled and run without + * any appropriate tool even in use. Therefore adopting JSpecify annotations is not a replacement + * for explicitly checking arguments at runtime. + */ +package org.jspecify.annotations; From 13ae048987eddb6f1de3527ed606320293649815 Mon Sep 17 00:00:00 2001 From: Chris Povirk Date: Mon, 7 Oct 2024 11:00:26 -0400 Subject: [PATCH 2/3] Trim list of platforms. --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d5958853701..057176fba55 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,7 +36,7 @@ on: platforms: description: 'Platform(s) to execute on (comma separated, e.g. "linux-x64, macos, aarch64")' required: true - default: 'linux-x64, linux-x86-hs, linux-x64-variants, linux-cross-compile, alpine-linux-x64, macos-x64, macos-aarch64, windows-x64, windows-aarch64, docs' + default: 'linux-x64' configure-arguments: description: 'Additional configure arguments' required: false @@ -153,6 +153,7 @@ jobs: uses: ./.github/workflows/build-linux.yml with: platform: linux-x64 + debug-levels: '[ "debug" ]' gcc-major-version: '10' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} From c62e6d0b41b2d2228536253c508516b5fcf2d7fb Mon Sep 17 00:00:00 2001 From: Chris Povirk Date: Mon, 7 Oct 2024 11:09:03 -0400 Subject: [PATCH 3/3] Try --disable-jvm-feature-jvmti. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 057176fba55..3366885ee58 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -155,7 +155,7 @@ jobs: platform: linux-x64 debug-levels: '[ "debug" ]' gcc-major-version: '10' - configure-arguments: ${{ github.event.inputs.configure-arguments }} + configure-arguments: ${{ github.event.inputs.configure-arguments }} --disable-jvm-feature-jvmti make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.select.outputs.linux-x64 == 'true'