Repository: microsoft/qdk (Rust)

Issue:
Typechecking doesn't support UDTs that reference other UDTs in the standard library
This isn't an issue today *in practice*, because we don't have any UDTs in the stdlib that reference other UDTs.

But it is a bug in the compiler. It would be exposed if we started adding such types to the stdlib.

**Repro steps**

You need to update the stdlib to repro this bug. In `math.qs`, add the following type declaration:

```qsharp
newtype ComplexWrapper = (c: Complex);
```

Compile with `cargo build`.

Launch `target/debug/qsi` . Run the following Q#:

```qsharp
open Microsoft.Quantum.Math;
let c = Complex(1.0,1.0);
let x: Double = c::Real;
let cw = ComplexWrapper(c);
let cx: Complex = cw::c;
```

**Expected**

The last assignment should succeed.

**Actual**

```
error: Qsc.TypeCk.TyMismatch

  × type error
  ╰─▶ expected UDT<Item 210 (Package 1)>, found UDT<Item 210>
   ╭─[line_9:1:1]
 1 │ let cx: Complex = cw::c;
   ·                   ─────
   ╰────
```

Even though both sides of the assignment are type `Complex` , one of them is missing its `PackageId`, making the comparison fail.


Exists as of a153981617a6c8a2bb745a8d15176755d399af44



Discussion:
FYI @swernli 
Hmm... this seems like it could have a similar cause to the issue fixed in #766. I might be able to fix this using the same infra that introduced.
@swernli I haven't confirmed this, but intuitively I also feel like this is related to the comments I left in my PR here:

https://github.com/microsoft/qsharp/pull/814/files#diff-2459af92d4e6b65ea27714c62d8ca0520a29f9e55c7148aef932b8eb8f2b7368R213

`typeck::Table::terms` doesn't keep track of the PackageId which makes it impossible to resolve UDTs through that type in the future. This is definitely causing bugs in the language service but I'm not sure if they're the same bug as this. For now, I'll assume the same, but we can revisit if they turn out to be separate bugs



