Repository: microsoft/qdk (Rust)

Issue:
Multiple imports of the same item should be allowed
Currently, auto-inserted imports can only be run once with subsequent runs generating a `ImportDuplicate` error. This harms the notebook experience, as cells with imports can only be run once. We should allow imports of the same item to the same name or unoccupied names and only treat it as an error if there is a collision with a different item.


Discussion:

Patch:
diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs
index 58e89ebdc2..a2dd49cad6 100644
--- a/compiler/qsc_frontend/src/resolve.rs
+++ b/compiler/qsc_frontend/src/resolve.rs
@@ -73,6 +73,16 @@ pub enum Res {
     ExportedItem(ItemId, Option<Ident>),
 }
 
+impl Res {
+    #[must_use]
+    pub fn item_id(&self) -> Option<ItemId> {
+        match self {
+            Res::Item(id, _) | Res::ExportedItem(id, _) => Some(*id),
+            _ => None,
+        }
+    }
+}
+
 #[derive(Clone, Debug, Diagnostic, Error, PartialEq)]
 pub(super) enum Error {
     #[error("`{name}` could refer to the item in `{first_open}` or `{second_open}`")]
@@ -923,10 +933,26 @@ impl Resolver {
                     (false, Some(entry), _) | (true, _, Some(entry))
                         if matches!(entry.source, ItemSource::Imported(..)) =>
                     {
-                        let err =
-                            Error::ImportedDuplicate(local_name.to_string(), decl_item.name().span);
-                        self.errors.push(err);
-                        continue;
+                        // only push this error if the import is of a different item.
+                        // this is for the re-runnability of jupyter cells.
+                        // we want to be able to re-run a cell which may have an import in
+                        // it, meaning it would evaluate the same import multiple times.
+                        //
+                        // if the import is of a different item, though, then we should throw an
+                        // error because the import introduces a conflict.
+                        match (term_result, ty_result) {
+                            (Ok(res), _) | (_, Ok(res)) if res.item_id() == Some(entry.id) => {
+                                continue;
+                            }
+                            _ => {
+                                let err = Error::ImportedDuplicate(
+                                    local_name.to_string(),
+                                    decl_item.name().span,
+                                );
+                                self.errors.push(err);
+                                continue;
+                            }
+                        }
                     }
                     // special case:
                     // if this is an export of an import with an alias,


Test patch:
diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs
index 4fd00e3596..ce550558e7 100644
--- a/compiler/qsc_frontend/src/resolve/tests.rs
+++ b/compiler/qsc_frontend/src/resolve/tests.rs
@@ -4472,6 +4472,7 @@ fn import_self() {
     );
 }
 
+// this should be allowed for jupyter cell re-runnability
 #[test]
 fn import_duplicate_symbol() {
     check(
@@ -4490,12 +4491,11 @@ fn import_duplicate_symbol() {
             namespace namespace9 {
                 operation item2() : Unit {}
             }
-
-            // ImportedDuplicate("Baz", Span { lo: 49, hi: 52 })
         "#]],
     );
 }
 
+// this should be allowed for jupyter cell re-runnability
 #[test]
 fn import_duplicate_symbol_different_name() {
     check(
@@ -4516,11 +4516,37 @@ fn import_duplicate_symbol_different_name() {
             namespace namespace9 {
                 operation item2() : Unit {}
             }
+        "#]],
+    );
+}
+
+// this should be allowed for jupyter cell re-runnability
+#[test]
+fn disallow_importing_different_items_with_same_name() {
+    check(
+        indoc! { r#"
+        namespace Main {
+            import Foo.Bar.Baz, Foo.Bar.Baz2 as Baz;
+        }
+        namespace Foo.Bar {
+            operation Baz() : Unit {}
+            operation Baz2() : Unit {}
+        }
+"# },
+        &expect![[r#"
+            namespace namespace7 {
+                import item2, item3;
+            }
+            namespace namespace9 {
+                operation item2() : Unit {}
+                operation item3() : Unit {}
+            }
 
-            // ImportedDuplicate("Baz", Span { lo: 65, hi: 68 })
+            // ImportedDuplicate("Baz", Span { lo: 57, hi: 60 })
         "#]],
     );
 }
+
 #[test]
 fn import_takes_precedence_over_local_decl() {
     check(