diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 4612300635..62a8f4c951 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -121,7 +121,7 @@ pub(super) enum Error { #[error("duplicate intrinsic `{0}`")] #[diagnostic(help( - "each callable declared as `body intrinsic` must have a globally unique name" + "each callable declared as `body intrinsic` or `@SimulatableIntrinsic` must have a globally unique name" ))] #[diagnostic(code("Qsc.Resolve.DuplicateIntrinsic"))] DuplicateIntrinsic(String, #[label] Span), @@ -1733,7 +1733,8 @@ fn bind_callable( scope: &mut GlobalScope, ) -> Result<(), Vec> { let item_id = next_id(); - let status = ItemStatus::from_attrs(&ast_attrs_as_hir_attrs(item.attrs.as_ref())); + let attrs = ast_attrs_as_hir_attrs(item.attrs.as_ref()); + let status = ItemStatus::from_attrs(&attrs); let res = Res::Item(item_id, status); names.insert(decl.name.id, res.clone()); let mut errors = Vec::new(); @@ -1759,7 +1760,7 @@ fn bind_callable( } } - if decl_is_intrinsic(decl) && !scope.intrinsics.insert(Rc::clone(&decl.name.name)) { + if decl_is_intrinsic(decl, &attrs) && !scope.intrinsics.insert(Rc::clone(&decl.name.name)) { errors.push(Error::DuplicateIntrinsic( decl.name.name.to_string(), decl.name.span, @@ -1816,7 +1817,13 @@ fn bind_ty( } } -fn decl_is_intrinsic(decl: &CallableDecl) -> bool { +fn decl_is_intrinsic(decl: &CallableDecl, attrs: &[hir::Attr]) -> bool { + if attrs + .iter() + .any(|attr| matches!(attr, hir::Attr::SimulatableIntrinsic)) + { + return true; + } if let CallableBody::Specs(specs) = decl.body.as_ref() { specs .iter() diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index ce550558e7..e1771d2636 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2910,6 +2910,36 @@ fn disallow_duplicate_intrinsic_and_non_intrinsic_collision() { ); } +#[test] +fn disallow_duplicate_intrinsic_and_simulatableintrinsic() { + check( + indoc! {" + namespace A { + operation C() : Unit { + body intrinsic; + } + } + namespace B { + @SimulatableIntrinsic() + operation C() : Unit {} + } + "}, + &expect![[r#" + namespace namespace7 { + operation item1() : Unit { + body intrinsic; + } + } + namespace namespace8 { + @SimulatableIntrinsic() + operation item3() : Unit {} + } + + // DuplicateIntrinsic("C", Span { lo: 129, hi: 130 }) + "#]], + ); +} + #[allow(clippy::cast_possible_truncation)] fn check_locals(input: &str, expect: &Expect) { let parts = input.split('↘').collect::>();