Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Two BindingsMap$ResolvedImport aren't equal and raise exception on the CI #11171

Closed
hubertp opened this issue Sep 25, 2024 · 9 comments · Fixed by #11513
Closed

Two BindingsMap$ResolvedImport aren't equal and raise exception on the CI #11171

hubertp opened this issue Sep 25, 2024 · 9 comments · Fixed by #11513
Assignees

Comments

@hubertp
Copy link
Collaborator

hubertp commented Sep 25, 2024

I somehow got an impression that this shouldn't happen anymore?

   INFO ide_ci::program::command: sbt ℹ️ [info] Iteration   1: <failure>
   INFO ide_ci::program::command: sbt ℹ️ [info] java.io.IOException: Adding at 20165 object:
   INFO ide_ci::program::command: sbt ℹ️ [info]   org.enso.compiler.data.BindingsMap$ResolvedImport@7402996f
   INFO ide_ci::program::command: sbt ℹ️ [info] but there already is:
   INFO ide_ci::program::command: sbt ℹ️ [info]   org.enso.compiler.data.BindingsMap$ResolvedImport@4252eec3
   INFO ide_ci::program::command: sbt ℹ️ [info] are they equal: false
   INFO ide_ci::program::command: sbt ℹ️ [info] 	at org.enso.runtime/org.enso.persist.PerInputImpl.readIndirect(PerInputImpl.java:229)
   INFO ide_ci::program::command: sbt ℹ️ [info] 	at org.enso.runtime/org.enso.persist.PerInputImpl.readObject(PerInputImpl.java:74)
   INFO ide_ci::program::command: sbt ℹ️ [info] 	at org.enso.runtime/org.enso.compiler.core.ir.IrPersistance$PersistScalaList.readObject(IrPersistance.java:227)
   INFO ide_ci::program::command: sbt ℹ️ [info] 	at org.enso.runtime/org.enso.compiler.core.ir.IrPersistance$PersistScalaList.readObject(IrPersistance.java:207)
   INFO ide_ci::program::command: sbt ℹ️ [info] 	at org.enso.runtime/org.enso.persist.Persistance.readWith(Persistance.java:162)
   INFO ide_ci::program::command: sbt ℹ️ [info] 	at org.enso.runtime/org.enso.persist.PerInputImpl.readInline(PerInputImpl.java:67)

https://github.com/enso-org/enso/actions/runs/11030572774/job/30635424983#step:7:1461

@JaroslavTulach
Copy link
Member

JaroslavTulach commented Sep 26, 2024

this shouldn't happen anymore?

The test has been disabled in the release branch only by

as the fix was integrated only into https://github.com/enso-org/enso/tree/release/2024.4 branch, the failure still appears in develop. The progress is being tracked by:

Closing as duplicate. But thanks for the reminder we need a fix for #10985 before next release.

GitHub
Hybrid visual and textual functional programming. Contribute to enso-org/enso development by creating an account on GitHub.

@JaroslavTulach JaroslavTulach closed this as not planned Won't fix, can't repro, duplicate, stale Sep 26, 2024
@github-project-automation github-project-automation bot moved this from ❓New to 🟢 Accepted in Issues Board Sep 26, 2024
@hubertp hubertp reopened this Oct 7, 2024
@github-project-automation github-project-automation bot moved this from 🟢 Accepted to ❓New in Issues Board Oct 7, 2024
@hubertp
Copy link
Collaborator Author

hubertp commented Oct 7, 2024

I continue to see this failure in CI. Latest one where it happened:
https://github.com/enso-org/enso/actions/runs/11211349356/job/31160043979?pr=11250#step:7:1446

GitHub
Hybrid visual and textual functional programming. Contribute to enso-org/enso development by creating an account on GitHub.

@JaroslavTulach JaroslavTulach changed the title Duplicate(?) BindingsMap entries Two BindingsMap$ResolvedImport aren't equal and raise exception on the CI Oct 8, 2024
@hubertp
Copy link
Collaborator Author

hubertp commented Oct 28, 2024

And another https://github.com/enso-org/enso/actions/runs/11563450462/job/32186772474?pr=11375#step:7:1636

