Revert special handling of yanked module files

This effectively reverts most of 2a2a4743ad624f7f238b7771b8ceedb7649e8f5c: Module files of yanked versions are evaluated just like those of any other versions and "yankedness" is only checked for the final dep graph after selection.

This greatly simplifies incremental fetching of (inherently mutable) yanked version information with the new lockfile format.

Work towards #20369

RELNOTES: `print` statements in module files are now only executed for the root module and modules subject to non-registry overrides (e.g. `local_path_override`).

Closes #22083.

PiperOrigin-RevId: 627953972
Change-Id: Ie0aba02d187e000450a89ad2cd281c173582880a
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java
index b0f84e6..9f121a6 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java
@@ -15,7 +15,9 @@
 
 package com.google.devtools.build.lib.bazel.bzlmod;
 
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import static com.google.devtools.build.lib.bazel.bzlmod.YankedVersionsUtil.BZLMOD_ALLOWED_YANKED_VERSIONS_ENV;
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
@@ -35,6 +37,8 @@
 import com.google.devtools.build.lib.profiler.ProfilerTask;
 import com.google.devtools.build.lib.profiler.SilentCloseable;
 import com.google.devtools.build.lib.server.FailureDetails.ExternalDeps.Code;
+import com.google.devtools.build.lib.skyframe.ClientEnvironmentFunction;
+import com.google.devtools.build.lib.skyframe.ClientEnvironmentValue;
 import com.google.devtools.build.lib.skyframe.PrecomputedValue.Precomputed;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionException;
@@ -44,6 +48,7 @@
 import com.google.devtools.build.skyframe.SkyframeLookupResult;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import javax.annotation.Nullable;
 