Patch:
diff --git a/compiler/qsc_frontend/src/typeck/infer.rs b/compiler/qsc_frontend/src/typeck/infer.rs
index b46fea00d3..c1fda93c2d 100644
--- a/compiler/qsc_frontend/src/typeck/infer.rs
+++ b/compiler/qsc_frontend/src/typeck/infer.rs
@@ -893,7 +893,9 @@ fn check_has_field(
                 Some(ty) => (
                     vec![Constraint::Eq {
                         expected: item,
-                        actual: ty.clone(),
+                        actual: id
+                            .package
+                            .map_or_else(|| ty.clone(), |package_id| ty.with_package(package_id)),
                         span,
                     }],
                     Vec::new(),
@@ -1011,7 +1013,10 @@ fn check_unwrap(
             return (
                 vec![Constraint::Eq {
                     expected: base,
-                    actual: udt.get_pure_ty(),
+                    actual: id.package.map_or_else(
+                        || udt.get_pure_ty(),
+                        |package_id| udt.get_pure_ty().with_package(package_id),
+                    ),
                     span,
                 }],
                 Vec::new(),
diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs
index ecb2e5c6fa..e51929234f 100644
--- a/compiler/qsc_hir/src/hir.rs
+++ b/compiler/qsc_hir/src/hir.rs
@@ -206,9 +206,6 @@ impl Res {
                 package: Some(package),
                 item: id.item,
             }),
-            Res::Item(id) if id.package.expect("none case should be handled above") != package => {
-                panic!("should not try to update Res with existing package id")
-            }
             _ => *self,
         }
     }


Test patch:
diff --git a/compiler/qsc_frontend/src/compile/tests.rs b/compiler/qsc_frontend/src/compile/tests.rs
index d57f7c19aa..d172ec2810 100644
--- a/compiler/qsc_frontend/src/compile/tests.rs
+++ b/compiler/qsc_frontend/src/compile/tests.rs
@@ -637,6 +637,99 @@ fn package_dependency_udt() {
     .assert_eq(&unit2.package.to_string());
 }
 
+#[test]
+fn package_dependency_nested_udt() {
+    let mut store = PackageStore::new(super::core());
+
+    let sources1 = SourceMap::new(
+        [(
+            "test".into(),
+            indoc! {"
+                namespace Package1 {
+                    newtype Bar = Int;
+                    newtype Baz = Int;
+                    newtype Foo = (bar : Bar, Baz);
+                }
+            "}
+            .into(),
+        )],
+        None,
+    );
+    let unit1 = compile(&store, &[], sources1, TargetProfile::Full);
+    assert!(unit1.errors.is_empty(), "{:#?}", unit1.errors);
+    let package1 = store.insert(unit1);
+
+    let sources2 = SourceMap::new(
+        [(
+            "test".into(),
+            indoc! {"
+                namespace Package2 {
+                    function Test() : Int {
+                        let bar = Package1.Bar(1);
+                        let baz = Package1.Baz(2);
+                        let foo = Package1.Foo(bar, baz);
+                        let inner : Package1.Bar = foo::bar;
+                        let (_, other : Package1.Baz) = foo!;
+                        inner!
+                    }
+                }
+            "}
+            .into(),
+        )],
+        None,
+    );
+    let unit2 = compile(&store, &[package1], sources2, TargetProfile::Full);
+    assert!(unit2.errors.is_empty(), "{:#?}", unit2.errors);
+
+    expect![[r#"
+        Package:
+            Item 0 [0-274] (Public):
+                Namespace (Ident 40 [10-18] "Package2"): Item 1
+            Item 1 [25-272] (Public):
+                Parent: 0
+                Callable 0 [25-272] (function):
+                    name: Ident 1 [34-38] "Test"
+                    input: Pat 2 [38-40] [Type Unit]: Unit
+                    output: Int
+                    functors: empty set
+                    body: SpecDecl 3 [25-272]: Impl:
+                        Block 4 [47-272] [Type Int]:
+                            Stmt 5 [57-83]: Local (Immutable):
+                                Pat 6 [61-64] [Type UDT<Item 1 (Package 1)>]: Bind: Ident 7 [61-64] "bar"
+                                Expr 8 [67-82] [Type UDT<Item 1 (Package 1)>]: Call:
+                                    Expr 9 [67-79] [Type (Int -> UDT<Item 1 (Package 1)>)]: Var: Item 1 (Package 1)
+                                    Expr 10 [80-81] [Type Int]: Lit: Int(1)
+                            Stmt 11 [92-118]: Local (Immutable):
+                                Pat 12 [96-99] [Type UDT<Item 2 (Package 1)>]: Bind: Ident 13 [96-99] "baz"
+                                Expr 14 [102-117] [Type UDT<Item 2 (Package 1)>]: Call:
+                                    Expr 15 [102-114] [Type (Int -> UDT<Item 2 (Package 1)>)]: Var: Item 2 (Package 1)
+                                    Expr 16 [115-116] [Type Int]: Lit: Int(2)
+                            Stmt 17 [127-160]: Local (Immutable):
+                                Pat 18 [131-134] [Type UDT<Item 3 (Package 1)>]: Bind: Ident 19 [131-134] "foo"
+                                Expr 20 [137-159] [Type UDT<Item 3 (Package 1)>]: Call:
+                                    Expr 21 [137-149] [Type ((UDT<Item 1 (Package 1)>, UDT<Item 2 (Package 1)>) -> UDT<Item 3 (Package 1)>)]: Var: Item 3 (Package 1)
+                                    Expr 22 [149-159] [Type (UDT<Item 1 (Package 1)>, UDT<Item 2 (Package 1)>)]: Tuple:
+                                        Expr 23 [150-153] [Type UDT<Item 1 (Package 1)>]: Var: Local 7
+                                        Expr 24 [155-158] [Type UDT<Item 2 (Package 1)>]: Var: Local 13
+                            Stmt 25 [169-205]: Local (Immutable):
+                                Pat 26 [173-193] [Type UDT<Item 1 (Package 1)>]: Bind: Ident 27 [173-178] "inner"
+                                Expr 28 [196-204] [Type UDT<Item 1 (Package 1)>]: Field:
+                                    Expr 29 [196-199] [Type UDT<Item 3 (Package 1)>]: Var: Local 19
+                                    Path(FieldPath { indices: [0] })
+                            Stmt 30 [214-251]: Local (Immutable):
+                                Pat 31 [218-243] [Type (UDT<Item 1 (Package 1)>, UDT<Item 2 (Package 1)>)]: Tuple:
+                                    Pat 32 [219-220] [Type UDT<Item 1 (Package 1)>]: Discard
+                                    Pat 33 [222-242] [Type UDT<Item 2 (Package 1)>]: Bind: Ident 34 [222-227] "other"
+                                Expr 35 [246-250] [Type (UDT<Item 1 (Package 1)>, UDT<Item 2 (Package 1)>)]: UnOp (Unwrap):
+                                    Expr 36 [246-249] [Type UDT<Item 3 (Package 1)>]: Var: Local 19
+                            Stmt 37 [260-266]: Expr: Expr 38 [260-266] [Type Int]: UnOp (Unwrap):
+                                Expr 39 [260-265] [Type UDT<Item 1 (Package 1)>]: Var: Local 27
+                    adj: <none>
+                    ctl: <none>
+                    ctl-adj: <none>"#]]
+    .assert_eq(&unit2.package.to_string());
+}
+
 #[test]
 fn std_dependency() {
     let mut store = PackageStore::new(super::core());