GitHub
Hybrid visual and textual functional programming. Contribute to enso-org/enso development by creating an account on GitHub.

@enso-bot
Copy link

enso-bot bot commented Nov 7, 2024

Jaroslav Tulach reports a new STANDUP for yesterday (2024-11-06):

Progress: .

@JaroslavTulach
Copy link
Member

I tried to re-enable proper check of metadata.equals(other.metadata) in:

diff --git engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FramePointer.java engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FramePointer.java
index 425af3bf8f..697b769b24 100644
--- engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FramePointer.java
+++ engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FramePointer.java
@@ -1,5 +1,6 @@
 package org.enso.compiler.pass.analyse;
 
+import java.util.IdentityHashMap;
 import org.enso.compiler.core.CompilerStub;
 import org.enso.compiler.core.ir.ProcessingPass;
 import org.enso.persist.Persistable;
@@ -10,10 +11,20 @@ import scala.Option;
  */
 @Persistable(clazz = FramePointer.class, id = 1283)
 public record FramePointer(int parentLevel, int frameSlotIdx) implements FrameAnalysisMeta {
+  public static final IdentityHashMap<FramePointer, Exception> WHEN = new IdentityHashMap<>();
 
   public FramePointer {
     assert parentLevel >= 0;
     assert frameSlotIdx >= 0;
+    if (parentLevel == 0 && frameSlotIdx == 3) {
+      //        System.err.println("now Frame for 0, 3");
+    }
+    //    WHEN.put(this, new Exception("Allocated now: " + parentLevel + " " + frameSlotIdx));
+  }
+
+  public Exception when() {
+    var ex = WHEN.get(this);
+    return ex == null ? new Exception("no previous record") : ex;
   }
 
   @Override
diff --git engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameVariableNames.java engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameVariableNames.java
index 9d87a57a17..92edc4c9e0 100644
--- engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameVariableNames.java
+++ engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/FrameVariableNames.java
@@ -1,6 +1,7 @@
 package org.enso.compiler.pass.analyse;
 
 import java.util.List;
+import java.util.Objects;
 import org.enso.compiler.core.CompilerStub;
 import org.enso.compiler.core.ir.ProcessingPass;
 import org.enso.persist.Persistable;
@@ -11,7 +12,7 @@ import scala.jdk.javaapi.CollectionConverters;
 public final class FrameVariableNames implements FrameAnalysisMeta {
   private final List<String> names;
 
-  public FrameVariableNames(List<String> variableNames) {
+  FrameVariableNames(List<String> variableNames) {
     this.names = variableNames;
   }
 
@@ -42,4 +43,31 @@ public final class FrameVariableNames implements FrameAnalysisMeta {
   public Option<ProcessingPass.Metadata> duplicate() {
     return Option.apply(new FrameVariableNames(names));
   }
+
+  @Override
+  public int hashCode() {
+    int hash = 3;
+    hash = 59 * hash + Objects.hashCode(this.names);
+    return hash;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    final FrameVariableNames other = (FrameVariableNames) obj;
+    return Objects.equals(this.names, other.names);
+  }
+
+  @Override
+  public String toString() {
+    return "FrameVariableNames{" + "names=" + names + '}';
+  }
 }
diff --git engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/FramePointerAnalysis.scala engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/FramePointerAnalysis.scala
index 71a1e00b1b..07263105f1 100644
--- engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/FramePointerAnalysis.scala
+++ engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/analyse/FramePointerAnalysis.scala
@@ -38,7 +38,10 @@ case object FramePointerAnalysis extends IRPass {
   override val invalidatedPasses: Seq[IRPass] = Seq(this)
 
   override def runModule(ir: Module, moduleContext: ModuleContext): Module = {
+    FramePointer.WHEN.clear()
+    System.err.println("running a module")
     ir.bindings.foreach(processBinding)
+    System.err.println("finished a module")
     ir
   }
 
@@ -319,16 +322,21 @@ case object FramePointerAnalysis extends IRPass {
     newMeta: FrameAnalysisMeta
   ): Unit = {
     ir.passData().get(this) match {
-      case None =>
-        ir.passData()
-          .update(this, newMeta)
       case Some(meta) =>
-        val ex = new IllegalStateException(
-          "Unexpected FrameAnalysisMeta associated with IR " + ir + "\nOld: " + meta + " new " + newMeta
-        )
-        ex.setStackTrace(ex.getStackTrace().slice(0, 10))
-        throw ex
+        if (meta != newMeta) {
+          val ex = new IllegalStateException(
+            "Unexpected FrameAnalysisMeta associated with IR " + ir + "\nOld: " + meta + " new " + newMeta
+          )
+//            ex.setStackTrace(ex.getStackTrace().slice(0, 10))
+          if (meta.isInstanceOf[FramePointer]) {
+            ex.printStackTrace()
+            meta.asInstanceOf[FramePointer].when.printStackTrace()
+          }
+          // throw ex
+        }
+      case None =>
     }
+    ir.passData().update(this, newMeta)
   }
 
   /** Returns the index of the given `defOcc` definition in the given `scope`
diff --git engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/core/ir/MetadataStorageTest.scala engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/core/ir/MetadataStorageTest.scala
index 1f2fdf6558..07a0c187d2 100644
--- engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/core/ir/MetadataStorageTest.scala
+++ engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/core/ir/MetadataStorageTest.scala
@@ -159,7 +159,7 @@ class MetadataStorageTest extends CompilerTest {
       meta1.update(TestPass1, meta)
       meta2.update(TestPass1, meta)
 
-      meta1 shouldNot equal(meta2)
+      meta1 shouldEqual meta2
     }
 
     def newMetadataStorage(init: Seq[MetadataPair[_]]): MetadataStorage = {
@@ -201,8 +201,8 @@ class MetadataStorageTest extends CompilerTest {
         )
       )
 
-      meta.duplicate shouldNot equal(meta)
-      meta.duplicate shouldNot equal(expected)
+      meta.duplicate shouldEqual meta
+      meta.duplicate shouldEqual expected
     }
 
     "enforce safe construction" in {
diff --git engine/runtime-parser/src/main/java/org/enso/compiler/core/ir/MetadataStorage.java engine/runtime-parser/src/main/java/org/enso/compiler/core/ir/MetadataStorage.java
index e0ddd703ae..89f08ccec1 100644
--- engine/runtime-parser/src/main/java/org/enso/compiler/core/ir/MetadataStorage.java
+++ engine/runtime-parser/src/main/java/org/enso/compiler/core/ir/MetadataStorage.java
@@ -3,10 +3,10 @@ package org.enso.compiler.core.ir;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.function.BiFunction;
 import java.util.stream.Collectors;
@@ -15,14 +15,17 @@ import scala.Option;
 
 /** Stores metadata for the various passes. */
 public final class MetadataStorage {
-  private Map<ProcessingPass, ProcessingPass.Metadata> metadata;
+  private SortedMap<ProcessingPass, ProcessingPass.Metadata> metadata;
 
   public MetadataStorage() {
-    this(Collections.emptyMap());
+    this(copyMetaMap(null));
   }
 
   public MetadataStorage(Map<ProcessingPass, ProcessingPass.Metadata> init) {
-    this.metadata = init;
+    this.metadata =
+        init instanceof SortedMap<ProcessingPass, ProcessingPass.Metadata> sorted
+            ? sorted
+            : copyMetaMap(init);
   }
 
   /**
@@ -34,7 +37,7 @@ public final class MetadataStorage {
    * @tparam K the concrete type of `pass`
    */
   public void update(ProcessingPass pass, ProcessingPass.Metadata newMeta) {
-    var copy = copyMetaMap();
+    var copy = copyMetaMap(metadata);
     copy.put(pass, newMeta);
     metadata = copy;
   }
@@ -63,7 +66,7 @@ public final class MetadataStorage {
     if (prev == null) {
       return Option.empty();
     } else {
-      var copy = copyMetaMap();
+      var copy = copyMetaMap(metadata);
       copy.remove(pass);
       metadata = copy;
       return Option.apply(prev);
@@ -88,7 +91,7 @@ public final class MetadataStorage {
    * @return a deep copy of `this`
    */
   public MetadataStorage duplicate() {
-    var map = new HashMap<ProcessingPass, ProcessingPass.Metadata>();
+    var map = new TreeMap<ProcessingPass, ProcessingPass.Metadata>(COMPARATOR);
     for (var entry : this.metadata.entrySet()) {
       var key = entry.getKey();
       var meta = (ProcessingPass.Metadata) entry.getValue();
@@ -151,24 +154,17 @@ public final class MetadataStorage {
    * @return `true` if restoration was successful, `false` otherwise
    */
   public boolean restoreFromSerialization(CompilerStub compiler) {
-    var ok = new boolean[] {true};
-    var newMap =
-        metadata.entrySet().stream()
-            .collect(
-                Collectors.toMap(
-                    Map.Entry::getKey,
-                    (en) -> {
-                      var value = en.getValue();
-                      var newOption = value.restoreFromSerialization(compiler);
-                      if (newOption.nonEmpty()) {
-                        return newOption.get();
-                      } else {
-                        ok[0] = false;
-                        return value;
-                      }
-                    }));
+    var newMap = new TreeMap<ProcessingPass, ProcessingPass.Metadata>(COMPARATOR);
+    for (var en : metadata.entrySet()) {
+      var newOption = en.getValue().restoreFromSerialization(compiler);
+      if (newOption.nonEmpty()) {
+        newMap.put(en.getKey(), newOption.get());
+      } else {
+        return false;
+      }
+    }
     this.metadata = newMap;
-    return ok[0];
+    return true;
   }
 
   @Override
@@ -195,9 +191,12 @@ public final class MetadataStorage {
         return p1.getClass().getName().compareTo(p2.getClass().getName());
       };
 
-  private Map<ProcessingPass, ProcessingPass.Metadata> copyMetaMap() {
+  private static SortedMap<ProcessingPass, ProcessingPass.Metadata> copyMetaMap(
+      Map<ProcessingPass, ProcessingPass.Metadata> data) {
     var copy = new TreeMap<ProcessingPass, ProcessingPass.Metadata>(COMPARATOR);
-    copy.putAll(metadata);
+    if (data != null) {
+      copy.putAll(data);
+    }
     return copy;
   }
 
@@ -214,7 +213,7 @@ public final class MetadataStorage {
       return true;
     }
     if (obj instanceof MetadataStorage other) {
-      return this.metadata == other.metadata;
+      return this.metadata.equals(other.metadata);
     }
     return false;
   }

but that fails with StackoverFlowError due to cycles in the IR. Possibly caused by #11509

@enso-bot
Copy link

enso-bot bot commented Nov 8, 2024

Jaroslav Tulach reports a new STANDUP for yesterday (2024-11-07):

Progress: .

@JaroslavTulach
Copy link
Member

JaroslavTulach commented Nov 8, 2024

I tried to re-enable proper check of metadata.equals(other.metadata) in:

There are problems with the equals check. I suspect that one of the possible cases is that we sometimes use the same MetadataStorage for multiple IR elements. When that happens we risk a change of metadata in one of the elements will affect other elements. That's probably not desirable. Clear place where unwanted(?) sharing is happening are the IR.copy methods. Let's do a shallow copy in copy methods:

69 copy methods

CCing @radeusgd

@JaroslavTulach
Copy link
Member

Finally found the problem! this.passData ne passData will do the fix! PR #11513 is ready for your consideration.bb

@mergify mergify bot closed this as completed in #11513 Nov 8, 2024
@mergify mergify bot closed this as completed in 67db825 Nov 8, 2024
@github-project-automation github-project-automation bot moved this from ⚙️ Design to 🟢 Accepted in Issues Board Nov 8, 2024
@enso-bot
Copy link

enso-bot bot commented Nov 8, 2024

Jaroslav Tulach reports a new STANDUP for today (2024-11-08):

Progress: .

Discord
Discord is great for playing games and chilling with friends, or even building a worldwide community. Customize your own space to talk, play, and hang out.
Discord
Discord is great for playing games and chilling with friends, or even building a worldwide community. Customize your own space to talk, play, and hang out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 🟢 Accepted
Development

Successfully merging a pull request may close this issue.

2 participants