@@ -62,6 +67,23 @@
   @Nullable
   public SkyValue compute(SkyKey skyKey, Environment env)
       throws BazelModuleResolutionFunctionException, InterruptedException {
+    ClientEnvironmentValue allowedYankedVersionsFromEnv =
+        (ClientEnvironmentValue)
+            env.getValue(ClientEnvironmentFunction.key(BZLMOD_ALLOWED_YANKED_VERSIONS_ENV));
+    if (allowedYankedVersionsFromEnv == null) {
+      return null;
+    }
+
+    Optional<ImmutableSet<ModuleKey>> allowedYankedVersions;
+    try {
+      allowedYankedVersions =
+          YankedVersionsUtil.parseAllowedYankedVersions(
+              allowedYankedVersionsFromEnv.getValue(),
+              Objects.requireNonNull(YankedVersionsUtil.ALLOWED_YANKED_VERSIONS.get(env)));
+    } catch (ExternalDepsException e) {
+      throw new BazelModuleResolutionFunctionException(e, Transience.PERSISTENT);
+    }
+
     RootModuleFileValue root =
         (RootModuleFileValue) env.getValue(ModuleFileValue.KEY_FOR_ROOT_MODULE);
     if (root == null) {
@@ -70,7 +92,7 @@
 
     var state = env.getState(ModuleResolutionComputeState::new);
     if (state.selectionResult == null) {
-      state.selectionResult = discoverAndSelect(env, root);
+      state.selectionResult = discoverAndSelect(env, root, allowedYankedVersions);
       if (state.selectionResult == null) {
         return null;
       }
@@ -108,7 +130,10 @@
   }
 
   @Nullable
-  private static Selection.Result discoverAndSelect(Environment env, RootModuleFileValue root)
+  private static Selection.Result discoverAndSelect(
+      Environment env,
+      RootModuleFileValue root,
+      Optional<ImmutableSet<ModuleKey>> allowedYankedVersions)
       throws BazelModuleResolutionFunctionException, InterruptedException {
     ImmutableMap<ModuleKey, InterimModule> initialDepGraph;
     try (SilentCloseable c = Profiler.instance().profile(ProfilerTask.BZLMOD, "discovery")) {
@@ -130,6 +155,24 @@
     }
     ImmutableMap<ModuleKey, InterimModule> resolvedDepGraph = selectionResult.getResolvedDepGraph();
 
+    var yankedVersionsKeys =
+        resolvedDepGraph.values().stream()
+            .filter(m -> m.getRegistry() != null)
+            .map(m -> YankedVersionsValue.Key.create(m.getName(), m.getRegistry().getUrl()))
+            .collect(toImmutableSet());
+    SkyframeLookupResult yankedVersionsResult = env.getValuesAndExceptions(yankedVersionsKeys);
+    if (env.valuesMissing()) {
+      return null;
+    }
+    var yankedVersionValues =
+        yankedVersionsKeys.stream()
+            .collect(
+                toImmutableMap(
+                    key -> key, key -> (YankedVersionsValue) yankedVersionsResult.get(key)));
+    if (yankedVersionValues.values().stream().anyMatch(Objects::isNull)) {
+      return null;
+    }
+
     try (SilentCloseable c =
         Profiler.instance().profile(ProfilerTask.BZLMOD, "verify root module direct deps")) {
       verifyRootModuleDirectDepsAreAccurate(
@@ -149,7 +192,7 @@
 
     try (SilentCloseable c =
         Profiler.instance().profile(ProfilerTask.BZLMOD, "check no yanked versions")) {
-      checkNoYankedVersions(resolvedDepGraph);
+      checkNoYankedVersions(resolvedDepGraph, yankedVersionValues, allowedYankedVersions);
     }
 
     return selectionResult;
@@ -246,10 +289,23 @@
     }
   }
 
-  private static void checkNoYankedVersions(ImmutableMap<ModuleKey, InterimModule> depGraph)
+  private static void checkNoYankedVersions(
+      ImmutableMap<ModuleKey, InterimModule> depGraph,
+      ImmutableMap<YankedVersionsValue.Key, YankedVersionsValue> yankedVersionValues,
+      Optional<ImmutableSet<ModuleKey>> allowedYankedVersions)
       throws BazelModuleResolutionFunctionException {
     for (InterimModule m : depGraph.values()) {
-      if (m.getYankedInfo().isPresent()) {
+      if (m.getRegistry() == null) {
+        // Non-registry modules do not have yanked versions.
+        continue;
+      }
+      Optional<String> yankedInfo =
+          YankedVersionsUtil.getYankedInfo(
+              m.getKey(),
+              yankedVersionValues.get(
+                  YankedVersionsValue.Key.create(m.getName(), m.getRegistry().getUrl())),
+              allowedYankedVersions);
+      if (yankedInfo.isPresent()) {
         throw new BazelModuleResolutionFunctionException(
             ExternalDepsException.withMessage(
                 Code.VERSION_RESOLUTION_ERROR,
@@ -259,7 +315,7 @@
                     + "continue using this version, allow it using the --allow_yanked_versions "
                     + "flag or the BZLMOD_ALLOW_YANKED_VERSIONS env variable.",
                 m.getKey(),
-                m.getYankedInfo().get()),
+                yankedInfo.get()),
             Transience.PERSISTENT);
       }
     }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModule.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModule.java
index 7d0d91e..e075b96 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModule.java
@@ -50,9 +50,6 @@
   /** List of bazel compatible versions that would run/fail this module */
   public abstract ImmutableList<String> getBazelCompatibility();
 
-  /** The reason why this module was yanked or empty if it hasn't been yanked. */
-  public abstract Optional<String> getYankedInfo();
-
   /** The specification of a dependency. */
   @AutoValue
   public abstract static class DepSpec {
@@ -105,8 +102,7 @@
         .setName("")
         .setVersion(Version.EMPTY)
         .setKey(ModuleKey.ROOT)
-        .setCompatibilityLevel(0)
-        .setYankedInfo(Optional.empty());
+        .setCompatibilityLevel(0);
   }
 
   /**
@@ -137,9 +133,6 @@
     /** Optional; defaults to {@link #setName}. */
     public abstract Builder setRepoName(String value);
 
-    /** Optional; defaults to {@link Optional#empty()}. */
-    public abstract Builder setYankedInfo(Optional<String> value);
-
     public abstract Builder setBazelCompatibility(ImmutableList<String> value);
 
     abstract ImmutableList.Builder<String> bazelCompatibilityBuilder();
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java
index cd486b6..6054406 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java
@@ -22,7 +22,6 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.devtools.build.lib.actions.FileValue;
 import com.google.devtools.build.lib.bazel.bzlmod.CompiledModuleFile.IncludeStatement;
@@ -117,10 +116,6 @@
   }
 
   private static class State implements Environment.SkyKeyComputeState {
-    // The following field is used during non-root module file evaluation. We store the module file
-    // here so that a later attempt to retrieve yanked versions wouldn't be overly expensive.
-    GetModuleFileResult getModuleFileResult;
-
     // The following fields are used during root module file evaluation. We try to compile the root
     // module file itself first, and then read, parse, and compile any included module files layer
     // by layer, in a BFS fashion (hence the `horizon` field). Finally, everything is collected into
@@ -152,54 +147,24 @@
       return null;
     }
 
-    Optional<ImmutableSet<ModuleKey>> allowedYankedVersions;
-    try {
-      allowedYankedVersions =
-          YankedVersionsUtil.parseAllowedYankedVersions(
-              allowedYankedVersionsFromEnv.getValue(),
-              Objects.requireNonNull(YankedVersionsUtil.ALLOWED_YANKED_VERSIONS.get(env)));
-    } catch (ExternalDepsException e) {
-      throw new ModuleFileFunctionException(e, SkyFunctionException.Transience.PERSISTENT);
-    }
-
     ModuleFileValue.Key moduleFileKey = (ModuleFileValue.Key) skyKey;
     ModuleKey moduleKey = moduleFileKey.getModuleKey();
-    State state = env.getState(State::new);
-    if (state.getModuleFileResult == null) {
-      try (SilentCloseable c =
-          Profiler.instance()
-              .profile(ProfilerTask.BZLMOD, () -> "fetch module file: " + moduleKey)) {
-        state.getModuleFileResult = getModuleFile(moduleKey, moduleFileKey.getOverride(), env);
-      }
-      if (state.getModuleFileResult == null) {
-        return null;
-      }
+    GetModuleFileResult getModuleFileResult;
+    try (SilentCloseable c =
+        Profiler.instance().profile(ProfilerTask.BZLMOD, () -> "fetch module file: " + moduleKey)) {
+      getModuleFileResult = getModuleFile(moduleKey, moduleFileKey.getOverride(), env);
     }
-    Optional<String> yankedInfo;
-    if (state.getModuleFileResult.registry != null) {
-      YankedVersionsValue yankedVersionsValue =
-          (YankedVersionsValue)
-              env.getValue(
-                  YankedVersionsValue.Key.create(
-                      moduleKey.getName(), state.getModuleFileResult.registry.getUrl()));
-      if (yankedVersionsValue == null) {
-        return null;
-      }
-      yankedInfo =
-          YankedVersionsUtil.getYankedInfo(moduleKey, yankedVersionsValue, allowedYankedVersions);
-    } else {
-      yankedInfo = Optional.empty();
+    if (getModuleFileResult == null) {
+      return null;
     }
     String moduleFileHash =
-        new Fingerprint()
-            .addBytes(state.getModuleFileResult.moduleFile.getContent())
-            .hexDigestAndReset();
+        new Fingerprint().addBytes(getModuleFileResult.moduleFile.getContent()).hexDigestAndReset();
 
     CompiledModuleFile compiledModuleFile;
     try {
       compiledModuleFile =
           CompiledModuleFile.parseAndCompile(
-              state.getModuleFileResult.moduleFile,
+              getModuleFileResult.moduleFile,
               moduleKey,
               starlarkSemantics,
               starlarkEnv,
@@ -221,8 +186,10 @@
             // Dev dependencies should always be ignored if the current module isn't the root module
             /* ignoreDevDeps= */ true,
             builtinModules,
-            // We try to prevent most side effects of yanked modules, in particular print().
-            /* printIsNoop= */ yankedInfo.isPresent(),
+            // Disable printing for modules from registries. We don't want them to be able to spam
+            // the console during resolution, but module files potentially edited by the user as
+            // part of a non-registry override should permit printing to aid debugging.
+            /* printIsNoop= */ getModuleFileResult.registry != null,
             starlarkSemantics,
             env.getListener(),
             SymbolGenerator.create(skyKey));
@@ -230,7 +197,7 @@
     // Perform some sanity checks.
     InterimModule module;
     try {
-      module = moduleThreadContext.buildModule(state.getModuleFileResult.registry);
+      module = moduleThreadContext.buildModule(getModuleFileResult.registry);
     } catch (EvalException e) {
       env.getListener().handle(Event.error(e.getMessageWithStack()));
       throw errorf(Code.BAD_MODULE, "error executing MODULE.bazel file for %s", moduleKey);
@@ -250,22 +217,6 @@
           module.getVersion());
     }
 
-    if (yankedInfo.isPresent()) {
-      // Yanked modules should not have observable side effects such as adding dependency
-      // requirements, so we drop those from the constructed module. We do have to preserve the
-      // compatibility level as it influences the set of versions the yanked version can be updated
-      // to during selection.
-      return NonRootModuleFileValue.create(
-          InterimModule.builder()
-              .setKey(module.getKey())
-              .setName(module.getName())
-              .setVersion(module.getVersion())
-              .setCompatibilityLevel(module.getCompatibilityLevel())
-              .setRegistry(module.getRegistry())
-              .setYankedInfo(yankedInfo)
-              .build(),
-          moduleFileHash);
-    }
 
     return NonRootModuleFileValue.create(module, moduleFileHash);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunctionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunctionTest.java
index 7fd91f6..218a36c 100644
--- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunctionTest.java
@@ -24,15 +24,24 @@
 import com.google.devtools.build.lib.actions.FileValue;
 import com.google.devtools.build.lib.analysis.BlazeDirectories;
 import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
+import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
 import com.google.devtools.build.lib.analysis.ServerDirectories;
 import com.google.devtools.build.lib.analysis.util.AnalysisMock;
 import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.BazelCompatibilityMode;
 import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.CheckDirectDepsMode;
 import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.LockfileMode;
+import com.google.devtools.build.lib.bazel.repository.downloader.DownloadManager;
+import com.google.devtools.build.lib.bazel.repository.starlark.StarlarkRepositoryFunction;
+import com.google.devtools.build.lib.bazel.repository.starlark.StarlarkRepositoryModule;
 import com.google.devtools.build.lib.clock.BlazeClock;
 import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
 import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
+import com.google.devtools.build.lib.rules.repository.LocalRepositoryFunction;
+import com.google.devtools.build.lib.rules.repository.LocalRepositoryRule;
+import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction;
+import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
 import com.google.devtools.build.lib.skyframe.BazelSkyframeExecutorConstants;
+import com.google.devtools.build.lib.skyframe.BzlmodRepoRuleFunction;
 import com.google.devtools.build.lib.skyframe.ClientEnvironmentFunction;
 import com.google.devtools.build.lib.skyframe.ExternalFilesHelper;
 import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
@@ -41,6 +50,7 @@
 import com.google.devtools.build.lib.skyframe.PrecomputedFunction;
 import com.google.devtools.build.lib.skyframe.PrecomputedValue;
 import com.google.devtools.build.lib.skyframe.SkyFunctions;
+import com.google.devtools.build.lib.starlarkbuildapi.repository.RepositoryBootstrap;
 import com.google.devtools.build.lib.testutil.FoundationTestCase;
 import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
@@ -57,12 +67,15 @@
 import com.google.devtools.build.skyframe.SkyFunctionName;
 import java.io.IOException;
 import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import net.starlark.java.eval.StarlarkSemantics;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
 
 /** Tests for {@link BazelModuleResolutionFunction}. */
 @RunWith(JUnit4.class)
@@ -97,6 +110,19 @@
             packageLocator,
             ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS,
             directories);
+    ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder();
+    TestRuleClassProvider.addStandardRules(builder);
+    builder
+        .clearWorkspaceFilePrefixForTesting()
+        .clearWorkspaceFileSuffixForTesting()
+        .addStarlarkBootstrap(new RepositoryBootstrap(new StarlarkRepositoryModule()));
+
+    ConfiguredRuleClassProvider ruleClassProvider = builder.build();
+    ImmutableMap<String, RepositoryFunction> repositoryHandlers =
+        ImmutableMap.of(LocalRepositoryRule.NAME, new LocalRepositoryFunction());
+    DownloadManager downloadManager = Mockito.mock(DownloadManager.class);
+    StarlarkRepositoryFunction starlarkRepositoryFunction =
+        new StarlarkRepositoryFunction(downloadManager);
 
     evaluator =
         new InMemoryMemoizingEvaluator(
@@ -128,6 +154,18 @@
                 .put(
                     SkyFunctions.CLIENT_ENVIRONMENT_VARIABLE,
                     new ClientEnvironmentFunction(new AtomicReference<>(ImmutableMap.of())))
+                .put(
+                    SkyFunctions.REPOSITORY_DIRECTORY,
+                    new RepositoryDelegatorFunction(
+                        repositoryHandlers,
+                        starlarkRepositoryFunction,
+                        new AtomicBoolean(true),
+                        ImmutableMap::of,
+                        directories,
+                        BazelSkyframeExecutorConstants.EXTERNAL_PACKAGE_HELPER))
+                .put(
+                    BzlmodRepoRuleValue.BZLMOD_REPO_RULE,
+                    new BzlmodRepoRuleFunction(ruleClassProvider, directories))
                 .buildOrThrow(),
             differencer);
 
@@ -142,6 +180,10 @@
         differencer, BazelCompatibilityMode.ERROR);
     BazelLockFileFunction.LOCKFILE_MODE.set(differencer, LockfileMode.UPDATE);
     YankedVersionsUtil.ALLOWED_YANKED_VERSIONS.set(differencer, ImmutableList.of());
+    RepositoryDelegatorFunction.FORCE_FETCH.set(
+        differencer, RepositoryDelegatorFunction.FORCE_FETCH_DISABLED);
+    RepositoryDelegatorFunction.REPOSITORY_OVERRIDES.set(differencer, ImmutableMap.of());
+    RepositoryDelegatorFunction.VENDOR_DIRECTORY.set(differencer, Optional.empty());
   }
 
   @Test
@@ -369,88 +411,6 @@
   }
 
   @Test
-  public void testYankedVersionSideEffects_equalCompatibilityLevel() throws Exception {
-    scratch.overwriteFile(
-        rootDirectory.getRelative("MODULE.bazel").getPathString(),
-        "module(name='mod', version='1.0')",
-        "bazel_dep(name = 'a', version = '1.0')",
-        "bazel_dep(name = 'b', version = '1.1')");
-
-    FakeRegistry registry =
-        registryFactory
-            .newFakeRegistry("/bar")
-            .addModule(
-                createModuleKey("a", "1.0"),
-                "module(name='a', version='1.0')",
-                "bazel_dep(name='b', version='1.0')")
-            .addModule(createModuleKey("c", "1.0"), "module(name='c', version='1.0')")
-            .addModule(createModuleKey("c", "1.1"), "module(name='c', version='1.1')")
-            .addModule(
-                createModuleKey("b", "1.0"),
-                "module(name='b', version='1.0', compatibility_level = 2)",
-                "bazel_dep(name='c', version='1.1')",
-                "print('hello from yanked version')")
-            .addModule(
-                createModuleKey("b", "1.1"),
-                "module(name='b', version='1.1', compatibility_level = 2)",
-                "bazel_dep(name='c', version='1.0')")
-            .addYankedVersion("b", ImmutableMap.of(Version.parse("1.0"), "1.0 is a bad version!"));
-
-    ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl()));
-    EvaluationResult<BazelModuleResolutionValue> result =
-        evaluator.evaluate(ImmutableList.of(BazelModuleResolutionValue.KEY), evaluationContext);
-
-    assertThat(result.hasError()).isFalse();
-    assertThat(result.get(BazelModuleResolutionValue.KEY).getResolvedDepGraph().keySet())
-        .containsExactly(
-            ModuleKey.ROOT,
-            createModuleKey("a", "1.0"),
-            createModuleKey("b", "1.1"),
-            createModuleKey("c", "1.0"));
-    assertDoesNotContainEvent("hello from yanked version");
-  }
-
-  @Test
-  public void testYankedVersionSideEffects_differentCompatibilityLevel() throws Exception {
-    scratch.overwriteFile(
-        rootDirectory.getRelative("MODULE.bazel").getPathString(),
-        "module(name='mod', version='1.0')",
-        "bazel_dep(name = 'a', version = '1.0')",
-        "bazel_dep(name = 'b', version = '1.1')");
-
-    FakeRegistry registry =
-        registryFactory
-            .newFakeRegistry("/bar")
-            .addModule(
-                createModuleKey("a", "1.0"),
-                "module(name='a', version='1.0')",
-                "bazel_dep(name='b', version='1.0')")
-            .addModule(createModuleKey("c", "1.0"), "module(name='c', version='1.0')")
-            .addModule(createModuleKey("c", "1.1"), "module(name='c', version='1.1')")
-            .addModule(
-                createModuleKey("b", "1.0"),
-                "module(name='b', version='1.0', compatibility_level = 2)",
-                "bazel_dep(name='c', version='1.1')",
-                "print('hello from yanked version')")
-            .addModule(
-                createModuleKey("b", "1.1"),
-                "module(name='b', version='1.1', compatibility_level = 3)",
-                "bazel_dep(name='c', version='1.0')")
-            .addYankedVersion("b", ImmutableMap.of(Version.parse("1.0"), "1.0 is a bad version!"));
-
-    ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl()));
-    EvaluationResult<BazelModuleResolutionValue> result =
-        evaluator.evaluate(ImmutableList.of(BazelModuleResolutionValue.KEY), evaluationContext);
-
-    assertThat(result.hasError()).isTrue();
-    assertThat(result.getError().toString())
-        .contains(
-            "a@1.0 depends on b@1.0 with compatibility level 2, but <root> depends on b@1.1 with"
-                + " compatibility level 3 which is different");
-    assertDoesNotContainEvent("hello from yanked version");
-  }
-
-  @Test
   public void overrideOnNonexistentModule() throws Exception {
     scratch.overwriteFile(
         rootDirectory.getRelative("MODULE.bazel").getPathString(),
@@ -485,4 +445,52 @@
     assertThat(result.getError().toString())
         .contains("the root module specifies overrides on nonexistent module(s): d");
   }
+
+  @Test
+  public void testPrintBehavior() throws Exception {
+    scratch.overwriteFile(
+        rootDirectory.getRelative("MODULE.bazel").getPathString(),
+        "module(name='mod', version='1.0')",
+        "print('hello from root module')",
+        "bazel_dep(name = 'a', version = '1.0')",
+        "bazel_dep(name = 'b', version = '1.1')",
+        "single_version_override(module_name = 'b', version = '1.1')",
+        "local_path_override(module_name='a', path='a')");
+    scratch.file(
+        "a/MODULE.bazel",
+        "module(name='a', version='1.0')",
+        "print('hello from overridden a')",
+        "bazel_dep(name='b', version='1.0')");
+
+    FakeRegistry registry =
+        registryFactory
+            .newFakeRegistry("/bar")
+            .addModule(
+                createModuleKey("a", "1.0"),
+                "module(name='a', version='1.0')",
+                "print('hello from a@1.0')",
+                "bazel_dep(name='b', version='1.0')")
+            .addModule(createModuleKey("c", "1.0"), "module(name='c', version='1.0')")
+            .addModule(createModuleKey("c", "1.1"), "module(name='c', version='1.1')")
+            .addModule(
+                createModuleKey("b", "1.0"),
+                "module(name='b', version='1.0', compatibility_level = 2)",
+                "bazel_dep(name='c', version='1.1')",
+                "print('hello from b@1.0')")
+            .addModule(
+                createModuleKey("b", "1.1"),
+                "module(name='b', version='1.1', compatibility_level = 3)",
+                "bazel_dep(name='c', version='1.0')",
+                "print('hello from b@1.1')");
+
+    ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl()));
+    EvaluationResult<BazelModuleResolutionValue> result =
+        evaluator.evaluate(ImmutableList.of(BazelModuleResolutionValue.KEY), evaluationContext);
+
+    assertContainsEvent("hello from root module");
+    assertContainsEvent("hello from overridden a");
+    assertDoesNotContainEvent("hello from a@1.0");
+    assertDoesNotContainEvent("hello from b@1.0");
+    assertDoesNotContainEvent("hello from b@1.1");
+  }
 }