Skip to content

Commit 66f67dd

Browse files
authored
Lower extern declaration (#2274)
2 parents af0bd05 + d1fb2d7 commit 66f67dd

File tree

7 files changed

+170
-16
lines changed

7 files changed

+170
-16
lines changed

compiler/qsc_qasm3/src/parser/ast.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -711,19 +711,24 @@ impl Display for ClassicalArgument {
711711
#[derive(Clone, Debug)]
712712
pub enum ExternParameter {
713713
ArrayReference(ArrayReferenceType, Span),
714-
Quantum(Option<Expr>, Span),
715714
Scalar(ScalarType, Span),
716715
}
717716

717+
impl ExternParameter {
718+
#[must_use]
719+
pub fn span(&self) -> Span {
720+
match self {
721+
ExternParameter::ArrayReference(_, span) | ExternParameter::Scalar(_, span) => *span,
722+
}
723+
}
724+
}
725+
718726
impl Display for ExternParameter {
719727
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
720728
match self {
721729
ExternParameter::Scalar(ty, span) => {
722730
write!(f, "{span}: {ty}")
723731
}
724-
ExternParameter::Quantum(expr, span) => {
725-
write!(f, "{span}: {expr:?}")
726-
}
727732
ExternParameter::ArrayReference(ty, span) => {
728733
write!(f, "{span}: {ty}")
729734
}
@@ -741,7 +746,6 @@ impl WithSpan for ExternParameter {
741746
fn with_span(self, span: Span) -> Self {
742747
match self {
743748
ExternParameter::Scalar(ty, _) => ExternParameter::Scalar(ty, span),
744-
ExternParameter::Quantum(expr, _) => ExternParameter::Quantum(expr, span),
745749
ExternParameter::ArrayReference(ty, _) => ExternParameter::ArrayReference(ty, span),
746750
}
747751
}

compiler/qsc_qasm3/src/parser/mut_visit.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -801,10 +801,6 @@ pub fn walk_extern_parameter(vis: &mut impl MutVisitor, param: &mut ExternParame
801801
vis.visit_span(span);
802802
vis.visit_array_ref_type(ty);
803803
}
804-
ExternParameter::Quantum(expr, span) => {
805-
vis.visit_span(span);
806-
expr.iter_mut().for_each(|expr| vis.visit_expr(expr));
807-
}
808804
ExternParameter::Scalar(ty, span) => {
809805
vis.visit_span(span);
810806
vis.visit_scalar_type(ty);

compiler/qsc_qasm3/src/semantic/ast.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,17 +1087,17 @@ impl Display for QuantumGateDefinition {
10871087
#[derive(Clone, Debug)]
10881088
pub struct ExternDecl {
10891089
pub span: Span,
1090-
pub ident: Box<Ident>,
1091-
pub params: List<ExternParameter>,
1092-
pub return_type: Option<ScalarType>,
1090+
pub symbol_id: SymbolId,
1091+
pub params: Box<[crate::types::Type]>,
1092+
pub return_type: crate::types::Type,
10931093
}
10941094

10951095
impl Display for ExternDecl {
10961096
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
10971097
writeln_header(f, "ExternDecl", self.span)?;
1098-
writeln_field(f, "ident", &self.ident)?;
1098+
writeln_field(f, "symbol_id", &self.symbol_id)?;
10991099
writeln_list_field(f, "parameters", &self.params)?;
1100-
write_opt_field(f, "return_type", self.return_type.as_ref())
1100+
write_field(f, "return_type", &self.return_type)
11011101
}
11021102
}
11031103

compiler/qsc_qasm3/src/semantic/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ pub enum SemanticErrorKind {
9393
#[error("Designator is too large.")]
9494
#[diagnostic(code("Qsc.Qasm3.Compile.DesignatorTooLarge"))]
9595
DesignatorTooLarge(#[label] Span),
96+
#[error("Extern declarations must be done in global scope.")]
97+
#[diagnostic(code("Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope"))]
98+
ExternDeclarationInNonGlobalScope(#[label] Span),
9699
#[error("Failed to compile all expressions in expression list.")]
97100
#[diagnostic(code("Qsc.Qasm3.Compile.FailedToCompileExpressionList"))]
98101
FailedToCompileExpressionList(#[label] Span),
@@ -296,6 +299,9 @@ impl SemanticErrorKind {
296299
Self::DefDeclarationInNonGlobalScope(span + offset)
297300
}
298301
Self::DesignatorTooLarge(span) => Self::DesignatorTooLarge(span + offset),
302+
Self::ExternDeclarationInNonGlobalScope(span) => {
303+
Self::ExternDeclarationInNonGlobalScope(span + offset)
304+
}
299305
Self::FailedToCompileExpressionList(span) => {
300306
Self::FailedToCompileExpressionList(span + offset)
301307
}

compiler/qsc_qasm3/src/semantic/lowerer.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,8 +1166,67 @@ impl Lowerer {
11661166
}
11671167

11681168
fn lower_extern(&mut self, stmt: &syntax::ExternDecl) -> semantic::StmtKind {
1169-
self.push_unimplemented_error_message("extern stmt", stmt.span);
1170-
semantic::StmtKind::Err
1169+
// 1. Check that we are in the global scope. QASM3 semantics
1170+
// only allow extern declarations in the global scope.
1171+
if !self.symbols.is_current_scope_global() {
1172+
let kind = SemanticErrorKind::ExternDeclarationInNonGlobalScope(stmt.span);
1173+
self.push_semantic_error(kind);
1174+
}
1175+
1176+
// 2. Build the parameter's type.
1177+
let mut params = Vec::with_capacity(stmt.params.len());
1178+
let mut qsharp_params = Vec::with_capacity(stmt.params.len());
1179+
1180+
for param in &stmt.params {
1181+
let ty = self.lower_extern_param(param);
1182+
let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, param.span());
1183+
params.push(ty);
1184+
qsharp_params.push(qsharp_ty);
1185+
}
1186+
1187+
// 2. Build the return type.
1188+
let (return_ty, qsharp_return_ty) = if let Some(ty) = &stmt.return_type {
1189+
let ty_span = ty.span;
1190+
let tydef = syntax::TypeDef::Scalar(ty.clone());
1191+
let ty = self.get_semantic_type_from_tydef(&tydef, false);
1192+
let qsharp_ty = self.convert_semantic_type_to_qsharp_type(&ty, ty_span);
1193+
(Rc::new(ty), qsharp_ty)
1194+
} else {
1195+
(
1196+
Rc::new(crate::semantic::types::Type::Void),
1197+
crate::types::Type::Tuple(Default::default()),
1198+
)
1199+
};
1200+
1201+
// 3. Push the extern symbol to the symbol table.
1202+
#[allow(clippy::cast_possible_truncation)]
1203+
let arity = stmt.params.len() as u32;
1204+
let name = stmt.ident.name.clone();
1205+
let name_span = stmt.ident.span;
1206+
let ty = crate::semantic::types::Type::Function(params.into(), return_ty.clone());
1207+
let kind = crate::types::CallableKind::Function;
1208+
let qsharp_ty = crate::types::Type::Callable(kind, arity, 0);
1209+
let symbol = Symbol::new(&name, name_span, ty, qsharp_ty, IOKind::Default);
1210+
let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol);
1211+
1212+
semantic::StmtKind::ExternDecl(semantic::ExternDecl {
1213+
span: stmt.span,
1214+
symbol_id,
1215+
params: qsharp_params.into(),
1216+
return_type: qsharp_return_ty,
1217+
})
1218+
}
1219+
1220+
fn lower_extern_param(&mut self, param: &syntax::ExternParameter) -> Type {
1221+
let tydef = match param {
1222+
syntax::ExternParameter::ArrayReference(array_reference_type, _) => {
1223+
syntax::TypeDef::ArrayReference(array_reference_type.clone())
1224+
}
1225+
syntax::ExternParameter::Scalar(scalar_type, _) => {
1226+
syntax::TypeDef::Scalar(scalar_type.clone())
1227+
}
1228+
};
1229+
self.get_semantic_type_from_tydef(&tydef, false)
11711230
}
11721231

11731232
fn lower_for_stmt(&mut self, stmt: &syntax::ForStmt) -> semantic::StmtKind {

compiler/qsc_qasm3/src/semantic/tests/decls.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod bool;
77
mod complex;
88
mod creg;
99
mod duration;
10+
mod extern_decl;
1011
mod float;
1112
mod int;
1213
mod qreg;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use crate::semantic::tests::check_stmt_kind;
5+
use expect_test::expect;
6+
7+
#[test]
8+
fn void_no_args() {
9+
check_stmt_kind(
10+
"extern f();",
11+
&expect![[r#"
12+
ExternDecl [0-11]:
13+
symbol_id: 8
14+
parameters: <empty>
15+
return_type: ()"#]],
16+
);
17+
}
18+
19+
#[test]
20+
fn void_one_arg() {
21+
check_stmt_kind(
22+
"extern f(int);",
23+
&expect![[r#"
24+
ExternDecl [0-14]:
25+
symbol_id: 8
26+
parameters:
27+
Int
28+
return_type: ()"#]],
29+
);
30+
}
31+
32+
#[test]
33+
fn void_multiple_args() {
34+
check_stmt_kind(
35+
"extern f(uint, int, float, bit, bool);",
36+
&expect![[r#"
37+
ExternDecl [0-38]:
38+
symbol_id: 8
39+
parameters:
40+
Int
41+
Int
42+
Double
43+
Result
44+
bool
45+
return_type: ()"#]],
46+
);
47+
}
48+
49+
#[test]
50+
fn return_type() {
51+
check_stmt_kind(
52+
"extern f() -> int;",
53+
&expect![[r#"
54+
ExternDecl [0-18]:
55+
symbol_id: 8
56+
parameters: <empty>
57+
return_type: Int"#]],
58+
);
59+
}
60+
61+
#[test]
62+
fn no_allowed_in_non_global_scope() {
63+
check_stmt_kind(
64+
"{ extern f(); }",
65+
&expect![[r#"
66+
Program:
67+
version: <none>
68+
statements:
69+
Stmt [0-15]:
70+
annotations: <empty>
71+
kind: Block [0-15]:
72+
Stmt [2-13]:
73+
annotations: <empty>
74+
kind: ExternDecl [2-13]:
75+
symbol_id: 8
76+
parameters: <empty>
77+
return_type: ()
78+
79+
[Qsc.Qasm3.Compile.DefDeclarationInNonGlobalScope
80+
81+
x Extern declarations must be done in global scope.
82+
,-[test:1:3]
83+
1 | { extern f(); }
84+
: ^^^^^^^^^^^
85+
`----
86+
]"#]],
87+
);
88+
}

0 commit comments

Comments
 (0)