Skip to content

Commit 8106f2f

Browse files
authored
Fix UDT re-exports (microsoft#2137)
Closes microsoft#2122
1 parent 6ce7372 commit 8106f2f

File tree

3 files changed

+102
-10
lines changed

3 files changed

+102
-10
lines changed

compiler/qsc_frontend/src/compile/tests/multiple_packages.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,54 @@ fn multiple_package_check_inner(packages: Vec<(&str, &str)>, expect: Option<&Exp
6161
}
6262
}
6363

64+
/// This can be used to test multiple packages which internally have multiple source files, as opposed to the more simple `multiple_package_check`
65+
/// which only allows one source file per package (for easy and quick test creation).
66+
fn multiple_package_multiple_source_check(
67+
packages: Vec<(&str, Vec<(&str, &str)>)>,
68+
expect: Option<&Expect>,
69+
) {
70+
let mut store = PackageStore::new(core());
71+
let mut prev_id_and_name: Option<(PackageId, &str)> = None;
72+
let num_packages = packages.len();
73+
for (ix, (package_name, sources)) in packages.into_iter().enumerate() {
74+
let is_last = ix == num_packages - 1;
75+
let deps = if let Some((prev_id, prev_name)) = prev_id_and_name {
76+
vec![(prev_id, Some(Arc::from(prev_name)))]
77+
} else {
78+
vec![]
79+
};
80+
81+
let sources = SourceMap::new(
82+
sources.iter().map(|(name, source)| {
83+
(
84+
Arc::from(format!("{package_name}/{name}.qs")),
85+
Arc::from(*source),
86+
)
87+
}),
88+
None,
89+
);
90+
91+
let unit = compile(
92+
&store,
93+
&deps[..],
94+
sources,
95+
TargetCapabilityFlags::all(),
96+
LanguageFeatures::default(),
97+
);
98+
99+
match (is_last, &expect) {
100+
(true, Some(expect)) => {
101+
expect.assert_eq(&format!("{:#?}", unit.errors));
102+
}
103+
_ => {
104+
assert!(unit.errors.is_empty(), "{:#?}", unit.errors);
105+
}
106+
}
107+
108+
prev_id_and_name = Some((store.insert(unit), package_name));
109+
}
110+
}
111+
64112
#[test]
65113
fn namespace_named_main_doesnt_create_main_namespace() {
66114
multiple_package_check_expect_err(
@@ -509,3 +557,48 @@ fn aliased_export_via_aliased_import() {
509557
),
510558
]);
511559
}
560+
561+
#[test]
562+
fn udt_reexport_with_alias() {
563+
multiple_package_multiple_source_check(
564+
vec![
565+
(
566+
"A",
567+
vec![
568+
("FileOne", "struct Foo { content: Bool }"),
569+
("FileTwo", "export FileOne.Foo as Bar;"),
570+
],
571+
),
572+
("B", vec![("FileThree", "export A.FileTwo.Bar as Baz;")]),
573+
(
574+
"C",
575+
vec![(
576+
"FileFour",
577+
"@EntryPoint()
578+
operation Main() : Unit {
579+
let x = new B.FileThree.Baz { content = true };
580+
}",
581+
)],
582+
),
583+
],
584+
Some(&expect!["[]"]),
585+
);
586+
}
587+
588+
#[test]
589+
fn callable_reexport() {
590+
multiple_package_check(vec![
591+
(
592+
"A",
593+
"function Foo() : Unit { }
594+
export Foo as Bar;",
595+
),
596+
(
597+
"B",
598+
" @EntryPoint()
599+
operation Main() : Unit {
600+
let x = A.A.Bar();
601+
}",
602+
),
603+
]);
604+
}

compiler/qsc_frontend/src/resolve.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,10 +1676,10 @@ impl GlobalTable {
16761676
.insert_or_find_namespace(ns.iter().map(|s| s.name.clone()));
16771677
}
16781678
hir::ItemKind::Ty(..) => {
1679-
self.scope
1680-
.tys
1681-
.get_mut_or_default(namespace)
1682-
.insert(global.name.clone(), Res::ExportedItem(item_id, None));
1679+
self.scope.tys.get_mut_or_default(namespace).insert(
1680+
global.name.clone(),
1681+
Res::Item(item_id, ItemStatus::Available),
1682+
);
16831683
}
16841684
hir::ItemKind::Export(_, _) => {
16851685
unreachable!("find_item will never return an Export")

compiler/qsc_frontend/src/typeck/convert.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,12 @@ pub(super) fn ty_from_path(names: &Names, path: &Path) -> Ty {
117117
// So realistically, by construction, `Param` here is unreachable.
118118
// A path can also never resolve to an export, because in typeck/check,
119119
// we resolve exports to their original definition.
120-
Some(
121-
resolve::Res::Local(_) | resolve::Res::Param { .. } | resolve::Res::ExportedItem(_, _),
122-
) => {
123-
unreachable!(
124-
"A path should never resolve \
120+
Some(resolve::Res::Local(_) | resolve::Res::Param { .. }) => unreachable!(
121+
" A path should never resolve \
125122
to a local or a parameter, as there is syntactic differentiation."
126-
)
123+
),
124+
Some(resolve::Res::ExportedItem(item_id, alias)) => {
125+
unreachable!("Exported items should have been resolved to their original definition in type checking. Found {:?} with alias {:?}", item_id, alias);
127126
}
128127
None => Ty::Err,
129128
}

0 commit comments

Comments
 (0)