Introduce new flag `--incompatible_string_join_requires_strings`

https://github.com/bazelbuild/bazel/issues/7802

RELNOTES:
  A new flag `--incompatible_string_join_requires_strings` is introduced. The sequence argument of `string.join` must contain only string elements.
PiperOrigin-RevId: 240775005
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java
index 57d1324..07a6e50 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java
@@ -326,6 +326,21 @@
   public boolean incompatibleDisallowNativeInBuildFile;
 
   @Option(
+      name = "incompatible_string_join_requires_strings",
+      defaultValue = "false",
+      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
+      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
+      metadataTags = {
+        OptionMetadataTag.INCOMPATIBLE_CHANGE,
+        OptionMetadataTag.TRIGGERED_BY_ALL_INCOMPATIBLE_CHANGES
+      },
+      help =
+          "If set to true, the argument of `string.join` must be an iterable whose elements are "
+              + "strings. If set to false, elements are first converted to string. "
+              + "See https://github.com/bazelbuild/bazel/issues/7802")
+  public boolean incompatibleStringJoinRequiresStrings;
+
+  @Option(
       name = "incompatible_disallow_struct_provider_syntax",
       defaultValue = "false",
       documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
@@ -552,6 +567,7 @@
         .incompatibleNoTransitiveLoads(incompatibleNoTransitiveLoads)
         .incompatibleRemapMainRepo(incompatibleRemapMainRepo)
         .incompatibleRemoveNativeMavenJar(incompatibleRemoveNativeMavenJar)
+        .incompatibleStringJoinRequiresStrings(incompatibleStringJoinRequiresStrings)
         .incompatibleUseToolchainProvidersInJavaCommon(
             incompatibleUseToolchainProvidersInJavaCommon)
         .internalSkylarkFlagTestCanary(internalSkylarkFlagTestCanary)
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java
index cf03d59..4a84325 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java
@@ -174,6 +174,8 @@
 
   public abstract boolean incompatibleRemoveNativeMavenJar();
 
+  public abstract boolean incompatibleStringJoinRequiresStrings();
+
   public abstract boolean internalSkylarkFlagTestCanary();
 
   public abstract boolean incompatibleUseToolchainProvidersInJavaCommon();
@@ -227,6 +229,7 @@
           .incompatibleNoTransitiveLoads(true)
           .incompatibleRemapMainRepo(false)
           .incompatibleRemoveNativeMavenJar(false)
+          .incompatibleStringJoinRequiresStrings(false)
           .internalSkylarkFlagTestCanary(false)
           .incompatibleDoNotSplitLinkingCmdline(false)
           .build();
@@ -298,6 +301,8 @@
 
     public abstract Builder incompatibleRemoveNativeMavenJar(boolean value);
 
+    public abstract Builder incompatibleStringJoinRequiresStrings(boolean value);
+
     public abstract Builder incompatibleUseToolchainProvidersInJavaCommon(boolean value);
 
     public abstract Builder internalSkylarkFlagTestCanary(boolean value);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java b/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java
index 7390fa8..d16eb5f 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java
@@ -99,10 +99,25 @@
               + "</pre>",
       parameters = {
         @Param(name = "self", type = String.class),
-        @Param(name = "elements", legacyNamed = true, type = SkylarkList.class,
+        @Param(
+            name = "elements",
+            legacyNamed = true,
+            type = SkylarkList.class,
             doc = "The objects to join.")
-      })
-  public String join(String self, SkylarkList<?> elements) throws ConversionException {
+      },
+      useLocation = true,
+      useEnvironment = true)
+  public String join(String self, SkylarkList<?> elements, Location loc, Environment env)
+      throws ConversionException, EvalException {
+    if (env.getSemantics().incompatibleStringJoinRequiresStrings()) {
+      for (Object item : elements) {
+        if (!(item instanceof String)) {
+          throw new EvalException(
+              loc,
+              "sequence element must be a string (got '" + EvalUtils.getDataTypeName(item) + "')");
+        }
+      }
+    }
     return Joiner.on(self).join(elements);
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
index ebe9004..91ab07b 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
@@ -155,6 +155,7 @@
         "--incompatible_no_transitive_loads=" + rand.nextBoolean(),
         "--incompatible_remap_main_repo=" + rand.nextBoolean(),
         "--incompatible_remove_native_maven_jar=" + rand.nextBoolean(),
+        "--incompatible_string_join_requires_strings=" + rand.nextBoolean(),
         "--incompatible_use_toolchain_providers_in_java_common=" + rand.nextBoolean(),
         "--internal_skylark_flag_test_canary=" + rand.nextBoolean(),
         "--incompatible_do_not_split_linking_cmdline=" + rand.nextBoolean());
@@ -200,6 +201,7 @@
         .incompatibleNoTransitiveLoads(rand.nextBoolean())
         .incompatibleRemapMainRepo(rand.nextBoolean())
         .incompatibleRemoveNativeMavenJar(rand.nextBoolean())
+        .incompatibleStringJoinRequiresStrings(rand.nextBoolean())
         .incompatibleUseToolchainProvidersInJavaCommon(rand.nextBoolean())
         .internalSkylarkFlagTestCanary(rand.nextBoolean())
         .incompatibleDoNotSplitLinkingCmdline(rand.nextBoolean())
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
index af019ff..12d3287 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
@@ -754,4 +754,17 @@
                 + "for call to method join(elements) of 'string'",
             "','.join(elements=['foo', 'bar'])");
   }
+
+  @Test
+  public void testStringJoinRequiresStrings() throws Exception {
+    new SkylarkTest("--incompatible_string_join_requires_strings")
+        .testIfErrorContains(
+            "sequence element must be a string (got 'int')", "', '.join(['foo', 2])");
+  }
+
+  @Test
+  public void testStringJoinDoesNotRequireStrings() throws Exception {
+    new SkylarkTest("--incompatible_string_join_requires_strings=false")
+        .testEval("', '.join(['foo', 2])", "'foo, 2'");
+  }
 }