From 016153c8c90adb6fc8c8e6e7f29a8cb171973ee1 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 14 Mar 2024 15:46:47 -0700 Subject: [PATCH 01/76] wip --- compiler/qsc_frontend/src/resolve.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 98cd5249df..af653f94b1 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -778,6 +778,26 @@ impl GlobalTable { let mut errors = Vec::new(); for node in &*package.nodes { match node { + // if a namespace is nested, create child namespaces + TopLevelNode::Namespace(namespace) if namespace.name.to_string().contains(".") => { + let namespaces = namespace.name.to_string().split('.').collect::>(); + let mut parent = self.scope; + for namespace in namespaces { + + self.names.insert( + namespace.id, + Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), + ); + self.scope.namespaces.insert(Rc::clone(&namespace.name)); + } + bind_global_items( + &mut self.names, + &mut self.scope, + namespace, + assigner, + &mut errors, + ); + } TopLevelNode::Namespace(namespace) => { bind_global_items( &mut self.names, @@ -1195,6 +1215,7 @@ fn resolve_explicit_opens<'a>( candidates } +/// Creates an [`ItemId`] for an item that is local to this package (internal to it). fn intrapackage(item: LocalItemId) -> ItemId { ItemId { package: None, From b8a801eac057bbf658bae5b6a87d7acc81b1c641 Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 18 Mar 2024 13:37:39 -0700 Subject: [PATCH 02/76] wip --- compiler/qsc_ast/src/ast.rs | 49 +++++++++++-- compiler/qsc_ast/src/mut_visit.rs | 21 +++++- compiler/qsc_ast/src/visit.rs | 15 ++-- .../qsc_frontend/src/compile/preprocess.rs | 18 ++--- compiler/qsc_frontend/src/compile/tests.rs | 72 ++++++++++++++++++- compiler/qsc_frontend/src/lower.rs | 8 ++- compiler/qsc_frontend/src/resolve.rs | 46 ++++++++---- compiler/qsc_parse/src/item.rs | 21 ++++-- compiler/qsc_parse/src/prim.rs | 33 +++------ 9 files changed, 215 insertions(+), 68 deletions(-) diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index be3f599617..aa8495a0ee 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -164,7 +164,7 @@ pub struct Namespace { /// The documentation. pub doc: Rc, /// The namespace name. - pub name: Box, + pub name: VecIdent, /// The items in the namespace. pub items: Box<[Box]>, } @@ -259,7 +259,7 @@ pub enum ItemKind { #[default] Err, /// An `open` item for a namespace with an optional alias. - Open(Box, Option>), + Open(VecIdent, Option>), /// A `newtype` declaration. Ty(Box, Box), } @@ -1273,7 +1273,7 @@ pub struct Path { /// The span. pub span: Span, /// The namespace. - pub namespace: Option>, + pub namespace: Option, /// The declaration name. pub name: Box, } @@ -1281,7 +1281,14 @@ pub struct Path { impl Display for Path { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(ns) = &self.namespace { - write!(f, "Path {} {} ({}) ({})", self.id, self.span, ns, self.name)?; + write!( + f, + "Path {} {} ({}) ({})", + self.id, + self.span, + ns, + self.name + )?; } else { write!(f, "Path {} {} ({})", self.id, self.span, self.name)?; } @@ -1306,6 +1313,40 @@ pub struct Ident { pub name: Rc, } +#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] +pub struct VecIdent(pub Vec); + +impl From for Vec> { + fn from(v: VecIdent) -> Self { + v.0.iter().map(|i| i.name.clone()).collect() + } +} + + +impl From> for VecIdent { + fn from(v: Vec) -> Self { + VecIdent(v) + } +} + +impl From for Vec { + fn from(v: VecIdent) -> Self { + v.0 + } +} + +impl Display for VecIdent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let idents = self.0.iter(); + write!(f, "{}", idents.map(|i| i.name.to_string()).collect::>().join(".")) + } +} +impl VecIdent { + pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { + self.0.iter() + } +} + impl Default for Ident { fn default() -> Self { Ident { diff --git a/compiler/qsc_ast/src/mut_visit.rs b/compiler/qsc_ast/src/mut_visit.rs index 8b47a31bb4..993b8c2d29 100644 --- a/compiler/qsc_ast/src/mut_visit.rs +++ b/compiler/qsc_ast/src/mut_visit.rs @@ -75,10 +75,14 @@ pub trait MutVisitor: Sized { fn visit_ident(&mut self, ident: &mut Ident) { walk_ident(self, ident); } + fn visit_vec_ident(&mut self, ident: &mut crate::ast::VecIdent) { + walk_vec_ident(self, ident); + } fn visit_span(&mut self, _: &mut Span) {} } + pub fn walk_package(vis: &mut impl MutVisitor, package: &mut Package) { package.nodes.iter_mut().for_each(|n| match n { TopLevelNode::Namespace(ns) => vis.visit_namespace(ns), @@ -89,7 +93,8 @@ pub fn walk_package(vis: &mut impl MutVisitor, package: &mut Package) { pub fn walk_namespace(vis: &mut impl MutVisitor, namespace: &mut Namespace) { vis.visit_span(&mut namespace.span); - vis.visit_ident(&mut namespace.name); + vis.visit_vec_ident(&mut namespace.name); + namespace.items.iter_mut().for_each(|i| vis.visit_item(i)); } @@ -104,7 +109,7 @@ pub fn walk_item(vis: &mut impl MutVisitor, item: &mut Item) { ItemKind::Callable(decl) => vis.visit_callable_decl(decl), ItemKind::Err => {} ItemKind::Open(ns, alias) => { - vis.visit_ident(ns); + vis.visit_vec_ident(ns); alias.iter_mut().for_each(|a| vis.visit_ident(a)); } ItemKind::Ty(ident, def) => { @@ -333,10 +338,20 @@ pub fn walk_qubit_init(vis: &mut impl MutVisitor, init: &mut QubitInit) { pub fn walk_path(vis: &mut impl MutVisitor, path: &mut Path) { vis.visit_span(&mut path.span); - path.namespace.iter_mut().for_each(|n| vis.visit_ident(n)); + if let Some(ref mut namespace) = path.namespace { + vis.visit_vec_ident(namespace) + } + if let Some(ref mut ns) = path.namespace { + vis.visit_vec_ident(ns) + } vis.visit_ident(&mut path.name); } pub fn walk_ident(vis: &mut impl MutVisitor, ident: &mut Ident) { vis.visit_span(&mut ident.span); } +pub fn walk_vec_ident(vis: &mut impl MutVisitor, ident: &mut crate::ast::VecIdent) { + for ref mut ident in ident.0.iter_mut() { + vis.visit_ident(ident) + } +} \ No newline at end of file diff --git a/compiler/qsc_ast/src/visit.rs b/compiler/qsc_ast/src/visit.rs index f9afe8b098..baf465314e 100644 --- a/compiler/qsc_ast/src/visit.rs +++ b/compiler/qsc_ast/src/visit.rs @@ -2,10 +2,7 @@ // Licensed under the MIT License. use crate::ast::{ - Attr, Block, CallableBody, CallableDecl, Expr, ExprKind, FunctorExpr, FunctorExprKind, Ident, - Item, ItemKind, Namespace, Package, Pat, PatKind, Path, QubitInit, QubitInitKind, SpecBody, - SpecDecl, Stmt, StmtKind, StringComponent, TopLevelNode, Ty, TyDef, TyDefKind, TyKind, - Visibility, + Attr, Block, CallableBody, CallableDecl, Expr, ExprKind, FunctorExpr, FunctorExprKind, Ident, Item, ItemKind, Namespace, Package, Pat, PatKind, Path, QubitInit, QubitInitKind, SpecBody, SpecDecl, Stmt, StmtKind, StringComponent, TopLevelNode, Ty, TyDef, TyDefKind, TyKind, VecIdent, Visibility }; pub trait Visitor<'a>: Sized { @@ -72,6 +69,8 @@ pub trait Visitor<'a>: Sized { } fn visit_ident(&mut self, _: &'a Ident) {} + + fn visit_vec_ident(&mut self, _: &'a VecIdent) {} } pub fn walk_package<'a>(vis: &mut impl Visitor<'a>, package: &'a Package) { @@ -83,7 +82,7 @@ pub fn walk_package<'a>(vis: &mut impl Visitor<'a>, package: &'a Package) { } pub fn walk_namespace<'a>(vis: &mut impl Visitor<'a>, namespace: &'a Namespace) { - vis.visit_ident(&namespace.name); + vis.visit_vec_ident(&namespace.name); namespace.items.iter().for_each(|i| vis.visit_item(i)); } @@ -94,7 +93,7 @@ pub fn walk_item<'a>(vis: &mut impl Visitor<'a>, item: &'a Item) { ItemKind::Err => {} ItemKind::Callable(decl) => vis.visit_callable_decl(decl), ItemKind::Open(ns, alias) => { - vis.visit_ident(ns); + vis.visit_vec_ident(&ns); alias.iter().for_each(|a| vis.visit_ident(a)); } ItemKind::Ty(ident, def) => { @@ -300,6 +299,8 @@ pub fn walk_qubit_init<'a>(vis: &mut impl Visitor<'a>, init: &'a QubitInit) { } pub fn walk_path<'a>(vis: &mut impl Visitor<'a>, path: &'a Path) { - path.namespace.iter().for_each(|n| vis.visit_ident(n)); + if let Some(ref ns) = path.namespace { + vis.visit_vec_ident(ns); + } vis.visit_ident(&path.name); } diff --git a/compiler/qsc_frontend/src/compile/preprocess.rs b/compiler/qsc_frontend/src/compile/preprocess.rs index ae84c4b201..dedcda60d4 100644 --- a/compiler/qsc_frontend/src/compile/preprocess.rs +++ b/compiler/qsc_frontend/src/compile/preprocess.rs @@ -14,7 +14,7 @@ use super::{ConfigAttr, RuntimeCapabilityFlags}; #[derive(PartialEq, Hash, Clone, Debug)] pub struct TrackedName { pub name: Rc, - pub namespace: Rc, + pub namespace: Vec>, } pub(crate) struct Conditional { @@ -51,12 +51,12 @@ impl MutVisitor for Conditional { ItemKind::Callable(callable) => { self.included_names.push(TrackedName { name: callable.name.name.clone(), - namespace: namespace.name.name.clone(), + namespace: namespace.name.clone().iter().map(|x| Rc::from(x.to_string())).collect(), }); } ItemKind::Ty(ident, _) => self.included_names.push(TrackedName { name: ident.name.clone(), - namespace: namespace.name.name.clone(), + namespace: namespace.name.clone().iter().map(|x| Rc::from(x.to_string())).collect(), }), _ => {} } @@ -66,12 +66,12 @@ impl MutVisitor for Conditional { ItemKind::Callable(callable) => { self.dropped_names.push(TrackedName { name: callable.name.name.clone(), - namespace: namespace.name.name.clone(), + namespace: namespace.name.clone().iter().map(|x| Rc::from(x.to_string())).collect(), }); } ItemKind::Ty(ident, _) => self.dropped_names.push(TrackedName { name: ident.name.clone(), - namespace: namespace.name.name.clone(), + namespace: namespace.name.clone().iter().map(|x| Rc::from(x.to_string())).collect(), }), _ => {} } @@ -89,12 +89,12 @@ impl MutVisitor for Conditional { ItemKind::Callable(callable) => { self.included_names.push(TrackedName { name: callable.name.name.clone(), - namespace: Rc::from(""), + namespace: Vec::default(), }); } ItemKind::Ty(ident, _) => self.included_names.push(TrackedName { name: ident.name.clone(), - namespace: Rc::from(""), + namespace: Vec::default(), }), _ => {} } @@ -103,12 +103,12 @@ impl MutVisitor for Conditional { ItemKind::Callable(callable) => { self.dropped_names.push(TrackedName { name: callable.name.name.clone(), - namespace: Rc::from(""), + namespace: Vec::default(), }); } ItemKind::Ty(ident, _) => self.dropped_names.push(TrackedName { name: ident.name.clone(), - namespace: Rc::from(""), + namespace: Vec::default() }), _ => {} } diff --git a/compiler/qsc_frontend/src/compile/tests.rs b/compiler/qsc_frontend/src/compile/tests.rs index b5f86a937d..f5f68b8d3f 100644 --- a/compiler/qsc_frontend/src/compile/tests.rs +++ b/compiler/qsc_frontend/src/compile/tests.rs @@ -1182,7 +1182,7 @@ fn reject_use_qubit_block_syntax_if_preview_feature_is_on() { // we have the v2 preview syntax feature enabled X(q); }; - + } } "} @@ -1255,3 +1255,73 @@ fn accept_use_qubit_block_syntax_if_preview_feature_is_off() { ); assert!(unit.errors.is_empty(), "{:#?}", unit.errors); } + +#[test] +fn hierarchical_namespace_basic() { + let lib_sources = SourceMap::new( + [( + "lib".into(), + indoc! {" + namespace Foo.Bar { + operation Baz() : Unit {} + } + namespace Main { + open Foo; + operation Main() : Unit { + Bar.Baz(); + } + } + "} + .into(), + )], + None, + ); + + let mut store = PackageStore::new(super::core()); + let lib = compile( + &store, + &[], + lib_sources, + RuntimeCapabilityFlags::all(), + LanguageFeatures::default(), + ); + assert!(lib.errors.is_empty(), "{:#?}", lib.errors); + let lib = store.insert(lib); + + let sources = SourceMap::new( + [( + "test".into(), + indoc! {" + namespace Test { + operation Bar() : Unit { body intrinsic; } + } + "} + .into(), + )], + None, + ); + + let unit = compile( + &store, + &[lib], + sources, + RuntimeCapabilityFlags::all(), + LanguageFeatures::default(), + ); + expect![[r#" + [ + Error( + Resolve( + DuplicateIntrinsic( + "Bar", + Span { + lo: 31, + hi: 34, + }, + ), + ), + ), + ] + "#]] + .assert_debug_eq(&unit.errors); +} diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs index 8ba1b6f609..6496732a61 100644 --- a/compiler/qsc_frontend/src/lower.rs +++ b/compiler/qsc_frontend/src/lower.rs @@ -125,7 +125,7 @@ impl With<'_> { pub(super) fn lower_namespace(&mut self, namespace: &ast::Namespace) { let Some(&resolve::Res::Item(hir::ItemId { item: id, .. }, _)) = - self.names.get(namespace.name.id) + self.names.get(namespace.id) else { panic!("namespace should have item ID"); }; @@ -137,7 +137,7 @@ impl With<'_> { .filter_map(|i| self.lower_item(ItemScope::Global, i)) .collect(); - let name = self.lower_ident(&namespace.name); + let name = self.lower_vec_ident(&namespace.name); self.lowerer.items.push(hir::Item { id, span: namespace.span, @@ -737,6 +737,10 @@ impl With<'_> { new_id }) } + + fn lower_vec_ident(&self, name: &[ast::Ident]) -> hir::Ident { + todo!() + } } fn lower_visibility(visibility: &ast::Visibility) -> hir::Visibility { diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index af653f94b1..e1f798cfc7 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -17,7 +17,7 @@ use qsc_hir::{ ty::{ParamId, Prim}, }; use rustc_hash::{FxHashMap, FxHashSet}; -use std::{collections::hash_map::Entry, rc::Rc, str::FromStr, vec}; +use std::{collections::hash_map::Entry, fmt::Display, rc::Rc, str::FromStr, vec}; use thiserror::Error; use crate::compile::preprocess::TrackedName; @@ -236,11 +236,28 @@ pub enum LocalKind { Var(NodeId), } +/// An ID that corresponds to a namespace in the global scope. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct NamespaceId(usize); +impl NamespaceId { + pub fn new(value: usize) -> Self { + Self(value) + } +} + +impl Display for NamespaceId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Namespace {}", self.0) + } +} + #[derive(Debug, Clone, Default)] pub struct GlobalScope { tys: FxHashMap, FxHashMap, Res>>, terms: FxHashMap, FxHashMap, Res>>, - namespaces: FxHashSet>, + // this is basically an index map, where indices are used as + // namespace ids + namespaces: Vec>, intrinsics: FxHashSet>, } @@ -252,6 +269,12 @@ impl GlobalScope { }; namespaces.get(namespace).and_then(|items| items.get(name)) } + + fn insert_namespace(&mut self, name: Rc) -> NamespaceId { + let id = self.namespaces.len(); + self.namespaces.insert(id, name); + NamespaceId::new(id) + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -413,7 +436,7 @@ impl Resolver { { self.errors.push(Error::NotAvailable( name, - format!("{}.{}", dropped_name.namespace, dropped_name.name), + format!("{}.{}", dropped_name.namespace.into_iter().collect::>().join("."), dropped_name.name), span, )); } else { @@ -764,7 +787,7 @@ impl GlobalTable { scope: GlobalScope { tys, terms: FxHashMap::default(), - namespaces: FxHashSet::default(), + namespaces: Vec::default(), intrinsics: FxHashSet::default(), }, } @@ -779,16 +802,14 @@ impl GlobalTable { for node in &*package.nodes { match node { // if a namespace is nested, create child namespaces - TopLevelNode::Namespace(namespace) if namespace.name.to_string().contains(".") => { - let namespaces = namespace.name.to_string().split('.').collect::>(); + TopLevelNode::Namespace(namespace) => { let mut parent = self.scope; - for namespace in namespaces { - + for namespace in namespace.name { self.names.insert( namespace.id, Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), ); - self.scope.namespaces.insert(Rc::clone(&namespace.name)); + self.scope.namespaces.push(Rc::clone(&namespace.name)); } bind_global_items( &mut self.names, @@ -841,7 +862,7 @@ impl GlobalTable { } } (global::Kind::Namespace, hir::Visibility::Public) => { - self.scope.namespaces.insert(global.name); + let namespace_id = self.scope.insert_namespace(global.name); } (_, hir::Visibility::Internal) => {} } @@ -860,7 +881,7 @@ fn bind_global_items( namespace.name.id, Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), ); - scope.namespaces.insert(Rc::clone(&namespace.name.name)); + let namespace_id = scope.insert_namespace(Rc::clone(&namespace.name.name)); for item in &*namespace.items { match bind_global_item( @@ -1009,13 +1030,14 @@ fn resolve<'a>( globals: &GlobalScope, scopes: impl Iterator, name: &Ident, - namespace: &Option>, + namespace: &Option>, ) -> Result { let scopes = scopes.collect::>(); let mut candidates = FxHashMap::default(); let mut vars = true; let name_str = &(*name.name); let namespace = namespace.as_ref().map_or("", |i| &i.name); + dbg!(&"checking namespace", namespace); for scope in scopes { if namespace.is_empty() { if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, name_str) { diff --git a/compiler/qsc_parse/src/item.rs b/compiler/qsc_parse/src/item.rs index 14375d5b6c..47e65101ee 100644 --- a/compiler/qsc_parse/src/item.rs +++ b/compiler/qsc_parse/src/item.rs @@ -10,7 +10,7 @@ mod tests; use super::{ expr::expr, keyword::Keyword, - prim::{dot_ident, ident, many, opt, pat, seq, token}, + prim::{ident, many, opt, pat, seq, token}, scan::ParserContext, stmt, ty::{self, ty}, @@ -18,7 +18,7 @@ use super::{ }; use crate::{ lex::{Delim, TokenKind}, - prim::{barrier, recovering, recovering_token, shorten}, + prim::{barrier, path, recovering, recovering_token, shorten}, stmt::check_semis, ty::array_or_arrow, ErrorKind, @@ -136,7 +136,7 @@ fn parse_namespace(s: &mut ParserContext) -> Result { let lo = s.peek().span.lo; let doc = parse_doc(s).unwrap_or_default(); token(s, TokenKind::Keyword(Keyword::Namespace))?; - let name = dot_ident(s)?; + let name = path(s)?; token(s, TokenKind::Open(Delim::Brace))?; let items = barrier(s, &[TokenKind::Close(Delim::Brace)], parse_many)?; recovering_token(s, TokenKind::Close(Delim::Brace)); @@ -144,7 +144,11 @@ fn parse_namespace(s: &mut ParserContext) -> Result { id: NodeId::default(), span: s.span(lo), doc: doc.into(), - name, + name: { + let mut buf = name.namespace.clone().unwrap_or_default(); + buf.0.push(*name.name); + buf + }, items: items.into_boxed_slice(), }) } @@ -202,14 +206,17 @@ fn parse_visibility(s: &mut ParserContext) -> Result { fn parse_open(s: &mut ParserContext) -> Result> { token(s, TokenKind::Keyword(Keyword::Open))?; - let name = dot_ident(s)?; + let mut name = vec![*(ident(s)?)]; + while let Ok(_dot) = token(s, TokenKind::Dot) { + name.push(*(ident(s)?)); + } let alias = if token(s, TokenKind::Keyword(Keyword::As)).is_ok() { - Some(dot_ident(s)?) + Some(ident(s)?) } else { None }; token(s, TokenKind::Semi)?; - Ok(Box::new(ItemKind::Open(name, alias))) + Ok(Box::new(ItemKind::Open(name.into(), alias))) } fn parse_newtype(s: &mut ParserContext) -> Result> { diff --git a/compiler/qsc_parse/src/prim.rs b/compiler/qsc_parse/src/prim.rs index 96f647347d..9ef7452cb5 100644 --- a/compiler/qsc_parse/src/prim.rs +++ b/compiler/qsc_parse/src/prim.rs @@ -77,24 +77,6 @@ pub(super) fn ident(s: &mut ParserContext) -> Result> { } } -/// This function parses a [Path] from the given context -/// and converts it into a single ident, which contains dots (`.`) -pub(super) fn dot_ident(s: &mut ParserContext) -> Result> { - let p = path(s)?; - let mut name = String::new(); - if let Some(namespace) = p.namespace { - name.push_str(&namespace.name); - name.push('.'); - } - name.push_str(&p.name.name); - - Ok(Box::new(Ident { - id: p.id, - span: p.span, - name: name.into(), - })) -} - /// A `path` is a dot-separated list of idents like "Foo.Bar.Baz" /// this can be either a namespace name (in an open statement or namespace declaration) or /// it can be a direct reference to something in a namespace, like `Microsoft.Quantum.Diagnostics.DumpMachine()` @@ -110,11 +92,16 @@ pub(super) fn path(s: &mut ParserContext) -> Result> { (Some(first), Some(last)) => { let lo = first.span.lo; let hi = last.span.hi; - Some(Box::new(Ident { - id: NodeId::default(), - span: Span { lo, hi }, - name: join(parts.iter().map(|i| &i.name), ".").into(), - })) + Some( + parts + .iter() + .map(|part| Ident { + id: NodeId::default(), + span: part.span, + name: part.name.clone(), + }) + .collect::>().into(), + ) } _ => None, }; From a83a1e910dc9877d389f29fdcd987b309442067f Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 18 Mar 2024 14:43:41 -0700 Subject: [PATCH 03/76] wip --- compiler/qsc_frontend/src/lower.rs | 2 +- compiler/qsc_frontend/src/resolve.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs index 6496732a61..b85affe38e 100644 --- a/compiler/qsc_frontend/src/lower.rs +++ b/compiler/qsc_frontend/src/lower.rs @@ -137,7 +137,7 @@ impl With<'_> { .filter_map(|i| self.lower_item(ItemScope::Global, i)) .collect(); - let name = self.lower_vec_ident(&namespace.name); + let name = self.lower_vec_ident(&namespace.name.0); self.lowerer.items.push(hir::Item { id, span: namespace.span, diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index e1f798cfc7..45930a413b 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -6,7 +6,7 @@ mod tests; use miette::Diagnostic; use qsc_ast::{ - ast::{self, CallableBody, CallableDecl, Ident, NodeId, SpecBody, SpecGen, TopLevelNode}, + ast::{self, CallableBody, CallableDecl, Ident, NodeId, SpecBody, SpecGen, TopLevelNode, VecIdent}, visit::{self as ast_visit, walk_attr, Visitor as AstVisitor}, }; use qsc_data_structures::{index_map::IndexMap, span::Span}; @@ -257,7 +257,7 @@ pub struct GlobalScope { terms: FxHashMap, FxHashMap, Res>>, // this is basically an index map, where indices are used as // namespace ids - namespaces: Vec>, + namespaces: Vec>>, intrinsics: FxHashSet>, } @@ -486,9 +486,9 @@ impl Resolver { } } - fn bind_open(&mut self, name: &ast::Ident, alias: &Option>) { + fn bind_open(&mut self, name: &ast::VecIdent, alias: &Option) { let alias = alias.as_ref().map_or("".into(), |a| Rc::clone(&a.name)); - if self.globals.namespaces.contains(&name.name) { + if self.globals.namespaces.contains(&name.into()) { self.current_scope_mut() .opens .entry(alias) @@ -1030,7 +1030,7 @@ fn resolve<'a>( globals: &GlobalScope, scopes: impl Iterator, name: &Ident, - namespace: &Option>, + namespace: &Option, ) -> Result { let scopes = scopes.collect::>(); let mut candidates = FxHashMap::default(); From 5d6a33a3ae08ca00e8e6d68ef18b6c71b6346cb0 Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 18 Mar 2024 15:11:00 -0700 Subject: [PATCH 04/76] wip --- compiler/qsc_ast/src/ast.rs | 15 +++++++++++++++ compiler/qsc_frontend/src/resolve.rs | 12 ++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index aa8495a0ee..f8338e5e61 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -1323,6 +1323,14 @@ impl From for Vec> { } +impl From<&VecIdent> for Vec> { + fn from(v: &VecIdent) -> Self { + v.0.iter().map(|i| i.name.clone()).collect() + } +} + + + impl From> for VecIdent { fn from(v: Vec) -> Self { VecIdent(v) @@ -1345,6 +1353,13 @@ impl VecIdent { pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { self.0.iter() } + + pub fn span(&self) -> Span { + Span { + lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), + hi: self.0.last().map(|i| i.span.hi).unwrap_or_default(), + } + } } impl Default for Ident { diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 45930a413b..3a5787b219 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -270,9 +270,9 @@ impl GlobalScope { namespaces.get(namespace).and_then(|items| items.get(name)) } - fn insert_namespace(&mut self, name: Rc) -> NamespaceId { + fn insert_namespace(&mut self, name: VecIdent) -> NamespaceId { let id = self.namespaces.len(); - self.namespaces.insert(id, name); + self.namespaces.insert(id, name.into()); NamespaceId::new(id) } } @@ -292,7 +292,7 @@ enum NameKind { #[derive(Debug, Clone)] struct Open { - namespace: Rc, + namespace: Vec>, span: Span, } @@ -494,12 +494,12 @@ impl Resolver { .entry(alias) .or_default() .push(Open { - namespace: Rc::clone(&name.name), - span: name.span, + namespace: name.into(), + span: name.span(), }); } else { self.errors - .push(Error::NotFound(name.name.to_string(), name.span)); + .push(Error::NotFound(name.to_string(), name.span())); } } From e5ec1937a525301c8ab54403b206186cce2d5f76 Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 18 Mar 2024 16:12:49 -0700 Subject: [PATCH 05/76] wip --- compiler/qsc_frontend/src/resolve.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 3a5787b219..0ecb0301d0 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -257,7 +257,11 @@ pub struct GlobalScope { terms: FxHashMap, FxHashMap, Res>>, // this is basically an index map, where indices are used as // namespace ids - namespaces: Vec>>, + // TODO maybe what we can do here is only store top-level namespaces here + // and bury the rest in the hierarchy? + // or, store the hierarchical structure right here on the below field, + // and then map the namespace IDs to a vec of entries + namespaces: Vec>, intrinsics: FxHashSet>, } @@ -279,7 +283,7 @@ impl GlobalScope { #[derive(Debug, Clone, Eq, PartialEq)] enum ScopeKind { - Namespace(Rc), + Namespace(Vec>), Callable, Block, } @@ -486,7 +490,7 @@ impl Resolver { } } - fn bind_open(&mut self, name: &ast::VecIdent, alias: &Option) { + fn bind_open(&mut self, name: &ast::VecIdent, alias: &Option>) { let alias = alias.as_ref().map_or("".into(), |a| Rc::clone(&a.name)); if self.globals.namespaces.contains(&name.into()) { self.current_scope_mut() @@ -616,7 +620,7 @@ impl With<'_> { impl AstVisitor<'_> for With<'_> { fn visit_namespace(&mut self, namespace: &ast::Namespace) { - let kind = ScopeKind::Namespace(Rc::clone(&namespace.name.name)); + let kind = ScopeKind::Namespace(namespace.name.into()); self.with_scope(namespace.span, kind, |visitor| { for item in &*namespace.items { if let ast::ItemKind::Open(name, alias) = &*item.kind { @@ -804,12 +808,13 @@ impl GlobalTable { // if a namespace is nested, create child namespaces TopLevelNode::Namespace(namespace) => { let mut parent = self.scope; - for namespace in namespace.name { - self.names.insert( - namespace.id, - Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), - ); - self.scope.namespaces.push(Rc::clone(&namespace.name)); + for namespace in namespace.name.iter() { + todo!("add nested namespaces here") + // self.names.insert( + // namespace.id, + // Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), + // ); + // parent.namespaces.push(Rc::clone(&namespace.name)); } bind_global_items( &mut self.names, From a18ef5d3238204c14585d0b2a9edd10a42d80bec Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 19 Mar 2024 11:22:59 -0700 Subject: [PATCH 06/76] it builds?? --- compiler/qsc_frontend/src/resolve.rs | 60 ++++++++++++++-------------- language_service/src/completion.rs | 4 +- language_service/src/name_locator.rs | 3 +- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 0ecb0301d0..22d78d7888 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -266,18 +266,17 @@ pub struct GlobalScope { } impl GlobalScope { - fn get(&self, kind: NameKind, namespace: &str, name: &str) -> Option<&Res> { - let namespaces = match kind { - NameKind::Ty => &self.tys, - NameKind::Term => &self.terms, - }; - namespaces.get(namespace).and_then(|items| items.get(name)) + fn get(&self, kind: NameKind, namespace: &[Rc], name: &str) -> Option<&Res> { + todo!("resolve Vec> to namespace id") + // let namespaces = match kind { + // NameKind::Ty => &self.tys, + // NameKind::Term => &self.terms, + // }; + // namespaces.get(namespace).and_then(|items| items.get(name)) } fn insert_namespace(&mut self, name: VecIdent) -> NamespaceId { - let id = self.namespaces.len(); - self.namespaces.insert(id, name.into()); - NamespaceId::new(id) + todo!("store namespace in nested fashion"); } } @@ -440,7 +439,7 @@ impl Resolver { { self.errors.push(Error::NotAvailable( name, - format!("{}.{}", dropped_name.namespace.into_iter().collect::>().join("."), dropped_name.name), + format!("{}.{}", dropped_name.namespace.iter().map(|x| x.to_string()).collect::>().join("."), dropped_name.name), span, )); } else { @@ -492,7 +491,7 @@ impl Resolver { fn bind_open(&mut self, name: &ast::VecIdent, alias: &Option>) { let alias = alias.as_ref().map_or("".into(), |a| Rc::clone(&a.name)); - if self.globals.namespaces.contains(&name.into()) { + if self.globals.namespaces.contains(todo!("refactor this contains method to do a nested lookup")) { self.current_scope_mut() .opens .entry(alias) @@ -620,7 +619,7 @@ impl With<'_> { impl AstVisitor<'_> for With<'_> { fn visit_namespace(&mut self, namespace: &ast::Namespace) { - let kind = ScopeKind::Namespace(namespace.name.into()); + let kind = ScopeKind::Namespace(namespace.name.clone().into()); self.with_scope(namespace.span, kind, |visitor| { for item in &*namespace.items { if let ast::ItemKind::Open(name, alias) = &*item.kind { @@ -807,7 +806,6 @@ impl GlobalTable { match node { // if a namespace is nested, create child namespaces TopLevelNode::Namespace(namespace) => { - let mut parent = self.scope; for namespace in namespace.name.iter() { todo!("add nested namespaces here") // self.names.insert( @@ -867,7 +865,8 @@ impl GlobalTable { } } (global::Kind::Namespace, hir::Visibility::Public) => { - let namespace_id = self.scope.insert_namespace(global.name); + todo!("insert the namespace into the global scope") + // let namespace_id = self.scope.insert(global.name); } (_, hir::Visibility::Internal) => {} } @@ -882,17 +881,18 @@ fn bind_global_items( assigner: &mut Assigner, errors: &mut Vec, ) { - names.insert( - namespace.name.id, - Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), - ); - let namespace_id = scope.insert_namespace(Rc::clone(&namespace.name.name)); + todo!("what is going on below?"); + // names.insert( + // namespace.name.id, + // Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), + // ); + let namespace_id = scope.insert_namespace(namespace.name); for item in &*namespace.items { match bind_global_item( names, scope, - &namespace.name.name, + namespace_id, || intrapackage(assigner.next_item()), item, ) { @@ -947,7 +947,7 @@ fn ast_attrs_as_hir_attrs(attrs: &[Box]) -> Vec { fn bind_global_item( names: &mut Names, scope: &mut GlobalScope, - namespace: &Rc, + namespace: NamespaceId, next_id: impl FnOnce() -> ItemId, item: &ast::Item, ) -> Result<(), Vec> { @@ -960,7 +960,7 @@ fn bind_global_item( let mut errors = Vec::new(); match scope .terms - .entry(Rc::clone(namespace)) + .entry(todo!("refactor this to use namespace id")) .or_default() .entry(Rc::clone(&decl.name.name)) { @@ -995,12 +995,12 @@ fn bind_global_item( match ( scope .terms - .entry(Rc::clone(namespace)) + .entry(todo!("refactor this API to take namespace IDs")) .or_default() .entry(Rc::clone(&name.name)), scope .tys - .entry(Rc::clone(namespace)) + .entry(todo!("refactor this API to take namespace IDs")) .or_default() .entry(Rc::clone(&name.name)), ) { @@ -1041,7 +1041,9 @@ fn resolve<'a>( let mut candidates = FxHashMap::default(); let mut vars = true; let name_str = &(*name.name); - let namespace = namespace.as_ref().map_or("", |i| &i.name); + let namespace: &str = todo!("delete this"); + todo!("resolve Vec> to namespace id -- copilot suggestion. We need to handle nested resolve logic here"); + // let namespace = namespace.as_ref().map_or("", |i| &i.name); dbg!(&"checking namespace", namespace); for scope in scopes { if namespace.is_empty() { @@ -1093,7 +1095,7 @@ fn resolve<'a>( } if candidates.is_empty() { - if let Some(&res) = globals.get(kind, namespace, name_str) { + if let Some(&res) = globals.get(kind, todo!("should be able to pass namespace id in here"), name_str) { // An unopened global is the last resort. return Ok(res); } @@ -1119,8 +1121,8 @@ fn resolve<'a>( opens.sort_unstable_by_key(|open| open.span); Err(Error::Ambiguous { name: name_str.to_string(), - first_open: opens[0].namespace.to_string(), - second_open: opens[1].namespace.to_string(), + first_open: opens[0].namespace.join("."), + second_open: opens[1].namespace.join("."), name_span: name.span, first_open_span: opens[0].span, second_open_span: opens[1].span, @@ -1220,7 +1222,7 @@ fn resolve_implicit_opens<'a, 'b>( ) -> FxHashMap { let mut candidates = FxHashMap::default(); for namespace in namespaces { - if let Some(&res) = globals.get(kind, namespace, name) { + if let Some(&res) = globals.get(kind, todo!("this should become namespace id"), name) { candidates.insert(res, *namespace); } } diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index 13107e528d..f98854348e 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -617,7 +617,7 @@ enum Context { impl Visitor<'_> for ContextFinder { fn visit_namespace(&mut self, namespace: &'_ qsc::ast::Namespace) { if span_contains(namespace.span, self.offset) { - self.current_namespace_name = Some(namespace.name.name.clone()); + self.current_namespace_name = Some(todo!("how will completions work here? should we use the immediate namespace or the absolute namespace name?")); self.context = Context::Namespace; self.opens = vec![]; self.start_of_namespace = None; @@ -632,7 +632,7 @@ impl Visitor<'_> for ContextFinder { if let qsc::ast::ItemKind::Open(name, alias) = &*item.kind { self.opens.push(( - name.name.clone(), + todo!("should namespace open be using namespace ID or rc str?"),//name.into(), alias.as_ref().map(|alias| alias.name.clone()), )); } diff --git a/language_service/src/name_locator.rs b/language_service/src/name_locator.rs index d0d1676b01..868cd006cc 100644 --- a/language_service/src/name_locator.rs +++ b/language_service/src/name_locator.rs @@ -128,7 +128,8 @@ impl<'inner, 'package, T> Locator<'inner, 'package, T> { impl<'inner, 'package, T: Handler<'package>> Visitor<'package> for Locator<'inner, 'package, T> { fn visit_namespace(&mut self, namespace: &'package ast::Namespace) { if span_contains(namespace.span, self.offset) { - self.context.current_namespace = namespace.name.name.clone(); + todo!("should below line use absolute or relative ns?"); + //self.context.current_namespace = namespace.name.name.clone(); walk_namespace(self, namespace); } } From a041e5862bbe8b8fccb7cbbbd093d4b601c48be1 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 19 Mar 2024 11:31:43 -0700 Subject: [PATCH 07/76] wip --- compiler/qsc_frontend/src/lower.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs index b85affe38e..bd082bc4ea 100644 --- a/compiler/qsc_frontend/src/lower.rs +++ b/compiler/qsc_frontend/src/lower.rs @@ -739,7 +739,7 @@ impl With<'_> { } fn lower_vec_ident(&self, name: &[ast::Ident]) -> hir::Ident { - todo!() + todo!("should this return a vec of hir idents or just one hir ident? or maybe a namespace id?") } } From 4a644ef12aa582de361702ebb569cc107484bef6 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 19 Mar 2024 15:26:29 -0700 Subject: [PATCH 08/76] wip: add nested namespace structure --- compiler/qsc_frontend/src/resolve.rs | 131 ++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 11 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 22d78d7888..95c1d76e78 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -6,7 +6,9 @@ mod tests; use miette::Diagnostic; use qsc_ast::{ - ast::{self, CallableBody, CallableDecl, Ident, NodeId, SpecBody, SpecGen, TopLevelNode, VecIdent}, + ast::{ + self, CallableBody, CallableDecl, Ident, NodeId, SpecBody, SpecGen, TopLevelNode, VecIdent, + }, visit::{self as ast_visit, walk_attr, Visitor as AstVisitor}, }; use qsc_data_structures::{index_map::IndexMap, span::Span}; @@ -17,7 +19,13 @@ use qsc_hir::{ ty::{ParamId, Prim}, }; use rustc_hash::{FxHashMap, FxHashSet}; -use std::{collections::hash_map::Entry, fmt::Display, rc::Rc, str::FromStr, vec}; +use std::{ + collections::{hash_map::Entry, HashMap}, + fmt::Display, + rc::Rc, + str::FromStr, + vec, +}; use thiserror::Error; use crate::compile::preprocess::TrackedName; @@ -237,7 +245,7 @@ pub enum LocalKind { } /// An ID that corresponds to a namespace in the global scope. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] pub struct NamespaceId(usize); impl NamespaceId { pub fn new(value: usize) -> Self { @@ -253,19 +261,103 @@ impl Display for NamespaceId { #[derive(Debug, Clone, Default)] pub struct GlobalScope { - tys: FxHashMap, FxHashMap, Res>>, - terms: FxHashMap, FxHashMap, Res>>, + tys: FxHashMap, Res>>, + terms: FxHashMap, Res>>, // this is basically an index map, where indices are used as // namespace ids // TODO maybe what we can do here is only store top-level namespaces here // and bury the rest in the hierarchy? // or, store the hierarchical structure right here on the below field, // and then map the namespace IDs to a vec of entries - namespaces: Vec>, + namespaces: NamespaceTreeRoot, intrinsics: FxHashSet>, } +#[derive(Debug, Clone)] + +pub struct NamespaceTreeRoot { + assigner: usize, + tree: NamespaceTreeNode, +} +impl NamespaceTreeRoot { + fn insert_namespace(&mut self, name: VecIdent) -> NamespaceId { + self.assigner += 1; + let id = self.assigner; + let node = self.new_namespace_node(Default::default()); + let mut components_iter = name.iter(); + // construct the initial rover for the breadth-first insertion + // (this is like a BFS but we create a new node if one doesn't exist) + let mut rover_node = &mut self.tree; + // create the rest of the nodes + for component in components_iter { + rover_node = rover_node.children + .entry(components_iter.next().expect("can't create namespace with no names -- this is an internal invariant violation").name) + .or_insert_with(|| self.new_namespace_node(Default::default())); + } + + rover_node.id + } + fn new_namespace_node( + &mut self, + children: HashMap, NamespaceTreeNode>, + ) -> NamespaceTreeNode { + self.assigner += 1; + NamespaceTreeNode { + id: NamespaceId::new(self.assigner), + children, + } + } + + fn contains_namespace(&self, ns: &VecIdent) -> Option { + self.tree.contains_namespace(ns) + } +} +impl Default for NamespaceTreeRoot { + fn default() -> Self { + Self { + assigner: 0, + tree: NamespaceTreeNode { + children: HashMap::new(), + id: NamespaceId::new(0), + }, + } + } +} + +#[derive(Debug, Clone)] +pub struct NamespaceTreeNode { + children: HashMap, NamespaceTreeNode>, + id: NamespaceId, +} +impl NamespaceTreeNode { + fn get(&self, component: &Ident) -> Option<&NamespaceTreeNode> { + self.children.get(&component.name) + } + + fn contains(&self, ns: &VecIdent) -> bool { + self.contains_namespace(ns).is_some() + } + fn contains_namespace(&self, ns: &VecIdent) -> Option { + // look up a namespace in the tree and return the id + // do a breadth-first search through NamespaceTree for the namespace + // if it's not found, return None + let mut buf = Rc::new(self); + for component in ns.iter() { + if let Some(next_ns) = buf.get(component) { + buf = Rc::new(next_ns); + } else { + return None; + } + } + return Some(buf.id); + } +} + impl GlobalScope { + fn contains_namespace(&self, ns: &VecIdent) -> Option { + self.namespaces.contains_namespace(ns) + } + fn get(&self, kind: NameKind, namespace: &[Rc], name: &str) -> Option<&Res> { todo!("resolve Vec> to namespace id") // let namespaces = match kind { @@ -276,7 +368,7 @@ impl GlobalScope { } fn insert_namespace(&mut self, name: VecIdent) -> NamespaceId { - todo!("store namespace in nested fashion"); + self.namespaces.insert_namespace(name) } } @@ -439,7 +531,16 @@ impl Resolver { { self.errors.push(Error::NotAvailable( name, - format!("{}.{}", dropped_name.namespace.iter().map(|x| x.to_string()).collect::>().join("."), dropped_name.name), + format!( + "{}.{}", + dropped_name + .namespace + .iter() + .map(|x| x.to_string()) + .collect::>() + .join("."), + dropped_name.name + ), span, )); } else { @@ -491,7 +592,11 @@ impl Resolver { fn bind_open(&mut self, name: &ast::VecIdent, alias: &Option>) { let alias = alias.as_ref().map_or("".into(), |a| Rc::clone(&a.name)); - if self.globals.namespaces.contains(todo!("refactor this contains method to do a nested lookup")) { + if self + .globals + .namespaces + .contains(todo!("refactor this contains method to do a nested lookup")) + { self.current_scope_mut() .opens .entry(alias) @@ -790,7 +895,7 @@ impl GlobalTable { scope: GlobalScope { tys, terms: FxHashMap::default(), - namespaces: Vec::default(), + namespaces: Default::default(), intrinsics: FxHashSet::default(), }, } @@ -1095,7 +1200,11 @@ fn resolve<'a>( } if candidates.is_empty() { - if let Some(&res) = globals.get(kind, todo!("should be able to pass namespace id in here"), name_str) { + if let Some(&res) = globals.get( + kind, + todo!("should be able to pass namespace id in here"), + name_str, + ) { // An unopened global is the last resort. return Ok(res); } From ac8745df988b870b3e0a812cbc35311c8a996cb8 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 19 Mar 2024 16:06:42 -0700 Subject: [PATCH 09/76] Switch to using new namespace API --- compiler/qsc_frontend/src/resolve.rs | 32 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 95c1d76e78..6d24840023 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -280,18 +280,18 @@ pub struct NamespaceTreeRoot { tree: NamespaceTreeNode, } impl NamespaceTreeRoot { - fn insert_namespace(&mut self, name: VecIdent) -> NamespaceId { + fn insert_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.assigner += 1; let id = self.assigner; let node = self.new_namespace_node(Default::default()); - let mut components_iter = name.iter(); + let mut components_iter = name.into().iter(); // construct the initial rover for the breadth-first insertion // (this is like a BFS but we create a new node if one doesn't exist) let mut rover_node = &mut self.tree; // create the rest of the nodes for component in components_iter { rover_node = rover_node.children - .entry(components_iter.next().expect("can't create namespace with no names -- this is an internal invariant violation").name) + .entry(Rc::clone(components_iter.next().expect("can't create namespace with no names -- this is an internal invariant violation"))) .or_insert_with(|| self.new_namespace_node(Default::default())); } @@ -308,8 +308,8 @@ impl NamespaceTreeRoot { } } - fn contains_namespace(&self, ns: &VecIdent) -> Option { - self.tree.contains_namespace(ns) + fn find_namespace(&self, ns: &VecIdent) -> Option { + self.tree.find_namespace(ns) } } impl Default for NamespaceTreeRoot { @@ -335,9 +335,9 @@ impl NamespaceTreeNode { } fn contains(&self, ns: &VecIdent) -> bool { - self.contains_namespace(ns).is_some() + self.find_namespace(ns).is_some() } - fn contains_namespace(&self, ns: &VecIdent) -> Option { + fn find_namespace(&self, ns: &VecIdent) -> Option { // look up a namespace in the tree and return the id // do a breadth-first search through NamespaceTree for the namespace // if it's not found, return None @@ -354,8 +354,8 @@ impl NamespaceTreeNode { } impl GlobalScope { - fn contains_namespace(&self, ns: &VecIdent) -> Option { - self.namespaces.contains_namespace(ns) + fn find_namespace(&self, ns: &VecIdent) -> Option { + self.namespaces.find_namespace(ns) } fn get(&self, kind: NameKind, namespace: &[Rc], name: &str) -> Option<&Res> { @@ -367,7 +367,9 @@ impl GlobalScope { // namespaces.get(namespace).and_then(|items| items.get(name)) } - fn insert_namespace(&mut self, name: VecIdent) -> NamespaceId { + /// Creates a namespace in the namespace mapping. Note that namespaces are tracked separately from their + /// item contents. This returns a [NamespaceId] which you can use to add more tys and terms to the scope. + fn insert_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.namespaces.insert_namespace(name) } } @@ -595,7 +597,7 @@ impl Resolver { if self .globals .namespaces - .contains(todo!("refactor this contains method to do a nested lookup")) + .find_namespace(&name).is_some() { self.current_scope_mut() .opens @@ -887,8 +889,12 @@ impl GlobalTable { for (name, res) in builtins { core.insert(name, res); } - let mut tys: FxHashMap, FxHashMap, Res>> = FxHashMap::default(); - tys.insert("Microsoft.Quantum.Core".into(), core); + + let mut scope = GlobalScope::default(); + let ns = scope.insert_namespace(vec![Rc::from("Microsoft"), Rc::from("Quantum"), Rc::from("Core")]); + + let mut tys = FxHashMap::default(); + tys.insert(ns, core); Self { names: IndexMap::new(), From bf6561b0199d96bd82535fd66bf0993f24b880cc Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 20 Mar 2024 10:29:22 -0700 Subject: [PATCH 10/76] doc comment --- compiler/qsc_hir/src/global.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index 2d51e9a667..f334728371 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -33,6 +33,8 @@ pub struct Term { pub intrinsic: bool, } + +/// A lookup table used for looking up global core items for insertion in `qsc_passes`. #[derive(Default)] pub struct Table { tys: FxHashMap, FxHashMap, Ty>>, From c571f2efc1e6adf856987949dfbe341278434e0b Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 20 Mar 2024 13:15:25 -0700 Subject: [PATCH 11/76] start working on converting HIR to new structure --- compiler/qsc_ast/src/ast.rs | 1 - compiler/qsc_eval/src/lower.rs | 6 +- compiler/qsc_frontend/src/resolve.rs | 1 + compiler/qsc_hir/src/global.rs | 134 +++++++++++++++++++++++---- compiler/qsc_hir/src/hir.rs | 49 +++++++++- compiler/qsc_hir/src/mut_visit.rs | 16 +++- compiler/qsc_hir/src/visit.rs | 8 +- 7 files changed, 190 insertions(+), 25 deletions(-) diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index f8338e5e61..c21db9700d 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -1330,7 +1330,6 @@ impl From<&VecIdent> for Vec> { } - impl From> for VecIdent { fn from(v: Vec) -> Self { VecIdent(v) diff --git a/compiler/qsc_eval/src/lower.rs b/compiler/qsc_eval/src/lower.rs index 4fd96b469b..f62399e000 100644 --- a/compiler/qsc_eval/src/lower.rs +++ b/compiler/qsc_eval/src/lower.rs @@ -130,7 +130,8 @@ impl Lowerer { fn lower_item(&mut self, item: &hir::Item) -> fir::Item { let kind = match &item.kind { hir::ItemKind::Namespace(name, items) => { - let name = self.lower_ident(name); + let name = name.iter().map(Clone::clone).collect::>(); + let name = self.lower_vec_ident(&name); let items = items.iter().map(|i| lower_local_item_id(*i)).collect(); fir::ItemKind::Namespace(name, items) } @@ -516,6 +517,9 @@ impl Lowerer { name_span: field.name_span, } } + fn lower_vec_ident(&self, name: &[hir::Ident]) -> fir::Ident { + todo!("should this return a vec of hir idents or just one hir ident? or maybe a namespace id?") + } } fn lower_generics(generics: &[qsc_hir::ty::GenericParam]) -> Vec { diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 6d24840023..5559c41d84 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -279,6 +279,7 @@ pub struct NamespaceTreeRoot { assigner: usize, tree: NamespaceTreeNode, } + impl NamespaceTreeRoot { fn insert_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.assigner += 1; diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index f334728371..1247a90357 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -2,15 +2,15 @@ // Licensed under the MIT License. use crate::{ - hir::{Item, ItemId, ItemKind, ItemStatus, Package, PackageId, SpecBody, SpecGen, Visibility}, + hir::{Ident, Item, ItemId, ItemKind, ItemStatus, Package, PackageId, SpecBody, SpecGen, VecIdent, Visibility}, ty::Scheme, }; use qsc_data_structures::index_map; use rustc_hash::FxHashMap; -use std::rc::Rc; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; pub struct Global { - pub namespace: Rc, + pub namespace: Vec>, pub name: Rc, pub visibility: Visibility, pub status: ItemStatus, @@ -37,19 +37,20 @@ pub struct Term { /// A lookup table used for looking up global core items for insertion in `qsc_passes`. #[derive(Default)] pub struct Table { - tys: FxHashMap, FxHashMap, Ty>>, - terms: FxHashMap, FxHashMap, Term>>, + tys: FxHashMap, Ty>>, + terms: FxHashMap, Term>>, + namespaces: NamespaceTreeRoot } impl Table { #[must_use] - pub fn resolve_ty(&self, namespace: &str, name: &str) -> Option<&Ty> { - self.tys.get(namespace).and_then(|terms| terms.get(name)) + pub fn resolve_ty(&self, namespace: NamespaceId, name: &str) -> Option<&Ty> { + self.tys.get(&namespace).and_then(|terms| terms.get(name)) } #[must_use] - pub fn resolve_term(&self, namespace: &str, name: &str) -> Option<&Term> { - self.terms.get(namespace).and_then(|terms| terms.get(name)) + pub fn resolve_term(&self, namespace: NamespaceId, name: &str) -> Option<&Term> { + self.terms.get(&namespace).and_then(|terms| terms.get(name)) } } @@ -57,16 +58,18 @@ impl FromIterator for Table { fn from_iter>(iter: T) -> Self { let mut tys: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); let mut terms: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); + let mut namespaces = NamespaceTreeRoot::default(); for global in iter { + let namespace = namespaces.upsert_namespace(global.namespace); match global.kind { Kind::Ty(ty) => { - tys.entry(global.namespace) + tys.entry(namespace) .or_default() .insert(global.name, ty); } Kind::Term(term) => { terms - .entry(global.namespace) + .entry(namespace) .or_default() .insert(global.name, term); } @@ -74,10 +77,107 @@ impl FromIterator for Table { } } - Self { tys, terms } + // TODO; copy namespace root etc over here and + // create a namespace structure with IDs + Self { namespaces, tys, terms } } } +pub struct NamespaceTreeRoot { + assigner: usize, + tree: NamespaceTreeNode, +} + +impl NamespaceTreeRoot { + fn upsert_namespace(&mut self, name: impl Into>>) -> NamespaceId { + self.assigner += 1; + let id = self.assigner; + let node = self.new_namespace_node(Default::default()); + let mut components_iter = name.into(); + let mut components_iter = components_iter.iter(); + // construct the initial rover for the breadth-first insertion + // (this is like a BFS but we create a new node if one doesn't exist) + let self_cell = RefCell::new(self); + let mut rover_node = &mut self_cell.borrow_mut().tree; + // create the rest of the nodes + for component in components_iter { + rover_node = rover_node.children + .entry(Rc::clone(component)) + .or_insert_with(|| self_cell.borrow_mut().new_namespace_node(Default::default())); + } + + rover_node.id + } + fn new_namespace_node( + &mut self, + children: HashMap, NamespaceTreeNode>, + ) -> NamespaceTreeNode { + self.assigner += 1; + NamespaceTreeNode { + id: NamespaceId::new(self.assigner), + children, + } + } + + fn find_namespace(&self, ns: &VecIdent) -> Option { + self.tree.find_namespace(ns) + } +} +impl Default for NamespaceTreeRoot { + fn default() -> Self { + Self { + assigner: 0, + tree: NamespaceTreeNode { + children: HashMap::new(), + id: NamespaceId::new(0), + }, + } + } +} + +#[derive(Debug, Clone)] +pub struct NamespaceTreeNode { + children: HashMap, NamespaceTreeNode>, + id: NamespaceId, +} +impl NamespaceTreeNode { + fn get(&self, component: &Ident) -> Option<&NamespaceTreeNode> { + self.children.get(&component.name) + } + + fn contains(&self, ns: &VecIdent) -> bool { + self.find_namespace(ns).is_some() + } + fn find_namespace(&self, ns: &VecIdent) -> Option { + // look up a namespace in the tree and return the id + // do a breadth-first search through NamespaceTree for the namespace + // if it's not found, return None + let mut buf = Rc::new(self); + for component in ns.iter() { + if let Some(next_ns) = buf.get(component) { + buf = Rc::new(next_ns); + } else { + return None; + } + } + return Some(buf.id); + } +} + +/// An ID that corresponds to a namespace in the global scope. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] +pub struct NamespaceId(usize); +impl NamespaceId { + pub fn new(value: usize) -> Self { + Self(value) + } +} + +impl std::fmt::Display for NamespaceId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Namespace {}", self.0) + } +} pub struct PackageIter<'a> { id: Option, package: &'a Package, @@ -103,7 +203,7 @@ impl PackageIter<'_> { match (&item.kind, &parent) { (ItemKind::Callable(decl), Some(ItemKind::Namespace(namespace, _))) => Some(Global { - namespace: Rc::clone(&namespace.name), + namespace: namespace.into(), name: Rc::clone(&decl.name.name), visibility: item.visibility, status, @@ -115,7 +215,7 @@ impl PackageIter<'_> { }), (ItemKind::Ty(name, def), Some(ItemKind::Namespace(namespace, _))) => { self.next = Some(Global { - namespace: Rc::clone(&namespace.name), + namespace: namespace.into(), name: Rc::clone(&name.name), visibility: item.visibility, status, @@ -127,7 +227,7 @@ impl PackageIter<'_> { }); Some(Global { - namespace: Rc::clone(&namespace.name), + namespace: namespace.into(), name: Rc::clone(&name.name), visibility: item.visibility, status, @@ -135,8 +235,8 @@ impl PackageIter<'_> { }) } (ItemKind::Namespace(ident, _), None) => Some(Global { - namespace: "".into(), - name: Rc::clone(&ident.name), + namespace: ident.into(), + name: "".into(), visibility: Visibility::Public, status, kind: Kind::Namespace, diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 3390d8786b..979d362b52 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -327,7 +327,7 @@ pub enum ItemKind { /// A `function` or `operation` declaration. Callable(CallableDecl), /// A `namespace` declaration. - Namespace(Ident, Vec), + Namespace(VecIdent, Vec), /// A `newtype` declaration. Ty(Ident, Udt), } @@ -1129,6 +1129,53 @@ impl Display for QubitInitKind { } } +#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] +pub struct VecIdent(pub Vec); + +impl From for Vec> { + fn from(v: VecIdent) -> Self { + v.0.iter().map(|i| i.name.clone()).collect() + } +} + + +impl From<&VecIdent> for Vec> { + fn from(v: &VecIdent) -> Self { + v.0.iter().map(|i| i.name.clone()).collect() + } +} + + +impl From> for VecIdent { + fn from(v: Vec) -> Self { + VecIdent(v) + } +} + +impl From for Vec { + fn from(v: VecIdent) -> Self { + v.0 + } +} + +impl Display for VecIdent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let idents = self.0.iter(); + write!(f, "{}", idents.map(|i| i.name.to_string()).collect::>().join(".")) + } +} +impl VecIdent { + pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { + self.0.iter() + } + + pub fn span(&self) -> Span { + Span { + lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), + hi: self.0.last().map(|i| i.span.hi).unwrap_or_default(), + } + } +} /// An identifier. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Ident { diff --git a/compiler/qsc_hir/src/mut_visit.rs b/compiler/qsc_hir/src/mut_visit.rs index 00333b70d3..03a29bbebb 100644 --- a/compiler/qsc_hir/src/mut_visit.rs +++ b/compiler/qsc_hir/src/mut_visit.rs @@ -48,9 +48,18 @@ pub trait MutVisitor: Sized { walk_ident(self, ident); } - fn visit_span(&mut self, _: &mut Span) {} -} + fn visit_span(&mut self, _: &mut Span) { + } + fn visit_vec_ident(&mut self, ident: &mut crate::hir::VecIdent) { + walk_vec_ident(self, ident); + } +} +pub fn walk_vec_ident(vis: &mut impl MutVisitor, ident: &mut crate::hir::VecIdent) { + for ref mut ident in ident.0.iter_mut() { + vis.visit_ident(ident) + } +} pub fn walk_package(vis: &mut impl MutVisitor, package: &mut Package) { package.items.values_mut().for_each(|i| vis.visit_item(i)); package.stmts.iter_mut().for_each(|s| vis.visit_stmt(s)); @@ -62,7 +71,8 @@ pub fn walk_item(vis: &mut impl MutVisitor, item: &mut Item) { match &mut item.kind { ItemKind::Callable(decl) => vis.visit_callable_decl(decl), - ItemKind::Namespace(name, _) | ItemKind::Ty(name, _) => vis.visit_ident(name), + ItemKind::Namespace(name, _) => vis.visit_vec_ident(name), + ItemKind::Ty(name, _) => vis.visit_ident(name), } } diff --git a/compiler/qsc_hir/src/visit.rs b/compiler/qsc_hir/src/visit.rs index 6f9655c29a..f6e1558a90 100644 --- a/compiler/qsc_hir/src/visit.rs +++ b/compiler/qsc_hir/src/visit.rs @@ -3,7 +3,7 @@ use crate::hir::{ Block, CallableDecl, Expr, ExprKind, Ident, Item, ItemKind, Package, Pat, PatKind, QubitInit, - QubitInitKind, SpecBody, SpecDecl, Stmt, StmtKind, StringComponent, + QubitInitKind, SpecBody, SpecDecl, Stmt, StmtKind, StringComponent, VecIdent, }; pub trait Visitor<'a>: Sized { @@ -44,6 +44,9 @@ pub trait Visitor<'a>: Sized { } fn visit_ident(&mut self, _: &'a Ident) {} + + fn visit_vec_ident(&mut self, _: &'a VecIdent) {} + } pub fn walk_package<'a>(vis: &mut impl Visitor<'a>, package: &'a Package) { @@ -55,7 +58,8 @@ pub fn walk_package<'a>(vis: &mut impl Visitor<'a>, package: &'a Package) { pub fn walk_item<'a>(vis: &mut impl Visitor<'a>, item: &'a Item) { match &item.kind { ItemKind::Callable(decl) => vis.visit_callable_decl(decl), - ItemKind::Namespace(name, _) | ItemKind::Ty(name, _) => vis.visit_ident(name), + ItemKind::Namespace(name, _) => vis.visit_vec_ident(name), + ItemKind::Ty(name, _) => vis.visit_ident(name), } } From 21fac31c5455106de13efef77e5de1c432096ef6 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 20 Mar 2024 15:06:53 -0700 Subject: [PATCH 12/76] wip: refactor core namespaces into namespace ids --- compiler/qsc/src/interpret/debug.rs | 2 +- compiler/qsc_doc_gen/src/generate_docs.rs | 4 +-- compiler/qsc_frontend/src/compile/tests.rs | 5 ++- compiler/qsc_frontend/src/lower.rs | 2 +- compiler/qsc_frontend/src/resolve.rs | 28 ++++++++-------- compiler/qsc_hir/src/global.rs | 16 +++++++--- compiler/qsc_hir/src/hir.rs | 4 +++ compiler/qsc_passes/src/common.rs | 4 +-- compiler/qsc_passes/src/invert_block.rs | 7 +++- compiler/qsc_passes/src/loop_unification.rs | 7 +++- .../src/replace_qubit_allocation.rs | 32 +++++++++++++++---- 11 files changed, 76 insertions(+), 35 deletions(-) diff --git a/compiler/qsc/src/interpret/debug.rs b/compiler/qsc/src/interpret/debug.rs index 7674fc5e62..04975cf333 100644 --- a/compiler/qsc/src/interpret/debug.rs +++ b/compiler/qsc/src/interpret/debug.rs @@ -83,7 +83,7 @@ fn get_item_file_name(store: &PackageStore, id: StoreItemId) -> Option { #[must_use] fn get_ns_name(item: &Item) -> Option { if let ItemKind::Namespace(ns, _) = &item.kind { - Some(ns.name.to_string()) + Some(format!("{ns}")) } else { None } diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 2a19ce79ff..2c9167d29a 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -163,10 +163,10 @@ fn get_namespace(package: &Package, item: &Item) -> Option> { .expect("Could not resolve parent item id"); match &parent.kind { ItemKind::Namespace(name, _) => { - if name.name.starts_with("QIR") { + if name.starts_with("QIR") { None // We ignore "QIR" namespaces } else { - Some(name.name.clone()) + Some(format!("{name}").into()) } } _ => None, diff --git a/compiler/qsc_frontend/src/compile/tests.rs b/compiler/qsc_frontend/src/compile/tests.rs index f5f68b8d3f..f0a3b0b659 100644 --- a/compiler/qsc_frontend/src/compile/tests.rs +++ b/compiler/qsc_frontend/src/compile/tests.rs @@ -3,6 +3,8 @@ #![allow(clippy::needless_raw_string_hashes)] +use std::rc::Rc; + use crate::compile::RuntimeCapabilityFlags; use super::{compile, CompileUnit, Error, PackageStore, SourceMap}; @@ -339,9 +341,10 @@ fn insert_core_call() { impl MutVisitor for Inserter<'_> { fn visit_block(&mut self, block: &mut Block) { + let ns = self.core.find_namespace(vec![Rc::from("QIR"), Rc::from("Runtime")]).expect("QIR runtime should be inserted at instantiation of core Table"); let allocate = self .core - .resolve_term("QIR.Runtime", "__quantum__rt__qubit_allocate") + .resolve_term(ns, "__quantum__rt__qubit_allocate") .expect("qubit allocation should be in core"); let allocate_ty = allocate .scheme diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs index bd082bc4ea..83e445d6df 100644 --- a/compiler/qsc_frontend/src/lower.rs +++ b/compiler/qsc_frontend/src/lower.rs @@ -738,7 +738,7 @@ impl With<'_> { }) } - fn lower_vec_ident(&self, name: &[ast::Ident]) -> hir::Ident { + fn lower_vec_ident(&self, name: &[ast::Ident]) -> hir::VecIdent { todo!("should this return a vec of hir idents or just one hir ident? or maybe a namespace id?") } } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 5559c41d84..d5f7c11d75 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -20,11 +20,7 @@ use qsc_hir::{ }; use rustc_hash::{FxHashMap, FxHashSet}; use std::{ - collections::{hash_map::Entry, HashMap}, - fmt::Display, - rc::Rc, - str::FromStr, - vec, + cell::RefCell, collections::{hash_map::Entry, HashMap}, fmt::Display, rc::Rc, str::FromStr, vec }; use thiserror::Error; @@ -285,17 +281,18 @@ impl NamespaceTreeRoot { self.assigner += 1; let id = self.assigner; let node = self.new_namespace_node(Default::default()); - let mut components_iter = name.into().iter(); + let mut components_iter = name.into(); + let mut components_iter = components_iter.iter(); // construct the initial rover for the breadth-first insertion // (this is like a BFS but we create a new node if one doesn't exist) - let mut rover_node = &mut self.tree; + let self_cell = RefCell::new(self); + let mut rover_node = &mut self_cell.borrow_mut().tree; // create the rest of the nodes for component in components_iter { rover_node = rover_node.children - .entry(Rc::clone(components_iter.next().expect("can't create namespace with no names -- this is an internal invariant violation"))) - .or_insert_with(|| self.new_namespace_node(Default::default())); + .entry(Rc::clone(component)) + .or_insert_with(|| self_cell.borrow_mut().new_namespace_node(Default::default())); } - rover_node.id } fn new_namespace_node( @@ -370,7 +367,7 @@ impl GlobalScope { /// Creates a namespace in the namespace mapping. Note that namespaces are tracked separately from their /// item contents. This returns a [NamespaceId] which you can use to add more tys and terms to the scope. - fn insert_namespace(&mut self, name: impl Into>>) -> NamespaceId { + fn upsert_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.namespaces.insert_namespace(name) } } @@ -892,7 +889,7 @@ impl GlobalTable { } let mut scope = GlobalScope::default(); - let ns = scope.insert_namespace(vec![Rc::from("Microsoft"), Rc::from("Quantum"), Rc::from("Core")]); + let ns = scope.upsert_namespace(vec![Rc::from("Microsoft"), Rc::from("Quantum"), Rc::from("Core")]); let mut tys = FxHashMap::default(); tys.insert(ns, core); @@ -956,11 +953,12 @@ impl GlobalTable { global.visibility == hir::Visibility::Public || matches!(&global.kind, global::Kind::Term(t) if t.intrinsic) }) { + let namespace = self.scope.upsert_namespace(global.namespace); match (global.kind, global.visibility) { (global::Kind::Ty(ty), hir::Visibility::Public) => { self.scope .tys - .entry(global.namespace) + .entry(namespace) .or_default() .insert(global.name, Res::Item(ty.id, global.status)); } @@ -968,7 +966,7 @@ impl GlobalTable { if visibility == hir::Visibility::Public { self.scope .terms - .entry(global.namespace) + .entry(namespace) .or_default() .insert(global.name.clone(), Res::Item(term.id, global.status)); } @@ -998,7 +996,7 @@ fn bind_global_items( // namespace.name.id, // Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), // ); - let namespace_id = scope.insert_namespace(namespace.name); + let namespace_id = scope.upsert_namespace(namespace.name); for item in &*namespace.items { match bind_global_item( diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index 1247a90357..f7d6d64d9a 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -52,6 +52,11 @@ impl Table { pub fn resolve_term(&self, namespace: NamespaceId, name: &str) -> Option<&Term> { self.terms.get(&namespace).and_then(|terms| terms.get(name)) } + + pub fn find_namespace(&self, vec: impl Into>>) -> Option { + // find a namespace if it exists and return its id + self.namespaces.find_namespace(vec) + } } impl FromIterator for Table { @@ -119,8 +124,8 @@ impl NamespaceTreeRoot { } } - fn find_namespace(&self, ns: &VecIdent) -> Option { - self.tree.find_namespace(ns) + fn find_namespace(&self, ns: impl Into>>) -> Option { + self.tree.find_namespace(ns.into()) } } impl Default for NamespaceTreeRoot { @@ -141,17 +146,18 @@ pub struct NamespaceTreeNode { id: NamespaceId, } impl NamespaceTreeNode { - fn get(&self, component: &Ident) -> Option<&NamespaceTreeNode> { - self.children.get(&component.name) + fn get(&self, component: &Rc) -> Option<&NamespaceTreeNode> { + self.children.get(component) } fn contains(&self, ns: &VecIdent) -> bool { self.find_namespace(ns).is_some() } - fn find_namespace(&self, ns: &VecIdent) -> Option { + fn find_namespace(&self, ns: impl Into>>) -> Option { // look up a namespace in the tree and return the id // do a breadth-first search through NamespaceTree for the namespace // if it's not found, return None + let ns = ns.into(); let mut buf = Rc::new(self); for component in ns.iter() { if let Some(next_ns) = buf.get(component) { diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 979d362b52..7f54d57e83 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -1175,6 +1175,10 @@ impl VecIdent { hi: self.0.last().map(|i| i.span.hi).unwrap_or_default(), } } + + pub fn starts_with(&self, arg: &str) -> bool { + self.0.first().map(|i| &*i.name == arg).unwrap_or_default() + } } /// An identifier. #[derive(Clone, Debug, Eq, Hash, PartialEq)] diff --git a/compiler/qsc_passes/src/common.rs b/compiler/qsc_passes/src/common.rs index d258babad0..db0909ffd2 100644 --- a/compiler/qsc_passes/src/common.rs +++ b/compiler/qsc_passes/src/common.rs @@ -4,7 +4,7 @@ use qsc_data_structures::span::Span; use qsc_hir::{ assigner::Assigner, - global::Table, + global::{NamespaceId, Table}, hir::{ Expr, ExprKind, Field, Ident, Mutability, NodeId, Pat, PatKind, PrimField, Res, Stmt, StmtKind, @@ -81,7 +81,7 @@ impl IdentTemplate { pub(crate) fn create_gen_core_ref( core: &Table, - namespace: &str, + namespace: NamespaceId, name: &str, generics: Vec, span: Span, diff --git a/compiler/qsc_passes/src/invert_block.rs b/compiler/qsc_passes/src/invert_block.rs index f89cc06278..f5c11a1380 100644 --- a/compiler/qsc_passes/src/invert_block.rs +++ b/compiler/qsc_passes/src/invert_block.rs @@ -329,6 +329,11 @@ fn make_range_field(range_id: NodeId, field: PrimField) -> Expr { } fn make_array_index_range_reverse(core: &Table, arr_id: NodeId, arr_ty: &Ty) -> Expr { + let ns = core.find_namespace(vec![ + "Microsoft".into(), + "Quantum".into(), + "Core".into(), + ]).unwrap(); let len = Box::new(Expr { id: NodeId::default(), span: Span::default(), @@ -336,7 +341,7 @@ fn make_array_index_range_reverse(core: &Table, arr_id: NodeId, arr_ty: &Ty) -> kind: ExprKind::Call( Box::new(create_gen_core_ref( core, - "Microsoft.Quantum.Core", + ns, "Length", vec![GenericArg::Ty(arr_ty.clone())], Span::default(), diff --git a/compiler/qsc_passes/src/loop_unification.rs b/compiler/qsc_passes/src/loop_unification.rs index 147bbb4e24..103a5dcb93 100644 --- a/compiler/qsc_passes/src/loop_unification.rs +++ b/compiler/qsc_passes/src/loop_unification.rs @@ -133,9 +133,14 @@ impl LoopUni<'_> { let Ty::Array(item_ty) = &array_id.ty else { panic!("iterator should have array type"); }; + let ns = self.core.find_namespace(vec![ + "Microsoft".into(), + "Quantum".into(), + "Core".into(), + ]).unwrap(); let mut len_callee = create_gen_core_ref( self.core, - "Microsoft.Quantum.Core", + ns, "Length", vec![GenericArg::Ty((**item_ty).clone())], array_id.span, diff --git a/compiler/qsc_passes/src/replace_qubit_allocation.rs b/compiler/qsc_passes/src/replace_qubit_allocation.rs index 4f92984c2d..b7a056a2f9 100644 --- a/compiler/qsc_passes/src/replace_qubit_allocation.rs +++ b/compiler/qsc_passes/src/replace_qubit_allocation.rs @@ -238,9 +238,13 @@ impl<'a> ReplaceQubitAllocation<'a> { } fn create_alloc_stmt(&mut self, ident: &IdentTemplate) -> Stmt { + let ns = self.core.find_namespace(vec![ + "QIR".into(), + "Runtime".into(), + ]).unwrap(); let mut call_expr = create_gen_core_ref( self.core, - "QIR.Runtime", + ns, "__quantum__rt__qubit_allocate", Vec::new(), ident.span, @@ -250,9 +254,13 @@ impl<'a> ReplaceQubitAllocation<'a> { } fn create_array_alloc_stmt(&mut self, ident: &IdentTemplate, array_size: Expr) -> Stmt { + let ns = self.core.find_namespace(vec![ + "QIR".into(), + "Runtime".into(), + ]).unwrap(); let mut call_expr = create_gen_core_ref( self.core, - "QIR.Runtime", + ns, "AllocateQubitArray", Vec::new(), ident.span, @@ -262,9 +270,13 @@ impl<'a> ReplaceQubitAllocation<'a> { } fn create_dealloc_stmt(&mut self, ident: &IdentTemplate) -> Stmt { + let ns = self.core.find_namespace(vec![ + "QIR".into(), + "Runtime".into(), + ]).unwrap(); let mut call_expr = create_gen_core_ref( self.core, - "QIR.Runtime", + ns, "__quantum__rt__qubit_release", Vec::new(), ident.span, @@ -274,9 +286,13 @@ impl<'a> ReplaceQubitAllocation<'a> { } fn create_array_dealloc_stmt(&mut self, ident: &IdentTemplate) -> Stmt { + let ns = self.core.find_namespace(vec![ + "QIR".into(), + "Runtime".into(), + ]).unwrap(); let mut call_expr = create_gen_core_ref( self.core, - "QIR.Runtime", + ns, "ReleaseQubitArray", Vec::new(), ident.span, @@ -412,11 +428,15 @@ fn create_qubit_global_alloc( qubit_init: QubitInit, ) -> StmtKind { fn qubit_alloc_expr(assigner: &mut Assigner, core: &Table, qubit_init: QubitInit) -> Expr { + let ns = core.find_namespace(vec![ + "QIR".into(), + "Runtime".into(), + ]).unwrap(); match qubit_init.kind { QubitInitKind::Array(mut expr) => { let mut call_expr = create_gen_core_ref( core, - "QIR.Runtime", + ns, "AllocateQubitArray", Vec::new(), qubit_init.span, @@ -432,7 +452,7 @@ fn create_qubit_global_alloc( QubitInitKind::Single => { let mut call_expr = create_gen_core_ref( core, - "QIR.Runtime", + ns, "__quantum__rt__qubit_allocate", Vec::new(), qubit_init.span, From f52ed1733d81e6f13ab750a3ae7d9db23f41f6b5 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 21 Mar 2024 10:59:29 -0700 Subject: [PATCH 13/76] wip --- compiler/qsc_hir/src/hir.rs | 12 ++++++++++++ language_service/src/completion.rs | 17 +++++++++++------ language_service/src/references.rs | 3 ++- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 7f54d57e83..b6c63af5e2 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -1179,6 +1179,18 @@ impl VecIdent { pub fn starts_with(&self, arg: &str) -> bool { self.0.first().map(|i| &*i.name == arg).unwrap_or_default() } + + pub fn starts_with_sequence(&self, arg: &[&str]) -> bool { + if arg.len() > self.0.len() { + return false; + } + for (i, s) in arg.iter().enumerate() { + if &*self.0[i].name != *s { + return false; + } + } + return true; + } } /// An identifier. #[derive(Clone, Debug, Eq, Hash, PartialEq)] diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index f98854348e..e27d09b3c7 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -9,6 +9,7 @@ use crate::protocol::{CompletionItem, CompletionItemKind, CompletionList}; use crate::qsc_utils::{into_range, span_contains}; use qsc::ast::visit::{self, Visitor}; use qsc::display::{CodeDisplay, Lookup}; +use qsc::hir::global::NamespaceId; use qsc::hir::{ItemKind, Package, PackageId, Visibility}; use qsc::line_column::{Encoding, Position, Range}; use qsc::resolve::{Local, LocalKind}; @@ -264,7 +265,7 @@ impl CompletionListBuilder { compilation: &Compilation, opens: &[(Rc, Option>)], insert_open_range: Option, - current_namespace_name: &Option>, + current_namespace_name: &Option, indent: &String, ) { let core = &compilation @@ -386,7 +387,7 @@ impl CompletionListBuilder { package_id: PackageId, opens: &'a [(Rc, Option>)], insert_open_at: Option, - current_namespace_name: Option>, + current_namespace_name: Option, indent: &'a String, ) -> impl Iterator + 'a { let package = &compilation @@ -403,7 +404,7 @@ impl CompletionListBuilder { if let Some(item_id) = i.parent { if let Some(parent) = package.items.get(item_id) { if let ItemKind::Namespace(namespace, _) = &parent.kind { - if namespace.name.starts_with("Microsoft.Quantum.Unstable") { + if namespace.starts_with_sequence(&["Microsoft", "Quantum", "Unstable"]) { return None; } // If the item's visibility is internal, the item may be ignored @@ -411,10 +412,14 @@ impl CompletionListBuilder { if !is_user_package { return None; // ignore item if not in the user's package } + let namespace = compilation + .package_store + .get(package_id) + .expect("package should exist"); // ignore item if the user is not in the item's namespace match ¤t_namespace_name { Some(curr_ns) => { - if *curr_ns != namespace.name { + if *curr_ns != namespace { return None; } } @@ -602,7 +607,7 @@ struct ContextFinder { context: Context, opens: Vec<(Rc, Option>)>, start_of_namespace: Option, - current_namespace_name: Option>, + current_namespace_name: Option, } #[derive(Debug, PartialEq)] @@ -632,7 +637,7 @@ impl Visitor<'_> for ContextFinder { if let qsc::ast::ItemKind::Open(name, alias) = &*item.kind { self.opens.push(( - todo!("should namespace open be using namespace ID or rc str?"),//name.into(), + todo!("should namespace open be using namespace ID or rc str?"), //name.into(), alias.as_ref().map(|alias| alias.name.clone()), )); } diff --git a/language_service/src/references.rs b/language_service/src/references.rs index 6ec3bb6df0..c39ebbd564 100644 --- a/language_service/src/references.rs +++ b/language_service/src/references.rs @@ -188,7 +188,8 @@ impl<'a> ReferenceFinder<'a> { if self.include_declaration { let def_span = match &def.kind { hir::ItemKind::Callable(decl) => decl.name.span, - hir::ItemKind::Namespace(name, _) | hir::ItemKind::Ty(name, _) => name.span, + hir::ItemKind::Namespace(name, _) => name.span(), + hir::ItemKind::Ty(name, _) => name.span, }; locations.push( self.location( From e6c15ba0785b599316449e88f80f0c1bdb819610 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 21 Mar 2024 13:04:49 -0700 Subject: [PATCH 14/76] wip --- compiler/qsc_frontend/src/compile.rs | 14 ++++++++------ compiler/qsc_frontend/src/incremental.rs | 2 ++ compiler/qsc_frontend/src/resolve.rs | 10 +++++++--- compiler/qsc_frontend/src/resolve/tests.rs | 2 +- compiler/qsc_frontend/src/typeck/tests.rs | 2 +- compiler/qsc_hir/src/global.rs | 1 + language_service/src/completion.rs | 16 ++++++++++------ 7 files changed, 30 insertions(+), 17 deletions(-) diff --git a/compiler/qsc_frontend/src/compile.rs b/compiler/qsc_frontend/src/compile.rs index 65a6d432d9..7b77be2211 100644 --- a/compiler/qsc_frontend/src/compile.rs +++ b/compiler/qsc_frontend/src/compile.rs @@ -9,7 +9,7 @@ pub mod preprocess; use crate::{ error::WithSource, lower::{self, Lowerer}, - resolve::{self, Locals, Names, Resolver}, + resolve::{self, Locals, Names, NamespaceTreeRoot, Resolver}, typeck::{self, Checker, Table}, }; use bitflags::bitflags; @@ -29,7 +29,7 @@ use qsc_data_structures::{ }; use qsc_hir::{ assigner::Assigner as HirAssigner, - global, + global::{self}, hir::{self, PackageId}, validate::Validator as HirValidator, visit::Visitor as _, @@ -106,6 +106,7 @@ pub struct AstPackage { pub tys: Table, pub names: Names, pub locals: Locals, + pub namespaces: NamespaceTreeRoot, } #[derive(Debug, Default)] @@ -354,7 +355,7 @@ pub fn compile( ast_assigner.visit_package(&mut ast_package); AstValidator::default().visit_package(&ast_package); let mut hir_assigner = HirAssigner::new(); - let (names, locals, name_errors) = resolve_all( + let (names, locals, name_errors, namespaces) = resolve_all( store, dependencies, &mut hir_assigner, @@ -385,6 +386,7 @@ pub fn compile( tys, names, locals, + namespaces, }, assigner: hir_assigner, sources, @@ -489,7 +491,7 @@ fn resolve_all( assigner: &mut HirAssigner, package: &ast::Package, mut dropped_names: Vec, -) -> (Names, Locals, Vec) { +) -> (Names, Locals, Vec, NamespaceTreeRoot) { let mut globals = resolve::GlobalTable::new(); if let Some(unit) = store.get(PackageId::CORE) { globals.add_external_package(PackageId::CORE, &unit.package); @@ -507,9 +509,9 @@ fn resolve_all( let mut errors = globals.add_local_package(assigner, package); let mut resolver = Resolver::new(globals, dropped_names); resolver.with(assigner).visit_package(package); - let (names, locals, mut resolver_errors) = resolver.into_result(); + let (names, locals, mut resolver_errors, namespaces) = resolver.into_result(); errors.append(&mut resolver_errors); - (names, locals, errors) + (names, locals, errors, namespaces) } fn typeck_all( diff --git a/compiler/qsc_frontend/src/incremental.rs b/compiler/qsc_frontend/src/incremental.rs index 3d435dc7dc..7f83f6d99d 100644 --- a/compiler/qsc_frontend/src/incremental.rs +++ b/compiler/qsc_frontend/src/incremental.rs @@ -134,6 +134,7 @@ impl Compiler { names: self.resolver.names().clone(), locals: self.resolver.locals().clone(), tys: self.checker.table().clone(), + namespaces: self.resolver.namespaces().clone(), }, hir, }) @@ -173,6 +174,7 @@ impl Compiler { names: self.resolver.names().clone(), locals: self.resolver.locals().clone(), tys: self.checker.table().clone(), + namespaces: self.resolver.namespaces().clone(), }, hir, }) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index d5f7c11d75..67b5a7c443 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -306,7 +306,7 @@ impl NamespaceTreeRoot { } } - fn find_namespace(&self, ns: &VecIdent) -> Option { + pub fn find_namespace(&self, ns: &VecIdent) -> Option { self.tree.find_namespace(ns) } } @@ -456,8 +456,8 @@ impl Resolver { } } - pub(super) fn into_result(self) -> (Names, Locals, Vec) { - (self.names, self.locals, self.errors) + pub(super) fn into_result(self) -> (Names, Locals, Vec, NamespaceTreeRoot) { + (self.names, self.locals, self.errors, self.globals.namespaces) } pub(super) fn extend_dropped_names(&mut self, dropped_names: Vec) { @@ -673,6 +673,10 @@ impl Resolver { self.locals.get_scope_mut(scope_id) } + + pub(crate) fn namespaces(&self) -> NamespaceTreeRoot { + self.globals.namespaces.clone() + } } /// Constructed from a [Resolver] and an [Assigner], this structure implements `Visitor` diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index da9dd59e3f..f0d193da16 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -113,7 +113,7 @@ fn compile( let mut errors = globals.add_local_package(&mut assigner, &package); let mut resolver = Resolver::new(globals, dropped_names); resolver.with(&mut assigner).visit_package(&package); - let (names, locals, mut resolve_errors) = resolver.into_result(); + let (names, locals, mut resolve_errors, _namespaces) = resolver.into_result(); errors.append(&mut resolve_errors); (package, names, locals, errors) } diff --git a/compiler/qsc_frontend/src/typeck/tests.rs b/compiler/qsc_frontend/src/typeck/tests.rs index 1bc0886f46..bdafc29a75 100644 --- a/compiler/qsc_frontend/src/typeck/tests.rs +++ b/compiler/qsc_frontend/src/typeck/tests.rs @@ -88,7 +88,7 @@ fn compile(input: &str, entry_expr: &str) -> (Package, super::Table, Vec for Table { } } +#[derive(Debug)] pub struct NamespaceTreeRoot { assigner: usize, tree: NamespaceTreeNode, diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index e27d09b3c7..d73196d612 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -398,7 +398,13 @@ impl CompletionListBuilder { let display = CodeDisplay { compilation }; let is_user_package = compilation.user_package_id == package_id; - + let namespaces = compilation + .package_store + .get(package_id) + .expect("package should exist") + .ast + .namespaces + ; package.items.values().filter_map(move |i| { // We only want items whose parents are namespaces if let Some(item_id) = i.parent { @@ -412,14 +418,12 @@ impl CompletionListBuilder { if !is_user_package { return None; // ignore item if not in the user's package } - let namespace = compilation - .package_store - .get(package_id) - .expect("package should exist"); + let ns_id = namespaces.find_namespace(namespace).expect("namespace should exist"); + // ignore item if the user is not in the item's namespace match ¤t_namespace_name { Some(curr_ns) => { - if *curr_ns != namespace { + if *curr_ns != ns_id { return None; } } From 2b1e51d2f4fb51893eab7d829968e13877560823 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 21 Mar 2024 13:59:11 -0700 Subject: [PATCH 15/76] wip --- compiler/qsc/src/lib.rs | 4 ++-- compiler/qsc_data_structures/src/lib.rs | 2 +- compiler/qsc_frontend/src/resolve.rs | 10 ++++++++-- compiler/qsc_hir/src/global.rs | 8 +++++++- compiler/qsc_hir/src/hir.rs | 2 +- language_service/src/completion.rs | 16 ++++++++++++++++ 6 files changed, 35 insertions(+), 7 deletions(-) diff --git a/compiler/qsc/src/lib.rs b/compiler/qsc/src/lib.rs index f275f522ef..4a7cbaec29 100644 --- a/compiler/qsc/src/lib.rs +++ b/compiler/qsc/src/lib.rs @@ -9,11 +9,11 @@ pub mod location; pub mod target; pub use qsc_frontend::compile::{ - CompileUnit, PackageStore, RuntimeCapabilityFlags, SourceContents, SourceMap, SourceName, + CompileUnit, PackageStore, RuntimeCapabilityFlags, SourceContents, SourceMap, SourceName }; pub mod resolve { - pub use qsc_frontend::resolve::{Local, LocalKind, Locals, Res}; + pub use qsc_frontend::resolve::{Local, LocalKind, Locals, Res, NamespaceTreeRoot, NamespaceTreeNode}; } pub mod fir { diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index 53a850c6e0..e195032fce 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -6,4 +6,4 @@ pub mod functors; pub mod index_map; pub mod language_features; pub mod line_column; -pub mod span; +pub mod span; \ No newline at end of file diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 67b5a7c443..36258af1ba 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -277,6 +277,9 @@ pub struct NamespaceTreeRoot { } impl NamespaceTreeRoot { + pub fn tree(&self) -> &NamespaceTreeNode { + &self.tree + } fn insert_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.assigner += 1; let id = self.assigner; @@ -324,10 +327,13 @@ impl Default for NamespaceTreeRoot { #[derive(Debug, Clone)] pub struct NamespaceTreeNode { - children: HashMap, NamespaceTreeNode>, - id: NamespaceId, + pub children: HashMap, NamespaceTreeNode>, + pub id: NamespaceId, } impl NamespaceTreeNode { + pub fn children(&self) -> &HashMap, NamespaceTreeNode> { + &self.children + } fn get(&self, component: &Ident) -> Option<&NamespaceTreeNode> { self.children.get(&component.name) } diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index 9038fcea9b..55fb8cc551 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -88,7 +88,7 @@ impl FromIterator for Table { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct NamespaceTreeRoot { assigner: usize, tree: NamespaceTreeNode, @@ -147,6 +147,12 @@ pub struct NamespaceTreeNode { id: NamespaceId, } impl NamespaceTreeNode { + pub fn new(id: NamespaceId, children: HashMap, NamespaceTreeNode>) -> Self { + Self { + children, + id, + } + } fn get(&self, component: &Rc) -> Option<&NamespaceTreeNode> { self.children.get(component) } diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index b6c63af5e2..124379f042 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -5,7 +5,7 @@ #![warn(missing_docs)] -use crate::ty::{Arrow, FunctorSet, FunctorSetValue, GenericArg, GenericParam, Scheme, Ty, Udt}; +use crate::{global::NamespaceTreeRoot, ty::{Arrow, FunctorSet, FunctorSetValue, GenericArg, GenericParam, Scheme, Ty, Udt}}; use indenter::{indented, Indented}; use num_bigint::BigInt; use qsc_data_structures::{index_map::IndexMap, span::Span}; diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index d73196d612..a1953eee82 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -7,6 +7,7 @@ mod tests; use crate::compilation::{Compilation, CompilationKind}; use crate::protocol::{CompletionItem, CompletionItemKind, CompletionList}; use crate::qsc_utils::{into_range, span_contains}; +use qsc::ast::ast; use qsc::ast::visit::{self, Visitor}; use qsc::display::{CodeDisplay, Lookup}; use qsc::hir::global::NamespaceId; @@ -405,6 +406,7 @@ impl CompletionListBuilder { .ast .namespaces ; + convert_ast_namespaces_into_hir_namespaces(namespaces); package.items.values().filter_map(move |i| { // We only want items whose parents are namespaces if let Some(item_id) = i.parent { @@ -547,6 +549,20 @@ impl CompletionListBuilder { } } +fn convert_ast_namespaces_into_hir_namespaces(namespaces: qsc::resolve::NamespaceTreeRoot) -> qsc::hir::global::NamespaceTreeRoot { + let mut hir_namespaces = Vec::new(); + let mut assigner = 0; + for (namespace, qsc::resolve::NamespaceTreeNode { children, id }) in namespaces.tree().children() { + let hir_namespace = qsc::hir::global::NamespaceTreeNode::new(id, children.clone()); + if namespace.id > assigner { assigner = namespace.id + 1; }; + hir_namespaces.push(hir_namespace); + } + + qsc::hir::global::NamespaceTreeRoot { + namespaces: hir_namespaces, + } +} + fn local_completion( candidate: &Local, compilation: &Compilation, From 422542b12c28184c36b9c291cb5083828c776e57 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 21 Mar 2024 14:16:12 -0700 Subject: [PATCH 16/76] Refactor new namespace work into qsc_data_structures --- compiler/qsc/src/lib.rs | 4 +- compiler/qsc_data_structures/src/lib.rs | 112 +++++++++++++++++++++++- compiler/qsc_frontend/src/compile.rs | 6 +- compiler/qsc_frontend/src/resolve.rs | 105 +--------------------- compiler/qsc_hir/src/global.rs | 107 +--------------------- compiler/qsc_hir/src/hir.rs | 3 +- compiler/qsc_passes/src/common.rs | 4 +- language_service/src/completion.rs | 34 ++++--- 8 files changed, 142 insertions(+), 233 deletions(-) diff --git a/compiler/qsc/src/lib.rs b/compiler/qsc/src/lib.rs index 4a7cbaec29..072fbf4fe6 100644 --- a/compiler/qsc/src/lib.rs +++ b/compiler/qsc/src/lib.rs @@ -13,7 +13,7 @@ pub use qsc_frontend::compile::{ }; pub mod resolve { - pub use qsc_frontend::resolve::{Local, LocalKind, Locals, Res, NamespaceTreeRoot, NamespaceTreeNode}; + pub use qsc_frontend::resolve::{Local, LocalKind, Locals, Res}; } pub mod fir { @@ -32,7 +32,7 @@ pub mod project { pub use qsc_project::{DirEntry, EntryType, FileSystem, Manifest, ManifestDescriptor}; } -pub use qsc_data_structures::{language_features::LanguageFeatures, span::Span}; +pub use qsc_data_structures::{language_features::LanguageFeatures, span::Span, namespaces::*}; pub use qsc_passes::{PackageType, PassContext}; diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index e195032fce..71d29e17ac 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -6,4 +6,114 @@ pub mod functors; pub mod index_map; pub mod language_features; pub mod line_column; -pub mod span; \ No newline at end of file +pub mod span; +pub mod namespaces { + use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc}; + + #[derive(Debug, Clone)] + + pub struct NamespaceTreeRoot { + assigner: usize, + tree: NamespaceTreeNode, + } + + /// An ID that corresponds to a namespace in the global scope. + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] + pub struct NamespaceId(usize); + impl NamespaceId { + pub fn new(value: usize) -> Self { + Self(value) + } + } + + impl Display for NamespaceId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Namespace {}", self.0) + } + } + impl NamespaceTreeRoot { + pub fn tree(&self) -> &NamespaceTreeNode { + &self.tree + } + pub fn insert_namespace(&mut self, name: impl Into>>) -> NamespaceId { + self.assigner += 1; + let id = self.assigner; + let node = self.new_namespace_node(Default::default()); + let mut components_iter = name.into(); + let mut components_iter = components_iter.iter(); + // construct the initial rover for the breadth-first insertion + // (this is like a BFS but we create a new node if one doesn't exist) + let self_cell = RefCell::new(self); + let mut rover_node = &mut self_cell.borrow_mut().tree; + // create the rest of the nodes + for component in components_iter { + rover_node = rover_node + .children + .entry(Rc::clone(component)) + .or_insert_with(|| { + self_cell + .borrow_mut() + .new_namespace_node(Default::default()) + }); + } + rover_node.id + } + fn new_namespace_node( + &mut self, + children: HashMap, NamespaceTreeNode>, + ) -> NamespaceTreeNode { + self.assigner += 1; + NamespaceTreeNode { + id: NamespaceId::new(self.assigner), + children, + } + } + + pub fn find_namespace(&self, ns: impl Into>>) -> Option { + self.tree.find_namespace(ns) + } + } + impl Default for NamespaceTreeRoot { + fn default() -> Self { + Self { + assigner: 0, + tree: NamespaceTreeNode { + children: HashMap::new(), + id: NamespaceId::new(0), + }, + } + } + } + + #[derive(Debug, Clone)] + pub struct NamespaceTreeNode { + pub children: HashMap, NamespaceTreeNode>, + pub id: NamespaceId, + } + impl NamespaceTreeNode { + pub fn children(&self) -> &HashMap, NamespaceTreeNode> { + &self.children + } + fn get(&self, component: &Rc) -> Option<&NamespaceTreeNode> { + self.children.get(component) + } + + fn contains(&self, ns: impl Into>>) -> bool { + self.find_namespace(ns).is_some() + } + fn find_namespace(&self, ns: impl Into>>) -> Option { + // look up a namespace in the tree and return the id + // do a breadth-first search through NamespaceTree for the namespace + // if it's not found, return None + let mut buf = Rc::new(self); + for component in ns.into().iter() { + if let Some(next_ns) = buf.get(component) { + buf = Rc::new(next_ns); + } else { + return None; + } + } + return Some(buf.id); + } + } +} diff --git a/compiler/qsc_frontend/src/compile.rs b/compiler/qsc_frontend/src/compile.rs index 7b77be2211..a236f94727 100644 --- a/compiler/qsc_frontend/src/compile.rs +++ b/compiler/qsc_frontend/src/compile.rs @@ -9,7 +9,7 @@ pub mod preprocess; use crate::{ error::WithSource, lower::{self, Lowerer}, - resolve::{self, Locals, Names, NamespaceTreeRoot, Resolver}, + resolve::{self, Locals, Names, Resolver}, typeck::{self, Checker, Table}, }; use bitflags::bitflags; @@ -23,9 +23,7 @@ use qsc_ast::{ visit::Visitor as _, }; use qsc_data_structures::{ - index_map::{self, IndexMap}, - language_features::LanguageFeatures, - span::Span, + index_map::{self, IndexMap}, language_features::LanguageFeatures, namespaces::NamespaceTreeRoot, span::Span }; use qsc_hir::{ assigner::Assigner as HirAssigner, diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 36258af1ba..7e17a67fcb 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -11,7 +11,7 @@ use qsc_ast::{ }, visit::{self as ast_visit, walk_attr, Visitor as AstVisitor}, }; -use qsc_data_structures::{index_map::IndexMap, span::Span}; +use qsc_data_structures::{index_map::IndexMap, namespaces::{NamespaceId, NamespaceTreeRoot}, span::Span}; use qsc_hir::{ assigner::Assigner, global, @@ -240,20 +240,6 @@ pub enum LocalKind { Var(NodeId), } -/// An ID that corresponds to a namespace in the global scope. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] -pub struct NamespaceId(usize); -impl NamespaceId { - pub fn new(value: usize) -> Self { - Self(value) - } -} - -impl Display for NamespaceId { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Namespace {}", self.0) - } -} #[derive(Debug, Clone, Default)] pub struct GlobalScope { @@ -269,93 +255,6 @@ pub struct GlobalScope { intrinsics: FxHashSet>, } -#[derive(Debug, Clone)] - -pub struct NamespaceTreeRoot { - assigner: usize, - tree: NamespaceTreeNode, -} - -impl NamespaceTreeRoot { - pub fn tree(&self) -> &NamespaceTreeNode { - &self.tree - } - fn insert_namespace(&mut self, name: impl Into>>) -> NamespaceId { - self.assigner += 1; - let id = self.assigner; - let node = self.new_namespace_node(Default::default()); - let mut components_iter = name.into(); - let mut components_iter = components_iter.iter(); - // construct the initial rover for the breadth-first insertion - // (this is like a BFS but we create a new node if one doesn't exist) - let self_cell = RefCell::new(self); - let mut rover_node = &mut self_cell.borrow_mut().tree; - // create the rest of the nodes - for component in components_iter { - rover_node = rover_node.children - .entry(Rc::clone(component)) - .or_insert_with(|| self_cell.borrow_mut().new_namespace_node(Default::default())); - } - rover_node.id - } - fn new_namespace_node( - &mut self, - children: HashMap, NamespaceTreeNode>, - ) -> NamespaceTreeNode { - self.assigner += 1; - NamespaceTreeNode { - id: NamespaceId::new(self.assigner), - children, - } - } - - pub fn find_namespace(&self, ns: &VecIdent) -> Option { - self.tree.find_namespace(ns) - } -} -impl Default for NamespaceTreeRoot { - fn default() -> Self { - Self { - assigner: 0, - tree: NamespaceTreeNode { - children: HashMap::new(), - id: NamespaceId::new(0), - }, - } - } -} - -#[derive(Debug, Clone)] -pub struct NamespaceTreeNode { - pub children: HashMap, NamespaceTreeNode>, - pub id: NamespaceId, -} -impl NamespaceTreeNode { - pub fn children(&self) -> &HashMap, NamespaceTreeNode> { - &self.children - } - fn get(&self, component: &Ident) -> Option<&NamespaceTreeNode> { - self.children.get(&component.name) - } - - fn contains(&self, ns: &VecIdent) -> bool { - self.find_namespace(ns).is_some() - } - fn find_namespace(&self, ns: &VecIdent) -> Option { - // look up a namespace in the tree and return the id - // do a breadth-first search through NamespaceTree for the namespace - // if it's not found, return None - let mut buf = Rc::new(self); - for component in ns.iter() { - if let Some(next_ns) = buf.get(component) { - buf = Rc::new(next_ns); - } else { - return None; - } - } - return Some(buf.id); - } -} impl GlobalScope { fn find_namespace(&self, ns: &VecIdent) -> Option { @@ -601,7 +500,7 @@ impl Resolver { if self .globals .namespaces - .find_namespace(&name).is_some() + .find_namespace(name).is_some() { self.current_scope_mut() .opens diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index 55fb8cc551..38062c62de 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -5,7 +5,7 @@ use crate::{ hir::{Ident, Item, ItemId, ItemKind, ItemStatus, Package, PackageId, SpecBody, SpecGen, VecIdent, Visibility}, ty::Scheme, }; -use qsc_data_structures::index_map; +use qsc_data_structures::{index_map, namespaces::{NamespaceId, NamespaceTreeRoot}}; use rustc_hash::FxHashMap; use std::{cell::RefCell, collections::HashMap, rc::Rc}; @@ -65,7 +65,7 @@ impl FromIterator for Table { let mut terms: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); let mut namespaces = NamespaceTreeRoot::default(); for global in iter { - let namespace = namespaces.upsert_namespace(global.namespace); + let namespace = namespaces.insert_namespace(global.namespace); match global.kind { Kind::Ty(ty) => { tys.entry(namespace) @@ -88,109 +88,6 @@ impl FromIterator for Table { } } -#[derive(Debug, Clone)] -pub struct NamespaceTreeRoot { - assigner: usize, - tree: NamespaceTreeNode, -} - -impl NamespaceTreeRoot { - fn upsert_namespace(&mut self, name: impl Into>>) -> NamespaceId { - self.assigner += 1; - let id = self.assigner; - let node = self.new_namespace_node(Default::default()); - let mut components_iter = name.into(); - let mut components_iter = components_iter.iter(); - // construct the initial rover for the breadth-first insertion - // (this is like a BFS but we create a new node if one doesn't exist) - let self_cell = RefCell::new(self); - let mut rover_node = &mut self_cell.borrow_mut().tree; - // create the rest of the nodes - for component in components_iter { - rover_node = rover_node.children - .entry(Rc::clone(component)) - .or_insert_with(|| self_cell.borrow_mut().new_namespace_node(Default::default())); - } - - rover_node.id - } - fn new_namespace_node( - &mut self, - children: HashMap, NamespaceTreeNode>, - ) -> NamespaceTreeNode { - self.assigner += 1; - NamespaceTreeNode { - id: NamespaceId::new(self.assigner), - children, - } - } - - fn find_namespace(&self, ns: impl Into>>) -> Option { - self.tree.find_namespace(ns.into()) - } -} -impl Default for NamespaceTreeRoot { - fn default() -> Self { - Self { - assigner: 0, - tree: NamespaceTreeNode { - children: HashMap::new(), - id: NamespaceId::new(0), - }, - } - } -} - -#[derive(Debug, Clone)] -pub struct NamespaceTreeNode { - children: HashMap, NamespaceTreeNode>, - id: NamespaceId, -} -impl NamespaceTreeNode { - pub fn new(id: NamespaceId, children: HashMap, NamespaceTreeNode>) -> Self { - Self { - children, - id, - } - } - fn get(&self, component: &Rc) -> Option<&NamespaceTreeNode> { - self.children.get(component) - } - - fn contains(&self, ns: &VecIdent) -> bool { - self.find_namespace(ns).is_some() - } - fn find_namespace(&self, ns: impl Into>>) -> Option { - // look up a namespace in the tree and return the id - // do a breadth-first search through NamespaceTree for the namespace - // if it's not found, return None - let ns = ns.into(); - let mut buf = Rc::new(self); - for component in ns.iter() { - if let Some(next_ns) = buf.get(component) { - buf = Rc::new(next_ns); - } else { - return None; - } - } - return Some(buf.id); - } -} - -/// An ID that corresponds to a namespace in the global scope. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] -pub struct NamespaceId(usize); -impl NamespaceId { - pub fn new(value: usize) -> Self { - Self(value) - } -} - -impl std::fmt::Display for NamespaceId { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Namespace {}", self.0) - } -} pub struct PackageIter<'a> { id: Option, package: &'a Package, diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 124379f042..f9310ab80b 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -4,8 +4,7 @@ //! The high-level intermediate representation for Q#. HIR is lowered from the AST. #![warn(missing_docs)] - -use crate::{global::NamespaceTreeRoot, ty::{Arrow, FunctorSet, FunctorSetValue, GenericArg, GenericParam, Scheme, Ty, Udt}}; +use crate::{ ty::{Arrow, FunctorSet, FunctorSetValue, GenericArg, GenericParam, Scheme, Ty, Udt}}; use indenter::{indented, Indented}; use num_bigint::BigInt; use qsc_data_structures::{index_map::IndexMap, span::Span}; diff --git a/compiler/qsc_passes/src/common.rs b/compiler/qsc_passes/src/common.rs index db0909ffd2..c6e511c5ee 100644 --- a/compiler/qsc_passes/src/common.rs +++ b/compiler/qsc_passes/src/common.rs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use qsc_data_structures::span::Span; +use qsc_data_structures::{namespaces::NamespaceId, span::Span}; use qsc_hir::{ assigner::Assigner, - global::{NamespaceId, Table}, + global::{ Table}, hir::{ Expr, ExprKind, Field, Ident, Mutability, NodeId, Pat, PatKind, PrimField, Res, Stmt, StmtKind, diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index a1953eee82..bc4fb94676 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -10,10 +10,11 @@ use crate::qsc_utils::{into_range, span_contains}; use qsc::ast::ast; use qsc::ast::visit::{self, Visitor}; use qsc::display::{CodeDisplay, Lookup}; -use qsc::hir::global::NamespaceId; + use qsc::hir::{ItemKind, Package, PackageId, Visibility}; use qsc::line_column::{Encoding, Position, Range}; use qsc::resolve::{Local, LocalKind}; +use qsc::NamespaceId; use rustc_hash::FxHashSet; use std::rc::Rc; @@ -400,12 +401,11 @@ impl CompletionListBuilder { let is_user_package = compilation.user_package_id == package_id; let namespaces = compilation - .package_store - .get(package_id) - .expect("package should exist") - .ast - .namespaces - ; + .package_store + .get(package_id) + .expect("package should exist") + .ast + .namespaces; convert_ast_namespaces_into_hir_namespaces(namespaces); package.items.values().filter_map(move |i| { // We only want items whose parents are namespaces @@ -420,7 +420,9 @@ impl CompletionListBuilder { if !is_user_package { return None; // ignore item if not in the user's package } - let ns_id = namespaces.find_namespace(namespace).expect("namespace should exist"); + let ns_id = namespaces + .find_namespace(namespace) + .expect("namespace should exist"); // ignore item if the user is not in the item's namespace match ¤t_namespace_name { @@ -549,18 +551,22 @@ impl CompletionListBuilder { } } -fn convert_ast_namespaces_into_hir_namespaces(namespaces: qsc::resolve::NamespaceTreeRoot) -> qsc::hir::global::NamespaceTreeRoot { +fn convert_ast_namespaces_into_hir_namespaces( + namespaces: qsc::resolve::NamespaceTreeRoot, +) -> qsc::hir::global::NamespaceTreeRoot { let mut hir_namespaces = Vec::new(); let mut assigner = 0; - for (namespace, qsc::resolve::NamespaceTreeNode { children, id }) in namespaces.tree().children() { + for (namespace, qsc::resolve::NamespaceTreeNode { children, id }) in + namespaces.tree().children() + { let hir_namespace = qsc::hir::global::NamespaceTreeNode::new(id, children.clone()); - if namespace.id > assigner { assigner = namespace.id + 1; }; + if namespace.id > assigner { + assigner = namespace.id + 1; + }; hir_namespaces.push(hir_namespace); } - qsc::hir::global::NamespaceTreeRoot { - namespaces: hir_namespaces, - } + qsc::hir::global::NamespaceTreeRoot::new(hir_namespaces, assigner) } fn local_completion( From 0b4069a6ba0fca5ce432d00a3b706ed697950634 Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 25 Mar 2024 10:34:32 -0700 Subject: [PATCH 17/76] add more namespace ids --- language_service/src/completion.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index bc4fb94676..ef23c63bc1 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -16,6 +16,7 @@ use qsc::line_column::{Encoding, Position, Range}; use qsc::resolve::{Local, LocalKind}; use qsc::NamespaceId; use rustc_hash::FxHashSet; +use std::cell::RefCell; use std::rc::Rc; const PRELUDE: [&str; 3] = [ @@ -74,7 +75,7 @@ pub(crate) fn get_completions( // The PRELUDE namespaces are always implicitly opened. context_finder .opens - .extend(PRELUDE.into_iter().map(|ns| (Rc::from(ns), None))); + .extend(PRELUDE.into_iter().map(|ns| (ns, None))); let mut builder = CompletionListBuilder::new(); @@ -265,7 +266,7 @@ impl CompletionListBuilder { fn push_globals( &mut self, compilation: &Compilation, - opens: &[(Rc, Option>)], + opens: &[(NamespaceId, Option>)], insert_open_range: Option, current_namespace_name: &Option, indent: &String, @@ -387,7 +388,8 @@ impl CompletionListBuilder { fn get_callables<'a>( compilation: &'a Compilation, package_id: PackageId, - opens: &'a [(Rc, Option>)], + // name and alias + opens: &'a [(NamespaceId, Option>)], insert_open_at: Option, current_namespace_name: Option, indent: &'a String, @@ -405,9 +407,10 @@ impl CompletionListBuilder { .get(package_id) .expect("package should exist") .ast - .namespaces; + .namespaces.clone(); + let namespaces = Rc::new(namespaces); convert_ast_namespaces_into_hir_namespaces(namespaces); - package.items.values().filter_map(move |i| { + package.items.values().map(|x| (is_user_package.clone(), namespaces.clone(), x)).filter_map( |(is_user_package, namespaces, i)| { // We only want items whose parents are namespaces if let Some(item_id) = i.parent { if let Some(parent) = package.items.get(item_id) { @@ -415,14 +418,14 @@ impl CompletionListBuilder { if namespace.starts_with_sequence(&["Microsoft", "Quantum", "Unstable"]) { return None; } + let ns_id = namespaces + .find_namespace(namespace) + .expect("namespace should exist"); // If the item's visibility is internal, the item may be ignored if matches!(i.visibility, Visibility::Internal) { if !is_user_package { return None; // ignore item if not in the user's package } - let ns_id = namespaces - .find_namespace(namespace) - .expect("namespace should exist"); // ignore item if the user is not in the item's namespace match ¤t_namespace_name { @@ -446,13 +449,13 @@ impl CompletionListBuilder { let mut additional_edits = vec![]; let mut qualification: Option> = None; match ¤t_namespace_name { - Some(curr_ns) if *curr_ns == namespace.name => {} + Some(curr_ns) if *curr_ns == ns_id => {} _ => { // open is an option of option of Rc // the first option tells if it found an open with the namespace name // the second, nested option tells if that open has an alias let open = opens.iter().find_map(|(name, alias)| { - if *name == namespace.name { + if *name == ns_id { Some(alias) } else { None @@ -466,13 +469,13 @@ impl CompletionListBuilder { start, format!( "open {};{}", - namespace.name.clone(), + namespace, indent, ), )); None } - None => Some(namespace.name.clone()), + None => Some(Rc::from(format!("{namespace}"))), }, } } @@ -631,7 +634,7 @@ fn local_completion( struct ContextFinder { offset: u32, context: Context, - opens: Vec<(Rc, Option>)>, + opens: Vec<(NamespaceId, Option>)>, start_of_namespace: Option, current_namespace_name: Option, } From 38327a95ab74dd1d336f23dcd181f70756b83c00 Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 25 Mar 2024 15:33:14 -0700 Subject: [PATCH 18/76] wip: continue refactor of namespaceids --- compiler/qsc_circuit/src/operations/tests.rs | 2 +- compiler/qsc_hir/src/hir.rs | 4 + language_service/src/code_lens.rs | 2 +- language_service/src/completion.rs | 120 ++----------------- 4 files changed, 18 insertions(+), 110 deletions(-) diff --git a/compiler/qsc_circuit/src/operations/tests.rs b/compiler/qsc_circuit/src/operations/tests.rs index 952bbaee80..a73c189df7 100644 --- a/compiler/qsc_circuit/src/operations/tests.rs +++ b/compiler/qsc_circuit/src/operations/tests.rs @@ -30,7 +30,7 @@ fn compile_one_operation(code: &str) -> (Item, String) { }); let mut namespaces = unit.package.items.values().filter_map(|i| { if let ItemKind::Namespace(ident, _) = &i.kind { - Some(ident.name.clone()) + Some(ident.clone()) } else { None } diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 69d79db8e6..7f4c908a52 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -1196,6 +1196,10 @@ impl VecIdent { } return true; } + + pub fn name(&self) -> String { + self.0.iter().map(|i| i.name.to_string()).collect::>().join(".") + } } /// An identifier. #[derive(Clone, Debug, Eq, Hash, PartialEq)] diff --git a/language_service/src/code_lens.rs b/language_service/src/code_lens.rs index 057b273f43..db142d1b16 100644 --- a/language_service/src/code_lens.rs +++ b/language_service/src/code_lens.rs @@ -42,7 +42,7 @@ pub(crate) fn get_code_lenses( .and_then(|parent_id| user_unit.package.items.get(parent_id)) .map(|parent| &parent.kind) { - let namespace = ns.name.to_string(); + let namespace = ns.name().to_string(); let range = into_range(position_encoding, decl.span, &user_unit.sources); let name = decl.name.name.clone(); diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index e2655712f7..ac0f5c58ed 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -75,7 +75,7 @@ pub(crate) fn get_completions( // The PRELUDE namespaces are always implicitly opened. context_finder .opens - .extend(PRELUDE.into_iter().map(|ns| (ns, None))); + .extend(PRELUDE.into_iter().map(|ns| (todo!("this needs to be a ns id") as NamespaceId, None))); let mut builder = CompletionListBuilder::new(); @@ -385,6 +385,7 @@ impl CompletionListBuilder { self.current_sort_group += 1; } + /// Get all callables in a package fn get_callables<'a>( compilation: &'a Compilation, package_id: PackageId, @@ -410,108 +411,10 @@ impl CompletionListBuilder { .namespaces.clone(); let namespaces = Rc::new(namespaces); convert_ast_namespaces_into_hir_namespaces(namespaces); - package.items.values().map(|x| (is_user_package.clone(), namespaces.clone(), x)).filter_map( |(is_user_package, namespaces, i)| { - // We only want items whose parents are namespaces - if let Some(item_id) = i.parent { - if let Some(parent) = package.items.get(item_id) { - if let ItemKind::Namespace(namespace, _) = &parent.kind { - if namespace.starts_with_sequence(&["Microsoft", "Quantum", "Unstable"]) { - return None; - } - let ns_id = namespaces - .find_namespace(namespace) - .expect("namespace should exist"); - // If the item's visibility is internal, the item may be ignored - if matches!(i.visibility, Visibility::Internal) { - if !is_user_package { - return None; // ignore item if not in the user's package - } - - // ignore item if the user is not in the item's namespace - match ¤t_namespace_name { - Some(curr_ns) => { - if *curr_ns != ns_id { - return None; - } - } - None => { - return None; - } - } - } - return match &i.kind { - ItemKind::Callable(callable_decl) => { - let name = callable_decl.name.name.as_ref(); - let detail = - Some(display.hir_callable_decl(callable_decl).to_string()); - // Everything that starts with a __ goes last in the list - let sort_group = u32::from(name.starts_with("__")); - let mut additional_edits = vec![]; - let mut qualification: Option> = None; - match ¤t_namespace_name { - Some(curr_ns) if *curr_ns == ns_id => {} - _ => { - // open is an option of option of Rc - // the first option tells if it found an open with the namespace name - // the second, nested option tells if that open has an alias - let open = opens.iter().find_map(|(name, alias)| { - if *name == ns_id { - Some(alias) - } else { - None - } - }); - qualification = match open { - Some(alias) => alias.clone(), - None => match insert_open_at { - Some(start) => { - additional_edits.push(TextEdit { - new_text: format!( - "open {};{}", - namespace, - indent, - ), - range: start, - }); - None - } - None => Some(Rc::from(format!("{namespace}"))), - }, - } - } - } - - let additional_text_edits = if additional_edits.is_empty() { - None - } else { - Some(additional_edits) - }; - - let label = if let Some(qualification) = qualification { - format!("{qualification}.{name}") - } else { - name.to_owned() - }; - Some(( - CompletionItem { - label, - kind: CompletionItemKind::Function, - sort_text: None, // This will get filled in during `push_sorted_completions` - detail, - additional_text_edits, - }, - sort_group, - )) - } - _ => None, - }; - } - } - } - None - }) + vec![todo!()].into_iter() } + /// Get all callables in the core package fn get_core_callables<'a>( compilation: &'a Compilation, package: &'a Package, @@ -542,10 +445,10 @@ impl CompletionListBuilder { fn get_namespaces(package: &'_ Package) -> impl Iterator + '_ { package.items.values().filter_map(|i| match &i.kind { ItemKind::Namespace(namespace, _) - if !namespace.name.starts_with("Microsoft.Quantum.Unstable") => + if !namespace.starts_with_sequence(&["Microsoft", "Quantum","Unstable"]) => { Some(CompletionItem::new( - namespace.name.to_string(), + namespace.name(), CompletionItemKind::Module, )) } @@ -555,23 +458,24 @@ impl CompletionListBuilder { } fn convert_ast_namespaces_into_hir_namespaces( - namespaces: qsc::resolve::NamespaceTreeRoot, -) -> qsc::hir::global::NamespaceTreeRoot { + namespaces: qsc::NamespaceTreeRoot, +) -> qsc::NamespaceTreeRoot { let mut hir_namespaces = Vec::new(); let mut assigner = 0; - for (namespace, qsc::resolve::NamespaceTreeNode { children, id }) in + for (namespace, qsc::NamespaceTreeNode { children, id }) in namespaces.tree().children() { - let hir_namespace = qsc::hir::global::NamespaceTreeNode::new(id, children.clone()); + let hir_namespace = qsc::NamespaceTreeNode::new(id, children.clone()); if namespace.id > assigner { assigner = namespace.id + 1; }; hir_namespaces.push(hir_namespace); } - qsc::hir::global::NamespaceTreeRoot::new(hir_namespaces, assigner) + qsc::NamespaceTreeRoot::new(hir_namespaces, assigner) } +/// Convert a local into a completion item fn local_completion( candidate: &Local, compilation: &Compilation, From 261aa8a4d024fab372e19104aff58bda7330f46c Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 25 Mar 2024 15:49:56 -0700 Subject: [PATCH 19/76] update methods for namespace tree node --- compiler/qsc_data_structures/src/lib.rs | 26 +++++++++++++++++++++++-- compiler/qsc_frontend/src/resolve.rs | 2 +- compiler/qsc_hir/src/global.rs | 2 +- language_service/src/completion.rs | 12 ++++++------ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index 71d29e17ac..c4fb5b9846 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -8,7 +8,7 @@ pub mod language_features; pub mod line_column; pub mod span; pub mod namespaces { - use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc}; + use std::{cell::RefCell, collections::HashMap, fmt::Display, ops::Deref, rc::Rc}; #[derive(Debug, Clone)] @@ -21,21 +21,37 @@ pub mod namespaces { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] pub struct NamespaceId(usize); impl NamespaceId { + /// Create a new namespace ID. pub fn new(value: usize) -> Self { Self(value) } } + impl Deref for NamespaceId { + type Target = usize; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl Display for NamespaceId { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "Namespace {}", self.0) } } + impl NamespaceTreeRoot { + /// Create a new namespace tree root. The assigner is used to assign new namespace IDs. + pub fn new(assigner: usize, tree: NamespaceTreeNode) -> Self { + Self { assigner, tree } + } + /// Get the namespace tree field. This is the root of the namespace tree. pub fn tree(&self) -> &NamespaceTreeNode { &self.tree } - pub fn insert_namespace(&mut self, name: impl Into>>) -> NamespaceId { + /// Upserts a namespace into the tree. If the namespace already exists, it will not be inserted. + /// Returns the ID of the namespace. + pub fn upsert_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.assigner += 1; let id = self.assigner; let node = self.new_namespace_node(Default::default()); @@ -91,6 +107,12 @@ pub mod namespaces { pub id: NamespaceId, } impl NamespaceTreeNode { + pub fn new(id: NamespaceId, children: HashMap, NamespaceTreeNode>) -> Self { + Self { + children, + id, + } + } pub fn children(&self) -> &HashMap, NamespaceTreeNode> { &self.children } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 7e17a67fcb..1166f86bae 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -273,7 +273,7 @@ impl GlobalScope { /// Creates a namespace in the namespace mapping. Note that namespaces are tracked separately from their /// item contents. This returns a [NamespaceId] which you can use to add more tys and terms to the scope. fn upsert_namespace(&mut self, name: impl Into>>) -> NamespaceId { - self.namespaces.insert_namespace(name) + self.namespaces.upsert_namespace(name) } } diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index 38062c62de..db16377fd4 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -65,7 +65,7 @@ impl FromIterator for Table { let mut terms: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); let mut namespaces = NamespaceTreeRoot::default(); for global in iter { - let namespace = namespaces.insert_namespace(global.namespace); + let namespace = namespaces.upsert_namespace(global.namespace); match global.kind { Kind::Ty(ty) => { tys.entry(namespace) diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index ac0f5c58ed..5efb7c23ca 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -409,7 +409,7 @@ impl CompletionListBuilder { .expect("package should exist") .ast .namespaces.clone(); - let namespaces = Rc::new(namespaces); + let namespaces =namespaces; convert_ast_namespaces_into_hir_namespaces(namespaces); vec![todo!()].into_iter() } @@ -461,18 +461,18 @@ fn convert_ast_namespaces_into_hir_namespaces( namespaces: qsc::NamespaceTreeRoot, ) -> qsc::NamespaceTreeRoot { let mut hir_namespaces = Vec::new(); - let mut assigner = 0; + let mut assigner: usize = 0; for (namespace, qsc::NamespaceTreeNode { children, id }) in namespaces.tree().children() { - let hir_namespace = qsc::NamespaceTreeNode::new(id, children.clone()); - if namespace.id > assigner { - assigner = namespace.id + 1; + let hir_namespace = qsc::NamespaceTreeNode::new(*id, children.clone()); + if id.into() > assigner { + assigner = id.into() + 1; }; hir_namespaces.push(hir_namespace); } - qsc::NamespaceTreeRoot::new(hir_namespaces, assigner) + qsc::NamespaceTreeRoot::new(assigner, hir_namespaces) } /// Convert a local into a completion item From 3e6d39d09750c19a01e981a3e12790cb3005097e Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 08:32:44 -0700 Subject: [PATCH 20/76] finish lining up types for completion lists --- compiler/qsc_data_structures/src/lib.rs | 28 +++++++++++++++++++++---- language_service/src/completion.rs | 21 ++++++++++++------- language_service/src/hover.rs | 2 +- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index c4fb5b9846..5db5f47572 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -27,6 +27,24 @@ pub mod namespaces { } } + impl From for NamespaceId { + fn from(value: usize) -> Self { + Self::new(value) + } + } + + impl From for usize { + fn from(value: NamespaceId) -> Self { + value.0 + } + } + + impl From<&NamespaceId> for usize { + fn from(value: &NamespaceId) -> Self { + value.0 + } + } + impl Deref for NamespaceId { type Target = usize; fn deref(&self) -> &Self::Target { @@ -55,8 +73,8 @@ pub mod namespaces { self.assigner += 1; let id = self.assigner; let node = self.new_namespace_node(Default::default()); - let mut components_iter = name.into(); - let mut components_iter = components_iter.iter(); + let components_iter = name.into(); + let components_iter = components_iter.iter(); // construct the initial rover for the breadth-first insertion // (this is like a BFS but we create a new node if one doesn't exist) let self_cell = RefCell::new(self); @@ -74,7 +92,7 @@ pub mod namespaces { } rover_node.id } - fn new_namespace_node( + pub fn new_namespace_node( &mut self, children: HashMap, NamespaceTreeNode>, ) -> NamespaceTreeNode { @@ -119,7 +137,9 @@ pub mod namespaces { fn get(&self, component: &Rc) -> Option<&NamespaceTreeNode> { self.children.get(component) } - + pub fn id(&self) -> NamespaceId { + self.id + } fn contains(&self, ns: impl Into>>) -> bool { self.find_namespace(ns).is_some() } diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index 5efb7c23ca..395c0fc470 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -14,9 +14,10 @@ use qsc::display::{CodeDisplay, Lookup}; use qsc::hir::{ItemKind, Package, PackageId, Visibility}; use qsc::line_column::{Encoding, Position, Range}; use qsc::resolve::{Local, LocalKind}; -use qsc::NamespaceId; +use qsc::{NamespaceId, NamespaceTreeNode, NamespaceTreeRoot}; use rustc_hash::FxHashSet; use std::cell::RefCell; +use std::collections::HashMap; use std::rc::Rc; const PRELUDE: [&str; 3] = [ @@ -460,19 +461,23 @@ impl CompletionListBuilder { fn convert_ast_namespaces_into_hir_namespaces( namespaces: qsc::NamespaceTreeRoot, ) -> qsc::NamespaceTreeRoot { - let mut hir_namespaces = Vec::new(); + let mut hir_namespaces: HashMap<_, _> = Default::default(); let mut assigner: usize = 0; + let tree = namespaces.tree(); + let root_id = tree.id(); + for (namespace, qsc::NamespaceTreeNode { children, id }) in - namespaces.tree().children() + tree.children() { - let hir_namespace = qsc::NamespaceTreeNode::new(*id, children.clone()); - if id.into() > assigner { - assigner = id.into() + 1; + let children = qsc::NamespaceTreeNode::new(*id, children.clone()); + let id = Into::::into(id); + if id > assigner { + assigner = id + 1; }; - hir_namespaces.push(hir_namespace); + hir_namespaces.insert(namespace.clone(), children); } - qsc::NamespaceTreeRoot::new(assigner, hir_namespaces) + qsc::NamespaceTreeRoot::new(assigner, NamespaceTreeNode::new(root_id, hir_namespaces)) } /// Convert a local into a completion item diff --git a/language_service/src/hover.rs b/language_service/src/hover.rs index a4c62855b2..1269b50ce3 100644 --- a/language_service/src/hover.rs +++ b/language_service/src/hover.rs @@ -82,7 +82,7 @@ impl<'a> Handler<'a> for HoverGenerator<'a> { .map_or_else( || Rc::from(""), |parent| match &parent.kind { - qsc::hir::ItemKind::Namespace(namespace, _) => namespace.name.clone(), + qsc::hir::ItemKind::Namespace(namespace, _) => Rc::from(namespace.name()), _ => Rc::from(""), }, ); From 71e3dddd05dca96ce3c4aa9aea50557233b6e4a8 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 08:48:51 -0700 Subject: [PATCH 21/76] fmT --- compiler/qsc/src/lib.rs | 4 +- compiler/qsc_ast/src/ast.rs | 22 ++++--- compiler/qsc_ast/src/mut_visit.rs | 3 +- compiler/qsc_ast/src/visit.rs | 5 +- compiler/qsc_data_structures/src/lib.rs | 7 +-- compiler/qsc_eval/src/lower.rs | 2 +- compiler/qsc_frontend/src/compile.rs | 5 +- .../qsc_frontend/src/compile/preprocess.rs | 30 ++++++++-- compiler/qsc_frontend/src/compile/tests.rs | 5 +- compiler/qsc_frontend/src/lower.rs | 4 +- compiler/qsc_frontend/src/resolve.rs | 36 ++++++++---- compiler/qsc_hir/src/global.rs | 29 ++++++---- compiler/qsc_hir/src/hir.rs | 25 +++++--- compiler/qsc_hir/src/mut_visit.rs | 6 +- compiler/qsc_hir/src/visit.rs | 3 +- compiler/qsc_parse/src/prim.rs | 3 +- compiler/qsc_passes/src/common.rs | 2 +- compiler/qsc_passes/src/invert_block.rs | 8 +-- compiler/qsc_passes/src/loop_unification.rs | 9 ++- .../src/replace_qubit_allocation.rs | 57 ++++++++----------- language_service/src/completion.rs | 19 ++++--- language_service/src/references.rs | 2 +- 22 files changed, 161 insertions(+), 125 deletions(-) diff --git a/compiler/qsc/src/lib.rs b/compiler/qsc/src/lib.rs index 4003c40c48..c259869993 100644 --- a/compiler/qsc/src/lib.rs +++ b/compiler/qsc/src/lib.rs @@ -11,7 +11,7 @@ pub mod target; pub use qsc_formatter::formatter; pub use qsc_frontend::compile::{ - CompileUnit, PackageStore, RuntimeCapabilityFlags, SourceContents, SourceMap, SourceName + CompileUnit, PackageStore, RuntimeCapabilityFlags, SourceContents, SourceMap, SourceName, }; pub mod resolve { @@ -34,7 +34,7 @@ pub mod project { pub use qsc_project::{DirEntry, EntryType, FileSystem, Manifest, ManifestDescriptor}; } -pub use qsc_data_structures::{language_features::LanguageFeatures, span::Span, namespaces::*}; +pub use qsc_data_structures::{language_features::LanguageFeatures, namespaces::*, span::Span}; pub use qsc_passes::{PackageType, PassContext}; diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index c21db9700d..7f4207bd14 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -1281,14 +1281,7 @@ pub struct Path { impl Display for Path { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(ns) = &self.namespace { - write!( - f, - "Path {} {} ({}) ({})", - self.id, - self.span, - ns, - self.name - )?; + write!(f, "Path {} {} ({}) ({})", self.id, self.span, ns, self.name)?; } else { write!(f, "Path {} {} ({})", self.id, self.span, self.name)?; } @@ -1322,14 +1315,12 @@ impl From for Vec> { } } - impl From<&VecIdent> for Vec> { fn from(v: &VecIdent) -> Self { v.0.iter().map(|i| i.name.clone()).collect() } } - impl From> for VecIdent { fn from(v: Vec) -> Self { VecIdent(v) @@ -1345,14 +1336,21 @@ impl From for Vec { impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let idents = self.0.iter(); - write!(f, "{}", idents.map(|i| i.name.to_string()).collect::>().join(".")) + write!( + f, + "{}", + idents + .map(|i| i.name.to_string()) + .collect::>() + .join(".") + ) } } impl VecIdent { pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { self.0.iter() } - + pub fn span(&self) -> Span { Span { lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), diff --git a/compiler/qsc_ast/src/mut_visit.rs b/compiler/qsc_ast/src/mut_visit.rs index 993b8c2d29..9eb22e6e2e 100644 --- a/compiler/qsc_ast/src/mut_visit.rs +++ b/compiler/qsc_ast/src/mut_visit.rs @@ -82,7 +82,6 @@ pub trait MutVisitor: Sized { fn visit_span(&mut self, _: &mut Span) {} } - pub fn walk_package(vis: &mut impl MutVisitor, package: &mut Package) { package.nodes.iter_mut().for_each(|n| match n { TopLevelNode::Namespace(ns) => vis.visit_namespace(ns), @@ -354,4 +353,4 @@ pub fn walk_vec_ident(vis: &mut impl MutVisitor, ident: &mut crate::ast::VecIden for ref mut ident in ident.0.iter_mut() { vis.visit_ident(ident) } -} \ No newline at end of file +} diff --git a/compiler/qsc_ast/src/visit.rs b/compiler/qsc_ast/src/visit.rs index baf465314e..947c719570 100644 --- a/compiler/qsc_ast/src/visit.rs +++ b/compiler/qsc_ast/src/visit.rs @@ -2,7 +2,10 @@ // Licensed under the MIT License. use crate::ast::{ - Attr, Block, CallableBody, CallableDecl, Expr, ExprKind, FunctorExpr, FunctorExprKind, Ident, Item, ItemKind, Namespace, Package, Pat, PatKind, Path, QubitInit, QubitInitKind, SpecBody, SpecDecl, Stmt, StmtKind, StringComponent, TopLevelNode, Ty, TyDef, TyDefKind, TyKind, VecIdent, Visibility + Attr, Block, CallableBody, CallableDecl, Expr, ExprKind, FunctorExpr, FunctorExprKind, Ident, + Item, ItemKind, Namespace, Package, Pat, PatKind, Path, QubitInit, QubitInitKind, SpecBody, + SpecDecl, Stmt, StmtKind, StringComponent, TopLevelNode, Ty, TyDef, TyDefKind, TyKind, + VecIdent, Visibility, }; pub trait Visitor<'a>: Sized { diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index 5db5f47572..c9f0b8a1fc 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -67,7 +67,7 @@ pub mod namespaces { pub fn tree(&self) -> &NamespaceTreeNode { &self.tree } - /// Upserts a namespace into the tree. If the namespace already exists, it will not be inserted. + /// Upserts a namespace into the tree. If the namespace already exists, it will not be inserted. /// Returns the ID of the namespace. pub fn upsert_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.assigner += 1; @@ -126,10 +126,7 @@ pub mod namespaces { } impl NamespaceTreeNode { pub fn new(id: NamespaceId, children: HashMap, NamespaceTreeNode>) -> Self { - Self { - children, - id, - } + Self { children, id } } pub fn children(&self) -> &HashMap, NamespaceTreeNode> { &self.children diff --git a/compiler/qsc_eval/src/lower.rs b/compiler/qsc_eval/src/lower.rs index 1d6a43b5ee..275a1e0ad6 100644 --- a/compiler/qsc_eval/src/lower.rs +++ b/compiler/qsc_eval/src/lower.rs @@ -754,7 +754,7 @@ impl Lowerer { } fn lower_vec_ident(&self, name: &[hir::Ident]) -> fir::Ident { todo!("should this return a vec of hir idents or just one hir ident? or maybe a namespace id?") - } + } } fn lower_generics(generics: &[qsc_hir::ty::GenericParam]) -> Vec { diff --git a/compiler/qsc_frontend/src/compile.rs b/compiler/qsc_frontend/src/compile.rs index a236f94727..70b930c928 100644 --- a/compiler/qsc_frontend/src/compile.rs +++ b/compiler/qsc_frontend/src/compile.rs @@ -23,7 +23,10 @@ use qsc_ast::{ visit::Visitor as _, }; use qsc_data_structures::{ - index_map::{self, IndexMap}, language_features::LanguageFeatures, namespaces::NamespaceTreeRoot, span::Span + index_map::{self, IndexMap}, + language_features::LanguageFeatures, + namespaces::NamespaceTreeRoot, + span::Span, }; use qsc_hir::{ assigner::Assigner as HirAssigner, diff --git a/compiler/qsc_frontend/src/compile/preprocess.rs b/compiler/qsc_frontend/src/compile/preprocess.rs index dedcda60d4..aaaf4bb4d4 100644 --- a/compiler/qsc_frontend/src/compile/preprocess.rs +++ b/compiler/qsc_frontend/src/compile/preprocess.rs @@ -51,12 +51,22 @@ impl MutVisitor for Conditional { ItemKind::Callable(callable) => { self.included_names.push(TrackedName { name: callable.name.name.clone(), - namespace: namespace.name.clone().iter().map(|x| Rc::from(x.to_string())).collect(), + namespace: namespace + .name + .clone() + .iter() + .map(|x| Rc::from(x.to_string())) + .collect(), }); } ItemKind::Ty(ident, _) => self.included_names.push(TrackedName { name: ident.name.clone(), - namespace: namespace.name.clone().iter().map(|x| Rc::from(x.to_string())).collect(), + namespace: namespace + .name + .clone() + .iter() + .map(|x| Rc::from(x.to_string())) + .collect(), }), _ => {} } @@ -66,12 +76,22 @@ impl MutVisitor for Conditional { ItemKind::Callable(callable) => { self.dropped_names.push(TrackedName { name: callable.name.name.clone(), - namespace: namespace.name.clone().iter().map(|x| Rc::from(x.to_string())).collect(), + namespace: namespace + .name + .clone() + .iter() + .map(|x| Rc::from(x.to_string())) + .collect(), }); } ItemKind::Ty(ident, _) => self.dropped_names.push(TrackedName { name: ident.name.clone(), - namespace: namespace.name.clone().iter().map(|x| Rc::from(x.to_string())).collect(), + namespace: namespace + .name + .clone() + .iter() + .map(|x| Rc::from(x.to_string())) + .collect(), }), _ => {} } @@ -108,7 +128,7 @@ impl MutVisitor for Conditional { } ItemKind::Ty(ident, _) => self.dropped_names.push(TrackedName { name: ident.name.clone(), - namespace: Vec::default() + namespace: Vec::default(), }), _ => {} } diff --git a/compiler/qsc_frontend/src/compile/tests.rs b/compiler/qsc_frontend/src/compile/tests.rs index f0a3b0b659..fb93dbb0f9 100644 --- a/compiler/qsc_frontend/src/compile/tests.rs +++ b/compiler/qsc_frontend/src/compile/tests.rs @@ -341,7 +341,10 @@ fn insert_core_call() { impl MutVisitor for Inserter<'_> { fn visit_block(&mut self, block: &mut Block) { - let ns = self.core.find_namespace(vec![Rc::from("QIR"), Rc::from("Runtime")]).expect("QIR runtime should be inserted at instantiation of core Table"); + let ns = self + .core + .find_namespace(vec![Rc::from("QIR"), Rc::from("Runtime")]) + .expect("QIR runtime should be inserted at instantiation of core Table"); let allocate = self .core .resolve_term(ns, "__quantum__rt__qubit_allocate") diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs index 83e445d6df..b0b740e042 100644 --- a/compiler/qsc_frontend/src/lower.rs +++ b/compiler/qsc_frontend/src/lower.rs @@ -737,9 +737,9 @@ impl With<'_> { new_id }) } - + fn lower_vec_ident(&self, name: &[ast::Ident]) -> hir::VecIdent { - todo!("should this return a vec of hir idents or just one hir ident? or maybe a namespace id?") + todo!("should this return a vec of hir idents or just one hir ident? or maybe a namespace id?") } } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 1166f86bae..71418d71dc 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -11,7 +11,11 @@ use qsc_ast::{ }, visit::{self as ast_visit, walk_attr, Visitor as AstVisitor}, }; -use qsc_data_structures::{index_map::IndexMap, namespaces::{NamespaceId, NamespaceTreeRoot}, span::Span}; +use qsc_data_structures::{ + index_map::IndexMap, + namespaces::{NamespaceId, NamespaceTreeRoot}, + span::Span, +}; use qsc_hir::{ assigner::Assigner, global, @@ -20,7 +24,12 @@ use qsc_hir::{ }; use rustc_hash::{FxHashMap, FxHashSet}; use std::{ - cell::RefCell, collections::{hash_map::Entry, HashMap}, fmt::Display, rc::Rc, str::FromStr, vec + cell::RefCell, + collections::{hash_map::Entry, HashMap}, + fmt::Display, + rc::Rc, + str::FromStr, + vec, }; use thiserror::Error; @@ -240,7 +249,6 @@ pub enum LocalKind { Var(NodeId), } - #[derive(Debug, Clone, Default)] pub struct GlobalScope { tys: FxHashMap, Res>>, @@ -255,7 +263,6 @@ pub struct GlobalScope { intrinsics: FxHashSet>, } - impl GlobalScope { fn find_namespace(&self, ns: &VecIdent) -> Option { self.namespaces.find_namespace(ns) @@ -362,7 +369,12 @@ impl Resolver { } pub(super) fn into_result(self) -> (Names, Locals, Vec, NamespaceTreeRoot) { - (self.names, self.locals, self.errors, self.globals.namespaces) + ( + self.names, + self.locals, + self.errors, + self.globals.namespaces, + ) } pub(super) fn extend_dropped_names(&mut self, dropped_names: Vec) { @@ -497,11 +509,7 @@ impl Resolver { fn bind_open(&mut self, name: &ast::VecIdent, alias: &Option>) { let alias = alias.as_ref().map_or("".into(), |a| Rc::clone(&a.name)); - if self - .globals - .namespaces - .find_namespace(name).is_some() - { + if self.globals.namespaces.find_namespace(name).is_some() { self.current_scope_mut() .opens .entry(alias) @@ -578,7 +586,7 @@ impl Resolver { self.locals.get_scope_mut(scope_id) } - + pub(crate) fn namespaces(&self) -> NamespaceTreeRoot { self.globals.namespaces.clone() } @@ -798,7 +806,11 @@ impl GlobalTable { } let mut scope = GlobalScope::default(); - let ns = scope.upsert_namespace(vec![Rc::from("Microsoft"), Rc::from("Quantum"), Rc::from("Core")]); + let ns = scope.upsert_namespace(vec![ + Rc::from("Microsoft"), + Rc::from("Quantum"), + Rc::from("Core"), + ]); let mut tys = FxHashMap::default(); tys.insert(ns, core); diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index db16377fd4..e131772534 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -2,10 +2,16 @@ // Licensed under the MIT License. use crate::{ - hir::{Ident, Item, ItemId, ItemKind, ItemStatus, Package, PackageId, SpecBody, SpecGen, VecIdent, Visibility}, + hir::{ + Ident, Item, ItemId, ItemKind, ItemStatus, Package, PackageId, SpecBody, SpecGen, VecIdent, + Visibility, + }, ty::Scheme, }; -use qsc_data_structures::{index_map, namespaces::{NamespaceId, NamespaceTreeRoot}}; +use qsc_data_structures::{ + index_map, + namespaces::{NamespaceId, NamespaceTreeRoot}, +}; use rustc_hash::FxHashMap; use std::{cell::RefCell, collections::HashMap, rc::Rc}; @@ -33,13 +39,12 @@ pub struct Term { pub intrinsic: bool, } - /// A lookup table used for looking up global core items for insertion in `qsc_passes`. #[derive(Default)] pub struct Table { tys: FxHashMap, Ty>>, terms: FxHashMap, Term>>, - namespaces: NamespaceTreeRoot + namespaces: NamespaceTreeRoot, } impl Table { @@ -52,7 +57,7 @@ impl Table { pub fn resolve_term(&self, namespace: NamespaceId, name: &str) -> Option<&Term> { self.terms.get(&namespace).and_then(|terms| terms.get(name)) } - + pub fn find_namespace(&self, vec: impl Into>>) -> Option { // find a namespace if it exists and return its id self.namespaces.find_namespace(vec) @@ -68,9 +73,7 @@ impl FromIterator for Table { let namespace = namespaces.upsert_namespace(global.namespace); match global.kind { Kind::Ty(ty) => { - tys.entry(namespace) - .or_default() - .insert(global.name, ty); + tys.entry(namespace).or_default().insert(global.name, ty); } Kind::Term(term) => { terms @@ -82,9 +85,13 @@ impl FromIterator for Table { } } - // TODO; copy namespace root etc over here and + // TODO; copy namespace root etc over here and // create a namespace structure with IDs - Self { namespaces, tys, terms } + Self { + namespaces, + tys, + terms, + } } } @@ -145,7 +152,7 @@ impl PackageIter<'_> { }) } (ItemKind::Namespace(ident, _), None) => Some(Global { - namespace: ident.into(), + namespace: ident.into(), name: "".into(), visibility: Visibility::Public, status, diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 7f4c908a52..beb0ef2737 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -4,7 +4,7 @@ //! The high-level intermediate representation for Q#. HIR is lowered from the AST. #![warn(missing_docs)] -use crate::{ ty::{Arrow, FunctorSet, FunctorSetValue, GenericArg, GenericParam, Scheme, Ty, Udt}}; +use crate::ty::{Arrow, FunctorSet, FunctorSetValue, GenericArg, GenericParam, Scheme, Ty, Udt}; use indenter::{indented, Indented}; use num_bigint::BigInt; use qsc_data_structures::{index_map::IndexMap, span::Span}; @@ -1143,14 +1143,12 @@ impl From for Vec> { } } - impl From<&VecIdent> for Vec> { fn from(v: &VecIdent) -> Self { v.0.iter().map(|i| i.name.clone()).collect() } } - impl From> for VecIdent { fn from(v: Vec) -> Self { VecIdent(v) @@ -1166,21 +1164,28 @@ impl From for Vec { impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let idents = self.0.iter(); - write!(f, "{}", idents.map(|i| i.name.to_string()).collect::>().join(".")) + write!( + f, + "{}", + idents + .map(|i| i.name.to_string()) + .collect::>() + .join(".") + ) } } impl VecIdent { pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { self.0.iter() } - + pub fn span(&self) -> Span { Span { lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), hi: self.0.last().map(|i| i.span.hi).unwrap_or_default(), } } - + pub fn starts_with(&self, arg: &str) -> bool { self.0.first().map(|i| &*i.name == arg).unwrap_or_default() } @@ -1196,9 +1201,13 @@ impl VecIdent { } return true; } - + pub fn name(&self) -> String { - self.0.iter().map(|i| i.name.to_string()).collect::>().join(".") + self.0 + .iter() + .map(|i| i.name.to_string()) + .collect::>() + .join(".") } } /// An identifier. diff --git a/compiler/qsc_hir/src/mut_visit.rs b/compiler/qsc_hir/src/mut_visit.rs index 03a29bbebb..ea246dab0c 100644 --- a/compiler/qsc_hir/src/mut_visit.rs +++ b/compiler/qsc_hir/src/mut_visit.rs @@ -48,9 +48,7 @@ pub trait MutVisitor: Sized { walk_ident(self, ident); } - fn visit_span(&mut self, _: &mut Span) { - - } + fn visit_span(&mut self, _: &mut Span) {} fn visit_vec_ident(&mut self, ident: &mut crate::hir::VecIdent) { walk_vec_ident(self, ident); } @@ -71,7 +69,7 @@ pub fn walk_item(vis: &mut impl MutVisitor, item: &mut Item) { match &mut item.kind { ItemKind::Callable(decl) => vis.visit_callable_decl(decl), - ItemKind::Namespace(name, _) => vis.visit_vec_ident(name), + ItemKind::Namespace(name, _) => vis.visit_vec_ident(name), ItemKind::Ty(name, _) => vis.visit_ident(name), } } diff --git a/compiler/qsc_hir/src/visit.rs b/compiler/qsc_hir/src/visit.rs index f6e1558a90..1c6372ab22 100644 --- a/compiler/qsc_hir/src/visit.rs +++ b/compiler/qsc_hir/src/visit.rs @@ -46,7 +46,6 @@ pub trait Visitor<'a>: Sized { fn visit_ident(&mut self, _: &'a Ident) {} fn visit_vec_ident(&mut self, _: &'a VecIdent) {} - } pub fn walk_package<'a>(vis: &mut impl Visitor<'a>, package: &'a Package) { @@ -59,7 +58,7 @@ pub fn walk_item<'a>(vis: &mut impl Visitor<'a>, item: &'a Item) { match &item.kind { ItemKind::Callable(decl) => vis.visit_callable_decl(decl), ItemKind::Namespace(name, _) => vis.visit_vec_ident(name), - ItemKind::Ty(name, _) => vis.visit_ident(name), + ItemKind::Ty(name, _) => vis.visit_ident(name), } } diff --git a/compiler/qsc_parse/src/prim.rs b/compiler/qsc_parse/src/prim.rs index 9ef7452cb5..d59bf4581c 100644 --- a/compiler/qsc_parse/src/prim.rs +++ b/compiler/qsc_parse/src/prim.rs @@ -100,7 +100,8 @@ pub(super) fn path(s: &mut ParserContext) -> Result> { span: part.span, name: part.name.clone(), }) - .collect::>().into(), + .collect::>() + .into(), ) } _ => None, diff --git a/compiler/qsc_passes/src/common.rs b/compiler/qsc_passes/src/common.rs index c6e511c5ee..8afaea8f30 100644 --- a/compiler/qsc_passes/src/common.rs +++ b/compiler/qsc_passes/src/common.rs @@ -4,7 +4,7 @@ use qsc_data_structures::{namespaces::NamespaceId, span::Span}; use qsc_hir::{ assigner::Assigner, - global::{ Table}, + global::Table, hir::{ Expr, ExprKind, Field, Ident, Mutability, NodeId, Pat, PatKind, PrimField, Res, Stmt, StmtKind, diff --git a/compiler/qsc_passes/src/invert_block.rs b/compiler/qsc_passes/src/invert_block.rs index f5c11a1380..87cc4ad958 100644 --- a/compiler/qsc_passes/src/invert_block.rs +++ b/compiler/qsc_passes/src/invert_block.rs @@ -329,11 +329,9 @@ fn make_range_field(range_id: NodeId, field: PrimField) -> Expr { } fn make_array_index_range_reverse(core: &Table, arr_id: NodeId, arr_ty: &Ty) -> Expr { - let ns = core.find_namespace(vec![ - "Microsoft".into(), - "Quantum".into(), - "Core".into(), - ]).unwrap(); + let ns = core + .find_namespace(vec!["Microsoft".into(), "Quantum".into(), "Core".into()]) + .unwrap(); let len = Box::new(Expr { id: NodeId::default(), span: Span::default(), diff --git a/compiler/qsc_passes/src/loop_unification.rs b/compiler/qsc_passes/src/loop_unification.rs index 103a5dcb93..4cdaeadcda 100644 --- a/compiler/qsc_passes/src/loop_unification.rs +++ b/compiler/qsc_passes/src/loop_unification.rs @@ -133,11 +133,10 @@ impl LoopUni<'_> { let Ty::Array(item_ty) = &array_id.ty else { panic!("iterator should have array type"); }; - let ns = self.core.find_namespace(vec![ - "Microsoft".into(), - "Quantum".into(), - "Core".into(), - ]).unwrap(); + let ns = self + .core + .find_namespace(vec!["Microsoft".into(), "Quantum".into(), "Core".into()]) + .unwrap(); let mut len_callee = create_gen_core_ref( self.core, ns, diff --git a/compiler/qsc_passes/src/replace_qubit_allocation.rs b/compiler/qsc_passes/src/replace_qubit_allocation.rs index b7a056a2f9..3589ea35e9 100644 --- a/compiler/qsc_passes/src/replace_qubit_allocation.rs +++ b/compiler/qsc_passes/src/replace_qubit_allocation.rs @@ -238,10 +238,10 @@ impl<'a> ReplaceQubitAllocation<'a> { } fn create_alloc_stmt(&mut self, ident: &IdentTemplate) -> Stmt { - let ns = self.core.find_namespace(vec![ - "QIR".into(), - "Runtime".into(), - ]).unwrap(); + let ns = self + .core + .find_namespace(vec!["QIR".into(), "Runtime".into()]) + .unwrap(); let mut call_expr = create_gen_core_ref( self.core, ns, @@ -254,26 +254,21 @@ impl<'a> ReplaceQubitAllocation<'a> { } fn create_array_alloc_stmt(&mut self, ident: &IdentTemplate, array_size: Expr) -> Stmt { - let ns = self.core.find_namespace(vec![ - "QIR".into(), - "Runtime".into(), - ]).unwrap(); - let mut call_expr = create_gen_core_ref( - self.core, - ns, - "AllocateQubitArray", - Vec::new(), - ident.span, - ); + let ns = self + .core + .find_namespace(vec!["QIR".into(), "Runtime".into()]) + .unwrap(); + let mut call_expr = + create_gen_core_ref(self.core, ns, "AllocateQubitArray", Vec::new(), ident.span); call_expr.id = self.assigner.next_node(); create_general_alloc_stmt(self.assigner, ident, call_expr, Some(array_size)) } fn create_dealloc_stmt(&mut self, ident: &IdentTemplate) -> Stmt { - let ns = self.core.find_namespace(vec![ - "QIR".into(), - "Runtime".into(), - ]).unwrap(); + let ns = self + .core + .find_namespace(vec!["QIR".into(), "Runtime".into()]) + .unwrap(); let mut call_expr = create_gen_core_ref( self.core, ns, @@ -286,17 +281,12 @@ impl<'a> ReplaceQubitAllocation<'a> { } fn create_array_dealloc_stmt(&mut self, ident: &IdentTemplate) -> Stmt { - let ns = self.core.find_namespace(vec![ - "QIR".into(), - "Runtime".into(), - ]).unwrap(); - let mut call_expr = create_gen_core_ref( - self.core, - ns, - "ReleaseQubitArray", - Vec::new(), - ident.span, - ); + let ns = self + .core + .find_namespace(vec!["QIR".into(), "Runtime".into()]) + .unwrap(); + let mut call_expr = + create_gen_core_ref(self.core, ns, "ReleaseQubitArray", Vec::new(), ident.span); call_expr.id = self.assigner.next_node(); create_general_dealloc_stmt(self.assigner, call_expr, ident) } @@ -428,10 +418,9 @@ fn create_qubit_global_alloc( qubit_init: QubitInit, ) -> StmtKind { fn qubit_alloc_expr(assigner: &mut Assigner, core: &Table, qubit_init: QubitInit) -> Expr { - let ns = core.find_namespace(vec![ - "QIR".into(), - "Runtime".into(), - ]).unwrap(); + let ns = core + .find_namespace(vec!["QIR".into(), "Runtime".into()]) + .unwrap(); match qubit_init.kind { QubitInitKind::Array(mut expr) => { let mut call_expr = create_gen_core_ref( diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index 395c0fc470..cc0f0e99b8 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -74,9 +74,11 @@ pub(crate) fn get_completions( }; // The PRELUDE namespaces are always implicitly opened. - context_finder - .opens - .extend(PRELUDE.into_iter().map(|ns| (todo!("this needs to be a ns id") as NamespaceId, None))); + context_finder.opens.extend( + PRELUDE + .into_iter() + .map(|ns| (todo!("this needs to be a ns id") as NamespaceId, None)), + ); let mut builder = CompletionListBuilder::new(); @@ -409,8 +411,9 @@ impl CompletionListBuilder { .get(package_id) .expect("package should exist") .ast - .namespaces.clone(); - let namespaces =namespaces; + .namespaces + .clone(); + let namespaces = namespaces; convert_ast_namespaces_into_hir_namespaces(namespaces); vec![todo!()].into_iter() } @@ -446,7 +449,7 @@ impl CompletionListBuilder { fn get_namespaces(package: &'_ Package) -> impl Iterator + '_ { package.items.values().filter_map(|i| match &i.kind { ItemKind::Namespace(namespace, _) - if !namespace.starts_with_sequence(&["Microsoft", "Quantum","Unstable"]) => + if !namespace.starts_with_sequence(&["Microsoft", "Quantum", "Unstable"]) => { Some(CompletionItem::new( namespace.name(), @@ -466,9 +469,7 @@ fn convert_ast_namespaces_into_hir_namespaces( let tree = namespaces.tree(); let root_id = tree.id(); - for (namespace, qsc::NamespaceTreeNode { children, id }) in - tree.children() - { + for (namespace, qsc::NamespaceTreeNode { children, id }) in tree.children() { let children = qsc::NamespaceTreeNode::new(*id, children.clone()); let id = Into::::into(id); if id > assigner { diff --git a/language_service/src/references.rs b/language_service/src/references.rs index c39ebbd564..f2ae1dd9f6 100644 --- a/language_service/src/references.rs +++ b/language_service/src/references.rs @@ -189,7 +189,7 @@ impl<'a> ReferenceFinder<'a> { let def_span = match &def.kind { hir::ItemKind::Callable(decl) => decl.name.span, hir::ItemKind::Namespace(name, _) => name.span(), - hir::ItemKind::Ty(name, _) => name.span, + hir::ItemKind::Ty(name, _) => name.span, }; locations.push( self.location( From dd1e312fc7940fc6c0d536b369983dfd6bd1b4ef Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 10:28:47 -0700 Subject: [PATCH 22/76] introduce vecident to qsc_fir --- compiler/qsc_eval/src/lower.rs | 2 +- compiler/qsc_fir/src/fir.rs | 55 ++++++++++++++++++++++++++++++- compiler/qsc_fir/src/global.rs | 48 +++++++++++++++++---------- compiler/qsc_fir/src/mut_visit.rs | 7 ++-- compiler/qsc_fir/src/visit.rs | 8 +++-- compiler/qsc_rca/src/overrider.rs | 2 +- 6 files changed, 97 insertions(+), 25 deletions(-) diff --git a/compiler/qsc_eval/src/lower.rs b/compiler/qsc_eval/src/lower.rs index 275a1e0ad6..4d3ff0baa1 100644 --- a/compiler/qsc_eval/src/lower.rs +++ b/compiler/qsc_eval/src/lower.rs @@ -752,7 +752,7 @@ impl Lowerer { name_span: field.name_span, } } - fn lower_vec_ident(&self, name: &[hir::Ident]) -> fir::Ident { + fn lower_vec_ident(&self, name: &[hir::Ident]) -> fir::VecIdent { todo!("should this return a vec of hir idents or just one hir ident? or maybe a namespace id?") } } diff --git a/compiler/qsc_fir/src/fir.rs b/compiler/qsc_fir/src/fir.rs index a0ddfef54c..f5623ca249 100644 --- a/compiler/qsc_fir/src/fir.rs +++ b/compiler/qsc_fir/src/fir.rs @@ -698,7 +698,7 @@ pub enum ItemKind { /// A `function` or `operation` declaration. Callable(CallableDecl), /// A `namespace` declaration. - Namespace(Ident, Vec), + Namespace(VecIdent, Vec), /// A `newtype` declaration. Ty(Ident, Udt), } @@ -1423,6 +1423,59 @@ impl Display for PatKind { } } +#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] +pub struct VecIdent(pub Vec); + +impl From for Vec> { + fn from(v: VecIdent) -> Self { + v.0.iter().map(|i| i.name.clone()).collect() + } +} + +impl From<&VecIdent> for Vec> { + fn from(v: &VecIdent) -> Self { + v.0.iter().map(|i| i.name.clone()).collect() + } +} + +impl From> for VecIdent { + fn from(v: Vec) -> Self { + VecIdent(v) + } +} + +impl From for Vec { + fn from(v: VecIdent) -> Self { + v.0 + } +} + +impl Display for VecIdent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let idents = self.0.iter(); + write!( + f, + "{}", + idents + .map(|i| i.name.to_string()) + .collect::>() + .join(".") + ) + } +} +impl VecIdent { + pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { + self.0.iter() + } + + pub fn span(&self) -> Span { + Span { + lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), + hi: self.0.last().map(|i| i.span.hi).unwrap_or_default(), + } + } +} + /// An identifier. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Ident { diff --git a/compiler/qsc_fir/src/global.rs b/compiler/qsc_fir/src/global.rs index 4591597c3c..a868789e65 100644 --- a/compiler/qsc_fir/src/global.rs +++ b/compiler/qsc_fir/src/global.rs @@ -5,12 +5,12 @@ use crate::{ fir::{Item, ItemId, ItemKind, Package, PackageId, Visibility}, ty::Scheme, }; -use qsc_data_structures::index_map; +use qsc_data_structures::{index_map, namespaces::{NamespaceId, NamespaceTreeRoot}}; use rustc_hash::FxHashMap; use std::rc::Rc; pub struct Global { - pub namespace: Rc, + pub namespace: Vec>, pub name: Rc, pub visibility: Visibility, pub kind: Kind, @@ -33,36 +33,43 @@ pub struct Term { #[derive(Default)] pub struct Table { - tys: FxHashMap, FxHashMap, Ty>>, - terms: FxHashMap, FxHashMap, Term>>, + tys: FxHashMap, Ty>>, + terms: FxHashMap, Term>>, + namespaces: NamespaceTreeRoot, } impl Table { #[must_use] - pub fn resolve_ty(&self, namespace: &str, name: &str) -> Option<&Ty> { - self.tys.get(namespace).and_then(|terms| terms.get(name)) + pub fn resolve_ty(&self, namespace: NamespaceId, name: &str) -> Option<&Ty> { + self.tys.get(&namespace).and_then(|terms| terms.get(name)) } #[must_use] - pub fn resolve_term(&self, namespace: &str, name: &str) -> Option<&Term> { - self.terms.get(namespace).and_then(|terms| terms.get(name)) + pub fn resolve_term(&self, namespace: NamespaceId, name: &str) -> Option<&Term> { + self.terms.get(&namespace).and_then(|terms| terms.get(name)) + } + + pub fn find_namespace(&self, vec: impl Into>>) -> Option { + // find a namespace if it exists and return its id + self.namespaces.find_namespace(vec) } } + impl FromIterator for Table { fn from_iter>(iter: T) -> Self { let mut tys: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); let mut terms: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); + let mut namespaces = NamespaceTreeRoot::default(); for global in iter { + let namespace = namespaces.upsert_namespace(global.namespace); match global.kind { Kind::Ty(ty) => { - tys.entry(global.namespace) - .or_default() - .insert(global.name, ty); + tys.entry(namespace).or_default().insert(global.name, ty); } Kind::Term(term) => { terms - .entry(global.namespace) + .entry(namespace) .or_default() .insert(global.name, term); } @@ -70,7 +77,12 @@ impl FromIterator for Table { } } - Self { tys, terms } + Self { + namespaces, + tys, + terms, + } + } } @@ -98,7 +110,7 @@ impl PackageIter<'_> { match (&item.kind, &parent) { (ItemKind::Callable(decl), Some(ItemKind::Namespace(namespace, _))) => Some(Global { - namespace: Rc::clone(&namespace.name), + namespace: namespace.into(), name: Rc::clone(&decl.name.name), visibility: item.visibility, kind: Kind::Term(Term { @@ -109,7 +121,7 @@ impl PackageIter<'_> { }), (ItemKind::Ty(name, def), Some(ItemKind::Namespace(namespace, _))) => { self.next = Some(Global { - namespace: Rc::clone(&namespace.name), + namespace: namespace.into(), name: Rc::clone(&name.name), visibility: item.visibility, kind: Kind::Term(Term { @@ -119,15 +131,15 @@ impl PackageIter<'_> { }); Some(Global { - namespace: Rc::clone(&namespace.name), + namespace: namespace.into(), name: Rc::clone(&name.name), visibility: item.visibility, kind: Kind::Ty(Ty { id }), }) } (ItemKind::Namespace(ident, _), None) => Some(Global { - namespace: "".into(), - name: Rc::clone(&ident.name), + namespace: ident.into(), + name: "".into(), visibility: Visibility::Public, kind: Kind::Namespace, }), diff --git a/compiler/qsc_fir/src/mut_visit.rs b/compiler/qsc_fir/src/mut_visit.rs index 1b144a9f6d..e7e1b5578c 100644 --- a/compiler/qsc_fir/src/mut_visit.rs +++ b/compiler/qsc_fir/src/mut_visit.rs @@ -3,7 +3,7 @@ use crate::fir::{ Block, BlockId, CallableDecl, CallableImpl, Expr, ExprId, ExprKind, Ident, Item, ItemKind, - Package, Pat, PatId, PatKind, SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StringComponent, + Package, Pat, PatId, PatKind, SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StringComponent, VecIdent, }; pub trait MutVisitor<'a>: Sized { @@ -49,6 +49,8 @@ pub trait MutVisitor<'a>: Sized { fn visit_ident(&mut self, _: &'a mut Ident) {} + fn visit_vec_ident(&mut self, _: &'a mut VecIdent) {} + fn get_block(&mut self, id: BlockId) -> &'a mut Block; fn get_expr(&mut self, id: ExprId) -> &'a mut Expr; fn get_pat(&mut self, id: PatId) -> &'a mut Pat; @@ -63,7 +65,8 @@ pub fn walk_package<'a>(vis: &mut impl MutVisitor<'a>, package: &'a mut Package) pub fn walk_item<'a>(vis: &mut impl MutVisitor<'a>, item: &'a mut Item) { match &mut item.kind { ItemKind::Callable(decl) => vis.visit_callable_decl(decl), - ItemKind::Namespace(name, _) | ItemKind::Ty(name, _) => vis.visit_ident(name), + ItemKind::Namespace(name, _) => vis.visit_vec_ident(name), + ItemKind::Ty(name, _) => vis.visit_ident(name), }; } diff --git a/compiler/qsc_fir/src/visit.rs b/compiler/qsc_fir/src/visit.rs index 81cb167c47..dc16a1f258 100644 --- a/compiler/qsc_fir/src/visit.rs +++ b/compiler/qsc_fir/src/visit.rs @@ -3,7 +3,7 @@ use crate::fir::{ Block, BlockId, CallableDecl, CallableImpl, Expr, ExprId, ExprKind, Ident, Item, ItemKind, - Package, Pat, PatId, PatKind, SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StringComponent, + Package, Pat, PatId, PatKind, SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StringComponent, VecIdent, }; pub trait Visitor<'a>: Sized { @@ -49,6 +49,8 @@ pub trait Visitor<'a>: Sized { fn visit_ident(&mut self, _: &'a Ident) {} + fn visit_vec_ident(&mut self, _: &'a VecIdent) {} + fn get_block(&self, id: BlockId) -> &'a Block; fn get_expr(&self, id: ExprId) -> &'a Expr; fn get_pat(&self, id: PatId) -> &'a Pat; @@ -63,7 +65,9 @@ pub fn walk_package<'a>(vis: &mut impl Visitor<'a>, package: &'a Package) { pub fn walk_item<'a>(vis: &mut impl Visitor<'a>, item: &'a Item) { match &item.kind { ItemKind::Callable(decl) => vis.visit_callable_decl(decl), - ItemKind::Namespace(name, _) | ItemKind::Ty(name, _) => vis.visit_ident(name), + ItemKind::Namespace(name, _) => vis.visit_vec_ident(name), + ItemKind::Ty(name, _) => vis.visit_ident(name), + }; } diff --git a/compiler/qsc_rca/src/overrider.rs b/compiler/qsc_rca/src/overrider.rs index e47224f705..0cbf4f74d8 100644 --- a/compiler/qsc_rca/src/overrider.rs +++ b/compiler/qsc_rca/src/overrider.rs @@ -356,7 +356,7 @@ impl<'a> Visitor<'a> for Overrider<'a> { .items .iter() .filter_map(|(_, item)| match &item.kind { - ItemKind::Namespace(ident, items) => Some((ident.name.to_string(), items)), + ItemKind::Namespace(ident, items) => Some((format!("{ident}"), items)), _ => None, }); for (namespace_ident, namespace_items) in namespaces { From 51eefa14cfcb51153f50aaa95602c17fff9fcbc0 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 10:36:02 -0700 Subject: [PATCH 23/76] lower VecIdent for fir and hir --- compiler/qsc_eval/src/lower.rs | 6 +++--- compiler/qsc_fir/src/fir.rs | 6 ++++++ compiler/qsc_frontend/src/lower.rs | 6 +++--- compiler/qsc_hir/src/global.rs | 2 -- compiler/qsc_hir/src/hir.rs | 6 ++++++ 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/compiler/qsc_eval/src/lower.rs b/compiler/qsc_eval/src/lower.rs index 4d3ff0baa1..8abb4af009 100644 --- a/compiler/qsc_eval/src/lower.rs +++ b/compiler/qsc_eval/src/lower.rs @@ -142,7 +142,6 @@ impl Lowerer { fn lower_item(&mut self, item: &hir::Item) -> fir::Item { let kind = match &item.kind { hir::ItemKind::Namespace(name, items) => { - let name = name.iter().map(Clone::clone).collect::>(); let name = self.lower_vec_ident(&name); let items = items.iter().map(|i| lower_local_item_id(*i)).collect(); fir::ItemKind::Namespace(name, items) @@ -752,8 +751,9 @@ impl Lowerer { name_span: field.name_span, } } - fn lower_vec_ident(&self, name: &[hir::Ident]) -> fir::VecIdent { - todo!("should this return a vec of hir idents or just one hir ident? or maybe a namespace id?") + + fn lower_vec_ident(&mut self, name: &hir::VecIdent) -> fir::VecIdent { + name.iter().cloned().map(|ident| self.lower_ident(&ident)).collect() } } diff --git a/compiler/qsc_fir/src/fir.rs b/compiler/qsc_fir/src/fir.rs index f5623ca249..8fc7f7d9c2 100644 --- a/compiler/qsc_fir/src/fir.rs +++ b/compiler/qsc_fir/src/fir.rs @@ -1450,6 +1450,12 @@ impl From for Vec { } } +impl FromIterator for VecIdent { + fn from_iter>(iter: T) -> Self { + VecIdent(iter.into_iter().collect()) + } +} + impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let idents = self.0.iter(); diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs index b0b740e042..a65f27f8c9 100644 --- a/compiler/qsc_frontend/src/lower.rs +++ b/compiler/qsc_frontend/src/lower.rs @@ -137,7 +137,7 @@ impl With<'_> { .filter_map(|i| self.lower_item(ItemScope::Global, i)) .collect(); - let name = self.lower_vec_ident(&namespace.name.0); + let name = self.lower_vec_ident(&namespace.name); self.lowerer.items.push(hir::Item { id, span: namespace.span, @@ -738,8 +738,8 @@ impl With<'_> { }) } - fn lower_vec_ident(&self, name: &[ast::Ident]) -> hir::VecIdent { - todo!("should this return a vec of hir idents or just one hir ident? or maybe a namespace id?") + fn lower_vec_ident(&mut self, name: &ast::VecIdent) -> hir::VecIdent { + name.iter().cloned().map(|i| self.lower_ident(&i)).collect() } } diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index e131772534..db2cd8709f 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -85,8 +85,6 @@ impl FromIterator for Table { } } - // TODO; copy namespace root etc over here and - // create a namespace structure with IDs Self { namespaces, tys, diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index beb0ef2737..13a5f4b697 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -1161,6 +1161,12 @@ impl From for Vec { } } +impl FromIterator for VecIdent { + fn from_iter>(iter: T) -> Self { + VecIdent(iter.into_iter().collect()) + } +} + impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let idents = self.0.iter(); From 2187506f22b8fbe210b811fa4c4794e603360a78 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 10:47:07 -0700 Subject: [PATCH 24/76] remove todos from name locator and hover in lang service --- language_service/src/hover.rs | 2 +- language_service/src/name_locator.rs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/language_service/src/hover.rs b/language_service/src/hover.rs index 1269b50ce3..d02bb2ca6f 100644 --- a/language_service/src/hover.rs +++ b/language_service/src/hover.rs @@ -59,7 +59,7 @@ impl<'a> Handler<'a> for HoverGenerator<'a> { ) { let contents = display_callable( &context.current_item_doc, - &context.current_namespace, + &context.current_namespace.join("."), self.display.ast_callable_decl(decl), ); self.hover = Some(Hover { diff --git a/language_service/src/name_locator.rs b/language_service/src/name_locator.rs index 868cd006cc..e99bf35563 100644 --- a/language_service/src/name_locator.rs +++ b/language_service/src/name_locator.rs @@ -89,7 +89,7 @@ pub(crate) struct LocatorContext<'package> { pub(crate) current_callable: Option<&'package ast::CallableDecl>, pub(crate) lambda_params: Vec<&'package ast::Pat>, pub(crate) current_item_doc: Rc, - pub(crate) current_namespace: Rc, + pub(crate) current_namespace: Vec>, pub(crate) in_params: bool, pub(crate) in_lambda_params: bool, pub(crate) current_udt_id: Option<&'package hir::ItemId>, @@ -113,7 +113,7 @@ impl<'inner, 'package, T> Locator<'inner, 'package, T> { offset, compilation, context: LocatorContext { - current_namespace: Rc::from(""), + current_namespace: Vec::new(), current_callable: None, in_params: false, lambda_params: vec![], @@ -128,8 +128,7 @@ impl<'inner, 'package, T> Locator<'inner, 'package, T> { impl<'inner, 'package, T: Handler<'package>> Visitor<'package> for Locator<'inner, 'package, T> { fn visit_namespace(&mut self, namespace: &'package ast::Namespace) { if span_contains(namespace.span, self.offset) { - todo!("should below line use absolute or relative ns?"); - //self.context.current_namespace = namespace.name.name.clone(); + self.context.current_namespace = namespace.name.clone().into(); walk_namespace(self, namespace); } } From 50ddf457825d754f46bc3181b5ef4c3809d520a1 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 11:17:59 -0700 Subject: [PATCH 25/76] include prelude namespace IDs in default opens --- language_service/src/compilation.rs | 26 +++++++++++++++----------- language_service/src/completion.rs | 22 ++++++++++------------ 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/language_service/src/compilation.rs b/language_service/src/compilation.rs index db2739ef4c..8ef7fa96e9 100644 --- a/language_service/src/compilation.rs +++ b/language_service/src/compilation.rs @@ -3,19 +3,10 @@ use log::trace; use qsc::{ - ast, - compile::{self, Error}, - display::Lookup, - error::WithSource, - hir::{self, PackageId}, - incremental::Compiler, - line_column::{Encoding, Position}, - resolve, - target::Profile, - CompileUnit, LanguageFeatures, PackageStore, PackageType, SourceMap, Span, + ast, compile::{self, Error}, display::Lookup, error::WithSource, hir::{self, PackageId}, incremental::Compiler, line_column::{Encoding, Position}, resolve, target::Profile, CompileUnit, LanguageFeatures, NamespaceId, PackageStore, PackageType, SourceMap, Span }; use qsc_linter::LintConfig; -use std::sync::Arc; +use std::{rc::Rc, sync::Arc}; /// Represents an immutable compilation state that can be used /// to implement language service features. @@ -218,6 +209,19 @@ impl Compilation { self.user_package_id = new.user_package_id; self.errors = new.errors; } + + pub(crate) fn find_namespace_id(&self, ns: [&str; 3]) -> NamespaceId { + self + .package_store + .get(self.user_package_id) + .expect("user package should exist") + .ast + .namespaces + .find_namespace(ns.into_iter().map(|s| Rc::from(s)).collect::>()) + .expect("namespace should exist") + + + } } impl Lookup for Compilation { diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index cc0f0e99b8..f6ba9b516c 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -11,19 +11,19 @@ use qsc::ast::ast; use qsc::ast::visit::{self, Visitor}; use qsc::display::{CodeDisplay, Lookup}; -use qsc::hir::{ItemKind, Package, PackageId, Visibility}; +use qsc::hir::{ItemKind, Package, PackageId}; use qsc::line_column::{Encoding, Position, Range}; use qsc::resolve::{Local, LocalKind}; -use qsc::{NamespaceId, NamespaceTreeNode, NamespaceTreeRoot}; +use qsc::{NamespaceId, NamespaceTreeNode}; use rustc_hash::FxHashSet; -use std::cell::RefCell; +use core::prelude; use std::collections::HashMap; use std::rc::Rc; -const PRELUDE: [&str; 3] = [ - "Microsoft.Quantum.Canon", - "Microsoft.Quantum.Core", - "Microsoft.Quantum.Intrinsic", +const PRELUDE: [[&str; 3]; 3] = [ + ["Microsoft", "Quantum","Canon"], + ["Microsoft", "Quantum","Core"], + ["Microsoft", "Quantum","Intrinsic"] ]; pub(crate) fn get_completions( @@ -73,12 +73,10 @@ pub(crate) fn get_completions( None => String::new(), }; + let mut prelude_ns_ids: Vec<_> = PRELUDE.iter().map(|ns| (compilation.find_namespace_id(*ns), None)).collect(); + // The PRELUDE namespaces are always implicitly opened. - context_finder.opens.extend( - PRELUDE - .into_iter() - .map(|ns| (todo!("this needs to be a ns id") as NamespaceId, None)), - ); + context_finder.opens.append(&mut prelude_ns_ids); let mut builder = CompletionListBuilder::new(); From ee532c0c393711b5768f8c364ac1fd36c04088d4 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 11:36:33 -0700 Subject: [PATCH 26/76] refactor opens to use string identifiers for namespaces instead of ids --- language_service/src/completion.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index f6ba9b516c..3d7a11fbaa 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -73,7 +73,7 @@ pub(crate) fn get_completions( None => String::new(), }; - let mut prelude_ns_ids: Vec<_> = PRELUDE.iter().map(|ns| (compilation.find_namespace_id(*ns), None)).collect(); + let mut prelude_ns_ids: Vec<_> = PRELUDE.into_iter().map(|ns| (ns.into_iter().map(Rc::from).collect(), None)).collect(); // The PRELUDE namespaces are always implicitly opened. context_finder.opens.append(&mut prelude_ns_ids); @@ -267,9 +267,9 @@ impl CompletionListBuilder { fn push_globals( &mut self, compilation: &Compilation, - opens: &[(NamespaceId, Option>)], + opens: &[(Vec>, Option>)], insert_open_range: Option, - current_namespace_name: &Option, + current_namespace_name: &Option>, indent: &String, ) { let core = &compilation @@ -391,9 +391,9 @@ impl CompletionListBuilder { compilation: &'a Compilation, package_id: PackageId, // name and alias - opens: &'a [(NamespaceId, Option>)], + opens: &'a [(Vec>, Option>)], insert_open_at: Option, - current_namespace_name: Option, + current_namespace_name: Option>, indent: &'a String, ) -> impl Iterator + 'a { let package = &compilation @@ -542,9 +542,9 @@ fn local_completion( struct ContextFinder { offset: u32, context: Context, - opens: Vec<(NamespaceId, Option>)>, + opens: Vec<(Vec>, Option>)>, start_of_namespace: Option, - current_namespace_name: Option, + current_namespace_name: Option>, } #[derive(Debug, PartialEq)] @@ -559,7 +559,7 @@ enum Context { impl Visitor<'_> for ContextFinder { fn visit_namespace(&mut self, namespace: &'_ qsc::ast::Namespace) { if span_contains(namespace.span, self.offset) { - self.current_namespace_name = Some(todo!("how will completions work here? should we use the immediate namespace or the absolute namespace name?")); + self.current_namespace_name = Some(Rc::from(format!("{}", namespace.name))); self.context = Context::Namespace; self.opens = vec![]; self.start_of_namespace = None; @@ -574,8 +574,7 @@ impl Visitor<'_> for ContextFinder { if let qsc::ast::ItemKind::Open(name, alias) = &*item.kind { self.opens.push(( - todo!("should namespace open be using namespace ID or rc str?"), //name.into(), - alias.as_ref().map(|alias| alias.name.clone()), + name.into(), alias.as_ref().map(|alias| alias.name.clone()) )); } From 8713435d971f491c84b1a8efb8d8674d280c0cac Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 13:06:20 -0700 Subject: [PATCH 27/76] remove todos in completions --- language_service/src/completion.rs | 112 ++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 8 deletions(-) diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index 3d7a11fbaa..0746757266 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -11,7 +11,7 @@ use qsc::ast::ast; use qsc::ast::visit::{self, Visitor}; use qsc::display::{CodeDisplay, Lookup}; -use qsc::hir::{ItemKind, Package, PackageId}; +use qsc::hir::{ItemKind, Package, PackageId, Visibility}; use qsc::line_column::{Encoding, Position, Range}; use qsc::resolve::{Local, LocalKind}; use qsc::{NamespaceId, NamespaceTreeNode}; @@ -269,7 +269,7 @@ impl CompletionListBuilder { compilation: &Compilation, opens: &[(Vec>, Option>)], insert_open_range: Option, - current_namespace_name: &Option>, + current_namespace_name: &Option>>, indent: &String, ) { let core = &compilation @@ -392,8 +392,10 @@ impl CompletionListBuilder { package_id: PackageId, // name and alias opens: &'a [(Vec>, Option>)], + // The range at which to insert an open statement if one is needed insert_open_at: Option, - current_namespace_name: Option>, + // The name of the current namespace, if any -- + current_namespace_name: Option>>, indent: &'a String, ) -> impl Iterator + 'a { let package = &compilation @@ -411,9 +413,103 @@ impl CompletionListBuilder { .ast .namespaces .clone(); - let namespaces = namespaces; - convert_ast_namespaces_into_hir_namespaces(namespaces); - vec![todo!()].into_iter() + let namespaces = convert_ast_namespaces_into_hir_namespaces(namespaces); + package.items.values().filter_map(move |i| { + // We only want items whose parents are namespaces + if let Some(item_id) = i.parent { + if let Some(parent) = package.items.get(item_id) { + if let ItemKind::Namespace(namespace, _) = &parent.kind { + if namespace.starts_with_sequence(&["Microsoft", "Quantum", "Unstable"]) { + return None; + } + // If the item's visibility is internal, the item may be ignored + if matches!(i.visibility, Visibility::Internal) { + if !is_user_package { + return None; // ignore item if not in the user's package + } + // ignore item if the user is not in the item's namespace + match ¤t_namespace_name { + Some(curr_ns) => { + if *curr_ns != Into::>>::into(namespace) { + return None; + } + } + None => { + return None; + } + } + } + return match &i.kind { + ItemKind::Callable(callable_decl) => { + let name = callable_decl.name.name.as_ref(); + let detail = + Some(display.hir_callable_decl(callable_decl).to_string()); + // Everything that starts with a __ goes last in the list + let sort_group = u32::from(name.starts_with("__")); + let mut additional_edits = vec![]; + let mut qualification: Option>> = None; + match ¤t_namespace_name { + Some(curr_ns) if *curr_ns == Into::>::into(namespace) => {} + _ => { + // open is an option of option of Rc + // the first option tells if it found an open with the namespace name + // the second, nested option tells if that open has an alias + let open = opens.iter().find_map(|(name, alias)| { + if *name == Into::>::into(namespace) { + Some(alias) + } else { + None + } + }); + qualification = match open { + Some(alias) => alias.clone().map(|x| vec![x]), + None => match insert_open_at { + Some(start) => { + additional_edits.push(TextEdit { + new_text: format!( + "open {};{}", + namespace, + indent, + ), + range: start, + }); + None + } + None => Some(namespace.into()), + }, + } + } + } + + let additional_text_edits = if additional_edits.is_empty() { + None + } else { + Some(additional_edits) + }; + + let label = if let Some(qualification) = qualification { + format!("{}.{name}", qualification.join(".")) + } else { + name.to_owned() + }; + Some(( + CompletionItem { + label, + kind: CompletionItemKind::Function, + sort_text: None, // This will get filled in during `push_sorted_completions` + detail, + additional_text_edits, + }, + sort_group, + )) + } + _ => None, + }; + } + } + } + None + }) } /// Get all callables in the core package @@ -544,7 +640,7 @@ struct ContextFinder { context: Context, opens: Vec<(Vec>, Option>)>, start_of_namespace: Option, - current_namespace_name: Option>, + current_namespace_name: Option>>, } #[derive(Debug, PartialEq)] @@ -559,7 +655,7 @@ enum Context { impl Visitor<'_> for ContextFinder { fn visit_namespace(&mut self, namespace: &'_ qsc::ast::Namespace) { if span_contains(namespace.span, self.offset) { - self.current_namespace_name = Some(Rc::from(format!("{}", namespace.name))); + self.current_namespace_name = Some(namespace.name.clone().into()); self.context = Context::Namespace; self.opens = vec![]; self.start_of_namespace = None; From 0bcd52688aef8881bebd7df6d98e629bf10cd880 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 14:42:34 -0700 Subject: [PATCH 28/76] implement get in name resolution --- compiler/qsc_frontend/src/resolve.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 71418d71dc..80a3f153a4 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -264,17 +264,17 @@ pub struct GlobalScope { } impl GlobalScope { - fn find_namespace(&self, ns: &VecIdent) -> Option { + fn find_namespace(&self, ns: impl Into>>) -> Option { self.namespaces.find_namespace(ns) } fn get(&self, kind: NameKind, namespace: &[Rc], name: &str) -> Option<&Res> { - todo!("resolve Vec> to namespace id") - // let namespaces = match kind { - // NameKind::Ty => &self.tys, - // NameKind::Term => &self.terms, - // }; - // namespaces.get(namespace).and_then(|items| items.get(name)) + let ns = self.find_namespace(namespace).expect("namespace should exist"); + let items = match kind { + NameKind::Ty => &self.tys, + NameKind::Term => &self.terms, + }; + items.get(&ns).and_then(|items| items.get(name)) } /// Creates a namespace in the namespace mapping. Note that namespaces are tracked separately from their From a78fe73663078af4d34d1009d7fde047fbf0fbbc Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 26 Mar 2024 15:57:43 -0700 Subject: [PATCH 29/76] continue work on resolve.rs --- compiler/qsc_data_structures/src/lib.rs | 2 +- compiler/qsc_fir/src/global.rs | 2 +- compiler/qsc_frontend/src/resolve.rs | 61 ++++++++++++------------- compiler/qsc_hir/src/global.rs | 2 +- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index c9f0b8a1fc..86c5380454 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -69,7 +69,7 @@ pub mod namespaces { } /// Upserts a namespace into the tree. If the namespace already exists, it will not be inserted. /// Returns the ID of the namespace. - pub fn upsert_namespace(&mut self, name: impl Into>>) -> NamespaceId { + pub fn insert_or_find_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.assigner += 1; let id = self.assigner; let node = self.new_namespace_node(Default::default()); diff --git a/compiler/qsc_fir/src/global.rs b/compiler/qsc_fir/src/global.rs index a868789e65..c1a2e3881f 100644 --- a/compiler/qsc_fir/src/global.rs +++ b/compiler/qsc_fir/src/global.rs @@ -62,7 +62,7 @@ impl FromIterator for Table { let mut terms: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); let mut namespaces = NamespaceTreeRoot::default(); for global in iter { - let namespace = namespaces.upsert_namespace(global.namespace); + let namespace = namespaces.insert_or_find_namespace(global.namespace); match global.kind { Kind::Ty(ty) => { tys.entry(namespace).or_default().insert(global.name, ty); diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 80a3f153a4..fd14d0abd2 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -279,8 +279,8 @@ impl GlobalScope { /// Creates a namespace in the namespace mapping. Note that namespaces are tracked separately from their /// item contents. This returns a [NamespaceId] which you can use to add more tys and terms to the scope. - fn upsert_namespace(&mut self, name: impl Into>>) -> NamespaceId { - self.namespaces.upsert_namespace(name) + fn insert_or_find_namespace(&mut self, name: impl Into>>) -> NamespaceId { + self.namespaces.insert_or_find_namespace(name) } } @@ -806,7 +806,7 @@ impl GlobalTable { } let mut scope = GlobalScope::default(); - let ns = scope.upsert_namespace(vec![ + let ns = scope.insert_or_find_namespace(vec![ Rc::from("Microsoft"), Rc::from("Quantum"), Rc::from("Core"), @@ -836,14 +836,8 @@ impl GlobalTable { match node { // if a namespace is nested, create child namespaces TopLevelNode::Namespace(namespace) => { - for namespace in namespace.name.iter() { - todo!("add nested namespaces here") - // self.names.insert( - // namespace.id, - // Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), - // ); - // parent.namespaces.push(Rc::clone(&namespace.name)); - } + let namespace_id = self.scope.namespaces.insert_or_find_namespace(namespace.name.clone()); + bind_global_items( &mut self.names, &mut self.scope, @@ -874,7 +868,7 @@ impl GlobalTable { global.visibility == hir::Visibility::Public || matches!(&global.kind, global::Kind::Term(t) if t.intrinsic) }) { - let namespace = self.scope.upsert_namespace(global.namespace); + let namespace = self.scope.insert_or_find_namespace(global.namespace.clone()); match (global.kind, global.visibility) { (global::Kind::Ty(ty), hir::Visibility::Public) => { self.scope @@ -896,8 +890,8 @@ impl GlobalTable { } } (global::Kind::Namespace, hir::Visibility::Public) => { - todo!("insert the namespace into the global scope") - // let namespace_id = self.scope.insert(global.name); + // todo!("Verify: does this need to have its items specifically inserted?"); + self.scope.insert_or_find_namespace(global.namespace); } (_, hir::Visibility::Internal) => {} } @@ -905,6 +899,7 @@ impl GlobalTable { } } +/// Given some namespace `namespace`, add all the globals declared within it to the global sc4pe. fn bind_global_items( names: &mut IndexMap, scope: &mut GlobalScope, @@ -912,12 +907,12 @@ fn bind_global_items( assigner: &mut Assigner, errors: &mut Vec, ) { - todo!("what is going on below?"); - // names.insert( - // namespace.name.id, - // Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), - // ); - let namespace_id = scope.upsert_namespace(namespace.name); + names.insert( + namespace.id, + Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), + ); + + let namespace_id = scope.insert_or_find_namespace(&namespace.name); for item in &*namespace.items { match bind_global_item( @@ -991,7 +986,7 @@ fn bind_global_item( let mut errors = Vec::new(); match scope .terms - .entry(todo!("refactor this to use namespace id")) + .entry(namespace) .or_default() .entry(Rc::clone(&decl.name.name)) { @@ -1026,12 +1021,12 @@ fn bind_global_item( match ( scope .terms - .entry(todo!("refactor this API to take namespace IDs")) + .entry(namespace) .or_default() .entry(Rc::clone(&name.name)), scope .tys - .entry(todo!("refactor this API to take namespace IDs")) + .entry(namespace) .or_default() .entry(Rc::clone(&name.name)), ) { @@ -1066,31 +1061,35 @@ fn resolve<'a>( globals: &GlobalScope, scopes: impl Iterator, name: &Ident, - namespace: &Option, + namespace_name: &Option, ) -> Result { let scopes = scopes.collect::>(); let mut candidates = FxHashMap::default(); let mut vars = true; let name_str = &(*name.name); - let namespace: &str = todo!("delete this"); - todo!("resolve Vec> to namespace id -- copilot suggestion. We need to handle nested resolve logic here"); + let namespace = if let Some(namespace) = namespace_name { + globals.find_namespace(namespace) + } else { + None + }; // let namespace = namespace.as_ref().map_or("", |i| &i.name); - dbg!(&"checking namespace", namespace); for scope in scopes { - if namespace.is_empty() { + if namespace.is_none() { if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, name_str) { // Local declarations shadow everything. return Ok(res); } } - if let Some(namespaces) = scope.opens.get(namespace) { + if let Some(name) = namespace_name { + if let (Some(namespaces)) = scope.opens.get(todo!("This key is the namespace name or alias. That means that we need to refactor that to use NS ids.")) { candidates = resolve_explicit_opens(kind, globals, namespaces, name_str); if !candidates.is_empty() { // Explicit opens shadow prelude and unopened globals. break; } } + } if scope.kind == ScopeKind::Callable { // Since local callables are not closures, hide local variables in parent scopes. @@ -1098,7 +1097,7 @@ fn resolve<'a>( } } - if candidates.is_empty() && namespace.is_empty() { + if candidates.is_empty() && namespace.is_none() { // Prelude shadows unopened globals. let candidates = resolve_implicit_opens(kind, globals, PRELUDE, name_str); if candidates.len() > 1 { @@ -1128,7 +1127,7 @@ fn resolve<'a>( if candidates.is_empty() { if let Some(&res) = globals.get( kind, - todo!("should be able to pass namespace id in here"), + &namespace_name.map(|x: VecIdent | Into::>::into(x)).unwrap_or_default(), name_str, ) { // An unopened global is the last resort. diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index db2cd8709f..d5a8baf85d 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -70,7 +70,7 @@ impl FromIterator for Table { let mut terms: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); let mut namespaces = NamespaceTreeRoot::default(); for global in iter { - let namespace = namespaces.upsert_namespace(global.namespace); + let namespace = namespaces.insert_or_find_namespace(global.namespace); match global.kind { Kind::Ty(ty) => { tys.entry(namespace).or_default().insert(global.name, ty); From 5da2fb9f6237b45f0172fdc1508392ef5baa6f92 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 27 Mar 2024 09:34:18 -0700 Subject: [PATCH 30/76] bind opens with namespace ids --- compiler/qsc_frontend/src/resolve.rs | 45 +++++++++++++++++----------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index fd14d0abd2..b87d97d8be 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -129,7 +129,7 @@ pub struct Scope { span: Span, kind: ScopeKind, /// Open statements. The key is the namespace name or alias. - opens: FxHashMap, Vec>, + opens: FxHashMap>, /// Local newtype declarations. tys: FxHashMap, ItemId>, /// Local callable and newtype declarations. @@ -269,7 +269,9 @@ impl GlobalScope { } fn get(&self, kind: NameKind, namespace: &[Rc], name: &str) -> Option<&Res> { - let ns = self.find_namespace(namespace).expect("namespace should exist"); + let ns = self + .find_namespace(namespace) + .expect("namespace should exist"); let items = match kind { NameKind::Ty => &self.tys, NameKind::Term => &self.terms, @@ -508,11 +510,13 @@ impl Resolver { } fn bind_open(&mut self, name: &ast::VecIdent, alias: &Option>) { - let alias = alias.as_ref().map_or("".into(), |a| Rc::clone(&a.name)); + // TODO figure out how aliases are going to work + let id = self.globals.find_namespace(name).expect("ns should exist"); + // let alias = alias.as_ref().map_or("".into(), |a| Rc::clone(&a.name)); if self.globals.namespaces.find_namespace(name).is_some() { self.current_scope_mut() .opens - .entry(alias) + .entry(id) .or_default() .push(Open { namespace: name.into(), @@ -836,8 +840,11 @@ impl GlobalTable { match node { // if a namespace is nested, create child namespaces TopLevelNode::Namespace(namespace) => { - let namespace_id = self.scope.namespaces.insert_or_find_namespace(namespace.name.clone()); - + let namespace_id = self + .scope + .namespaces + .insert_or_find_namespace(namespace.name.clone()); + bind_global_items( &mut self.names, &mut self.scope, @@ -868,7 +875,9 @@ impl GlobalTable { global.visibility == hir::Visibility::Public || matches!(&global.kind, global::Kind::Term(t) if t.intrinsic) }) { - let namespace = self.scope.insert_or_find_namespace(global.namespace.clone()); + let namespace = self + .scope + .insert_or_find_namespace(global.namespace.clone()); match (global.kind, global.visibility) { (global::Kind::Ty(ty), hir::Visibility::Public) => { self.scope @@ -890,7 +899,7 @@ impl GlobalTable { } } (global::Kind::Namespace, hir::Visibility::Public) => { - // todo!("Verify: does this need to have its items specifically inserted?"); + // todo!("Verify: does this need to have its items specifically inserted?"); self.scope.insert_or_find_namespace(global.namespace); } (_, hir::Visibility::Internal) => {} @@ -911,7 +920,7 @@ fn bind_global_items( namespace.id, Res::Item(intrapackage(assigner.next_item()), ItemStatus::Available), ); - + let namespace_id = scope.insert_or_find_namespace(&namespace.name); for item in &*namespace.items { @@ -1081,15 +1090,15 @@ fn resolve<'a>( } } - if let Some(name) = namespace_name { - if let (Some(namespaces)) = scope.opens.get(todo!("This key is the namespace name or alias. That means that we need to refactor that to use NS ids.")) { - candidates = resolve_explicit_opens(kind, globals, namespaces, name_str); - if !candidates.is_empty() { - // Explicit opens shadow prelude and unopened globals. - break; + if let Some(namespace) = namespace { + if let (Some(namespaces)) = scope.opens.get(&namespace) { + candidates = resolve_explicit_opens(kind, globals, namespaces, name_str); + if !candidates.is_empty() { + // Explicit opens shadow prelude and unopened globals. + break; + } } } - } if scope.kind == ScopeKind::Callable { // Since local callables are not closures, hide local variables in parent scopes. @@ -1127,7 +1136,9 @@ fn resolve<'a>( if candidates.is_empty() { if let Some(&res) = globals.get( kind, - &namespace_name.map(|x: VecIdent | Into::>::into(x)).unwrap_or_default(), + &namespace_name + .map(|x: VecIdent| Into::>::into(x)) + .unwrap_or_default(), name_str, ) { // An unopened global is the last resort. From 7c5b9d6c974a038b7f6deda5a482f42c49160361 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 27 Mar 2024 11:04:51 -0700 Subject: [PATCH 31/76] finish removing TODOs; work on unit testing --- compiler/qsc_data_structures/src/lib.rs | 442 ++++++++++++++++++++++-- compiler/qsc_fir/src/global.rs | 2 +- compiler/qsc_frontend/src/resolve.rs | 45 +-- compiler/qsc_hir/src/global.rs | 2 +- 4 files changed, 442 insertions(+), 49 deletions(-) diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index 86c5380454..ebbb6226f2 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -8,7 +8,7 @@ pub mod language_features; pub mod line_column; pub mod span; pub mod namespaces { - use std::{cell::RefCell, collections::HashMap, fmt::Display, ops::Deref, rc::Rc}; + use std::{cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; #[derive(Debug, Clone)] @@ -67,31 +67,17 @@ pub mod namespaces { pub fn tree(&self) -> &NamespaceTreeNode { &self.tree } - /// Upserts a namespace into the tree. If the namespace already exists, it will not be inserted. - /// Returns the ID of the namespace. - pub fn insert_or_find_namespace(&mut self, name: impl Into>>) -> NamespaceId { - self.assigner += 1; - let id = self.assigner; - let node = self.new_namespace_node(Default::default()); - let components_iter = name.into(); - let components_iter = components_iter.iter(); - // construct the initial rover for the breadth-first insertion - // (this is like a BFS but we create a new node if one doesn't exist) - let self_cell = RefCell::new(self); - let mut rover_node = &mut self_cell.borrow_mut().tree; - // create the rest of the nodes - for component in components_iter { - rover_node = rover_node - .children - .entry(Rc::clone(component)) - .or_insert_with(|| { - self_cell - .borrow_mut() - .new_namespace_node(Default::default()) - }); - } - rover_node.id + + /// Insert a namespace into the tree. If the namespace already exists, return its ID. + pub fn insert_or_find_namespace( + &mut self, + ns: impl IntoIterator>, + ) -> NamespaceId { + self.tree + .insert_or_find_namespace(ns.into_iter().peekable(), &mut self.assigner) + .expect("namespace creation should not fail") } + pub fn new_namespace_node( &mut self, children: HashMap, NamespaceTreeNode>, @@ -106,6 +92,379 @@ pub mod namespaces { pub fn find_namespace(&self, ns: impl Into>>) -> Option { self.tree.find_namespace(ns) } + + pub fn find_id(&self, id: &NamespaceId) -> (Vec>, Rc<&NamespaceTreeNode>) { + // traverse the nodes to find the node with the correct id + let mut buf = Rc::new(&self.tree); + let mut components = Vec::new(); + while buf.id != *id { + let mut found = false; + for (component, node) in buf.children.iter() { + if node.id == *id { + components.push(Rc::clone(component)); + buf = Rc::new(node); + found = true; + break; + } + } + if !found { + panic!("Namespace ID {id} not found in tree") + } + } + return (components, buf); + } + + pub fn root_id(&self) -> NamespaceId { + self.tree.id + } + } + + // write some unit tests for the above `find_id` method + #[cfg(test)] + mod tests { + use expect_test::expect; + + use super::*; + use std::collections::HashMap; + + #[test] + fn test_tree_construction() { + let mut root = NamespaceTreeRoot::default(); + for i in 0..10 { + for j in 'a'..'d' { + root.insert_or_find_namespace(vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter()); + } + } + expect![[r#" + NamespaceTreeRoot { + assigner: 40, + tree: NamespaceTreeNode { + children: { + "ns6": NamespaceTreeNode { + children: { + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 27, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 28, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 26, + ), + }, + }, + id: NamespaceId( + 25, + ), + }, + "ns3": NamespaceTreeNode { + children: { + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 14, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 16, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 15, + ), + }, + }, + id: NamespaceId( + 13, + ), + }, + "ns7": NamespaceTreeNode { + children: { + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 32, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 31, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 30, + ), + }, + }, + id: NamespaceId( + 29, + ), + }, + "ns8": NamespaceTreeNode { + children: { + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 35, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 36, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 34, + ), + }, + }, + id: NamespaceId( + 33, + ), + }, + "ns0": NamespaceTreeNode { + children: { + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 3, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 2, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 4, + ), + }, + }, + id: NamespaceId( + 1, + ), + }, + "ns4": NamespaceTreeNode { + children: { + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 20, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 18, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 19, + ), + }, + }, + id: NamespaceId( + 17, + ), + }, + "ns2": NamespaceTreeNode { + children: { + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 10, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 11, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 12, + ), + }, + }, + id: NamespaceId( + 9, + ), + }, + "ns5": NamespaceTreeNode { + children: { + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 22, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 23, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 24, + ), + }, + }, + id: NamespaceId( + 21, + ), + }, + "ns1": NamespaceTreeNode { + children: { + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 8, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 6, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 7, + ), + }, + }, + id: NamespaceId( + 5, + ), + }, + "ns9": NamespaceTreeNode { + children: { + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 38, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 39, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 40, + ), + }, + }, + id: NamespaceId( + 37, + ), + }, + }, + id: NamespaceId( + 0, + ), + }, + } + "#]] + .assert_debug_eq(&root); + } + + #[test] + fn test_find_id() { + let mut root = NamespaceTreeRoot::default(); + let mut id_buf = vec![]; + for i in 0..10 { + for j in 'a'..'d' { + id_buf.push(root.insert_or_find_namespace(vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter())); + } + } + for id in id_buf { + root.find_id(&id); + } + } + // test that after inserting lots of namespaces, all ids are unique and sequential + #[test] + fn test_insert_or_find_namespace() { + let mut root = NamespaceTreeRoot::default(); + let mut ids: Vec = vec![]; + for i in 0..10 { + for j in 'a'..'d' { + let id = root.insert_or_find_namespace(vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter()); + ids.push(id.into()); + } + } + let mut ids_sorted = ids.clone(); + ids_sorted.sort(); + ids_sorted.dedup(); + // there should be no duplicate or out-of-order ids + assert_eq!(ids_sorted, ids); + expect![[r#" + [ + 2, + 3, + 4, + 6, + 7, + 8, + 10, + 11, + 12, + 14, + 15, + 16, + 18, + 19, + 20, + 22, + 23, + 24, + 26, + 27, + 28, + 30, + 31, + 32, + 34, + 35, + 36, + 38, + 39, + 40, + ] + "#]] + .assert_debug_eq(&ids); + } } impl Default for NamespaceTreeRoot { fn default() -> Self { @@ -154,5 +513,38 @@ pub mod namespaces { } return Some(buf.id); } + + /// If the namespace already exists, it will not be inserted. + /// Returns the ID of the namespace. + pub fn insert_or_find_namespace( + &mut self, + mut iter: Peekable, + assigner: &mut usize, + ) -> Option where I: Iterator> { + let next_item = match iter.next() { + Some(item) => item, + None => return None, + }; + println!("Inserting namespace {}", next_item); + + let next_node = self.children.get_mut(&next_item); + if let Some(mut next_node) = next_node { + return next_node.insert_or_find_namespace(iter, assigner); + } else { + println!("creating new node"); + *assigner += 1; + let mut new_node = + NamespaceTreeNode::new(NamespaceId::new(*assigner), HashMap::new()); + if iter.peek().is_none() { + let new_node_id = new_node.id; + self.children.insert(next_item, new_node); + return Some(new_node_id); + } else { + let id = new_node.insert_or_find_namespace(iter, assigner); + self.children.insert(next_item, new_node); + return id; + } + } + } } } diff --git a/compiler/qsc_fir/src/global.rs b/compiler/qsc_fir/src/global.rs index c1a2e3881f..8f25af3a72 100644 --- a/compiler/qsc_fir/src/global.rs +++ b/compiler/qsc_fir/src/global.rs @@ -62,7 +62,7 @@ impl FromIterator for Table { let mut terms: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); let mut namespaces = NamespaceTreeRoot::default(); for global in iter { - let namespace = namespaces.insert_or_find_namespace(global.namespace); + let namespace = namespaces.insert_or_find_namespace(global.namespace.into_iter()); match global.kind { Kind::Ty(ty) => { tys.entry(namespace).or_default().insert(global.name, ty); diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index b87d97d8be..100f5474ef 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -268,27 +268,24 @@ impl GlobalScope { self.namespaces.find_namespace(ns) } - fn get(&self, kind: NameKind, namespace: &[Rc], name: &str) -> Option<&Res> { - let ns = self - .find_namespace(namespace) - .expect("namespace should exist"); + fn get(&self, kind: NameKind, namespace: NamespaceId, name: &str) -> Option<&Res> { let items = match kind { NameKind::Ty => &self.tys, NameKind::Term => &self.terms, }; - items.get(&ns).and_then(|items| items.get(name)) + items.get(&namespace).and_then(|items| items.get(name)) } /// Creates a namespace in the namespace mapping. Note that namespaces are tracked separately from their /// item contents. This returns a [NamespaceId] which you can use to add more tys and terms to the scope. fn insert_or_find_namespace(&mut self, name: impl Into>>) -> NamespaceId { - self.namespaces.insert_or_find_namespace(name) + self.namespaces.insert_or_find_namespace(name.into()) } } #[derive(Debug, Clone, Eq, PartialEq)] enum ScopeKind { - Namespace(Vec>), + Namespace(NamespaceId), Callable, Block, } @@ -301,7 +298,7 @@ enum NameKind { #[derive(Debug, Clone)] struct Open { - namespace: Vec>, + namespace: NamespaceId, span: Span, } @@ -519,7 +516,7 @@ impl Resolver { .entry(id) .or_default() .push(Open { - namespace: name.into(), + namespace: id, span: name.span(), }); } else { @@ -645,7 +642,8 @@ impl With<'_> { impl AstVisitor<'_> for With<'_> { fn visit_namespace(&mut self, namespace: &ast::Namespace) { - let kind = ScopeKind::Namespace(namespace.name.clone().into()); + let ns = self.resolver.globals.find_namespace(Into::>::into(namespace.name.clone())).expect("namespace should exist"); + let kind = ScopeKind::Namespace(ns); self.with_scope(namespace.span, kind, |visitor| { for item in &*namespace.items { if let ast::ItemKind::Open(name, alias) = &*item.kind { @@ -843,7 +841,7 @@ impl GlobalTable { let namespace_id = self .scope .namespaces - .insert_or_find_namespace(namespace.name.clone()); + .insert_or_find_namespace(Into::>::into(namespace.name.clone())); bind_global_items( &mut self.names, @@ -899,7 +897,7 @@ impl GlobalTable { } } (global::Kind::Namespace, hir::Visibility::Public) => { - // todo!("Verify: does this need to have its items specifically inserted?"); + // TODO ("Verify: does this need to have its items specifically inserted?"); self.scope.insert_or_find_namespace(global.namespace); } (_, hir::Visibility::Internal) => {} @@ -1110,6 +1108,7 @@ fn resolve<'a>( // Prelude shadows unopened globals. let candidates = resolve_implicit_opens(kind, globals, PRELUDE, name_str); if candidates.len() > 1 { + // If there are multiple candidates, sort them by namespace and return an error. let mut candidates: Vec<_> = candidates.into_iter().collect(); candidates.sort_by_key(|x| x.1); let mut candidates = candidates @@ -1128,6 +1127,7 @@ fn resolve<'a>( candidate_b, }); } + // if there is a candidate, return it if let Some((res, _)) = single(candidates) { return Ok(res); } @@ -1136,9 +1136,7 @@ fn resolve<'a>( if candidates.is_empty() { if let Some(&res) = globals.get( kind, - &namespace_name - .map(|x: VecIdent| Into::>::into(x)) - .unwrap_or_default(), + namespace.unwrap_or_else(|| globals.namespaces.root_id()), name_str, ) { // An unopened global is the last resort. @@ -1164,10 +1162,12 @@ fn resolve<'a>( if candidates.len() > 1 { let mut opens: Vec<_> = candidates.into_values().collect(); opens.sort_unstable_by_key(|open| open.span); + let (first_open_ns, _) = globals.namespaces.find_id(&opens[0].namespace); + let (second_open_ns, _) = globals.namespaces.find_id(&opens[1].namespace); Err(Error::Ambiguous { name: name_str.to_string(), - first_open: opens[0].namespace.join("."), - second_open: opens[1].namespace.join("."), + first_open: first_open_ns.join("."), + second_open: second_open_ns.join("."), name_span: name.span, first_open_span: opens[0].span, second_open_span: opens[1].span, @@ -1213,7 +1213,7 @@ fn resolve_scope_locals( } if let ScopeKind::Namespace(namespace) = &scope.kind { - if let Some(&res) = globals.get(kind, namespace, name) { + if let Some(&res) = globals.get(kind, *namespace, name) { return Some(res); } } @@ -1256,9 +1256,9 @@ fn get_scope_locals(scope: &Scope, offset: u32, vars: bool) -> Vec { names } -/// The return type represents the resolution of implicit opens, but also -/// retains the namespace that the resolution comes from. -/// This retained namespace string is used for error reporting. +/// The return type represents the resolution of all implicit opens. +/// The namespace is returned along with the res, so that the namespace can be used to +/// report the ambiguity to the user. fn resolve_implicit_opens<'a, 'b>( kind: NameKind, globals: &'b GlobalScope, @@ -1267,6 +1267,7 @@ fn resolve_implicit_opens<'a, 'b>( ) -> FxHashMap { let mut candidates = FxHashMap::default(); for namespace in namespaces { + let namespace_id = globals.find_namespace(&[Rc::from(*namespace)]); if let Some(&res) = globals.get(kind, todo!("this should become namespace id"), name) { candidates.insert(res, *namespace); } @@ -1282,7 +1283,7 @@ fn resolve_explicit_opens<'a>( ) -> FxHashMap { let mut candidates = FxHashMap::default(); for open in opens { - if let Some(&res) = globals.get(kind, &open.namespace, name) { + if let Some(&res) = globals.get(kind, open.namespace, name) { candidates.insert(res, open); } } diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index d5a8baf85d..3dae96cd11 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -70,7 +70,7 @@ impl FromIterator for Table { let mut terms: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); let mut namespaces = NamespaceTreeRoot::default(); for global in iter { - let namespace = namespaces.insert_or_find_namespace(global.namespace); + let namespace = namespaces.insert_or_find_namespace(global.namespace.into_iter()); match global.kind { Kind::Ty(ty) => { tys.entry(namespace).or_default().insert(global.name, ty); From 01bcddc2c51d91e05b898e127832e14573b4ede7 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 27 Mar 2024 11:11:51 -0700 Subject: [PATCH 32/76] namespace root traversals are working --- compiler/qsc_data_structures/src/lib.rs | 404 ++++++++++++++++++++++-- 1 file changed, 384 insertions(+), 20 deletions(-) diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index ebbb6226f2..5a1847ac29 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -94,24 +94,7 @@ pub mod namespaces { } pub fn find_id(&self, id: &NamespaceId) -> (Vec>, Rc<&NamespaceTreeNode>) { - // traverse the nodes to find the node with the correct id - let mut buf = Rc::new(&self.tree); - let mut components = Vec::new(); - while buf.id != *id { - let mut found = false; - for (component, node) in buf.children.iter() { - if node.id == *id { - components.push(Rc::clone(component)); - buf = Rc::new(node); - found = true; - break; - } - } - if !found { - panic!("Namespace ID {id} not found in tree") - } - } - return (components, buf); + return self.tree.find_id(*id, vec![]); } pub fn root_id(&self) -> NamespaceId { @@ -409,9 +392,374 @@ pub mod namespaces { id_buf.push(root.insert_or_find_namespace(vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter())); } } + let mut result_buf = vec![]; for id in id_buf { - root.find_id(&id); - } + result_buf.push(root.find_id(&id)); + } + expect![[r#" + [ + ( + [ + "ns0", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 2, + ), + }, + ), + ( + [ + "ns0", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 3, + ), + }, + ), + ( + [ + "ns0", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 4, + ), + }, + ), + ( + [ + "ns1", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 6, + ), + }, + ), + ( + [ + "ns1", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 7, + ), + }, + ), + ( + [ + "ns1", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 8, + ), + }, + ), + ( + [ + "ns2", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 10, + ), + }, + ), + ( + [ + "ns2", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 11, + ), + }, + ), + ( + [ + "ns2", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 12, + ), + }, + ), + ( + [ + "ns3", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 14, + ), + }, + ), + ( + [ + "ns3", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 15, + ), + }, + ), + ( + [ + "ns3", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 16, + ), + }, + ), + ( + [ + "ns4", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 18, + ), + }, + ), + ( + [ + "ns4", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 19, + ), + }, + ), + ( + [ + "ns4", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 20, + ), + }, + ), + ( + [ + "ns5", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 22, + ), + }, + ), + ( + [ + "ns5", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 23, + ), + }, + ), + ( + [ + "ns5", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 24, + ), + }, + ), + ( + [ + "ns6", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 26, + ), + }, + ), + ( + [ + "ns6", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 27, + ), + }, + ), + ( + [ + "ns6", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 28, + ), + }, + ), + ( + [ + "ns7", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 30, + ), + }, + ), + ( + [ + "ns7", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 31, + ), + }, + ), + ( + [ + "ns7", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 32, + ), + }, + ), + ( + [ + "ns8", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 34, + ), + }, + ), + ( + [ + "ns8", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 35, + ), + }, + ), + ( + [ + "ns8", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 36, + ), + }, + ), + ( + [ + "ns9", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 38, + ), + }, + ), + ( + [ + "ns9", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 39, + ), + }, + ), + ( + [ + "ns9", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 40, + ), + }, + ), + ] + "#] ].assert_debug_eq(&result_buf) } // test that after inserting lots of namespaces, all ids are unique and sequential #[test] @@ -546,5 +894,21 @@ pub mod namespaces { } } } + + fn find_id(&self, id: NamespaceId, names_buf: Vec>) -> (Vec>, Rc<&NamespaceTreeNode>) { + if self.id == id { + return (names_buf, Rc::new(self)); + } else { + for (name, node) in self.children.iter() { + let mut new_buf = names_buf.clone(); + new_buf.push(name.clone()); + let (names, node) = node.find_id(id, new_buf); + if names.len() > 0 { + return (names, node); + } + } + return (vec![], Rc::new(self)); + } + } } } From 105e625431f911890123da0cd64db5fc52d64dc1 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 27 Mar 2024 11:11:59 -0700 Subject: [PATCH 33/76] fmt --- compiler/qsc_data_structures/src/lib.rs | 45 ++++++++++++++++++------- compiler/qsc_eval/src/lower.rs | 5 ++- compiler/qsc_fir/src/global.rs | 7 ++-- compiler/qsc_fir/src/mut_visit.rs | 5 +-- compiler/qsc_fir/src/visit.rs | 4 +-- compiler/qsc_frontend/src/resolve.rs | 8 +++-- language_service/src/compilation.rs | 30 ++++++++++------- language_service/src/completion.rs | 26 +++++++------- 8 files changed, 84 insertions(+), 46 deletions(-) diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index 5a1847ac29..13859a1756 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -8,7 +8,9 @@ pub mod language_features; pub mod line_column; pub mod span; pub mod namespaces { - use std::{cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; + use std::{ + cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc, + }; #[derive(Debug, Clone)] @@ -96,7 +98,7 @@ pub mod namespaces { pub fn find_id(&self, id: &NamespaceId) -> (Vec>, Rc<&NamespaceTreeNode>) { return self.tree.find_id(*id, vec![]); } - + pub fn root_id(&self) -> NamespaceId { self.tree.id } @@ -115,9 +117,12 @@ pub mod namespaces { let mut root = NamespaceTreeRoot::default(); for i in 0..10 { for j in 'a'..'d' { - root.insert_or_find_namespace(vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter()); + root.insert_or_find_namespace( + vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] + .into_iter(), + ); } - } + } expect![[r#" NamespaceTreeRoot { assigner: 40, @@ -382,14 +387,19 @@ pub mod namespaces { "#]] .assert_debug_eq(&root); } - + #[test] fn test_find_id() { let mut root = NamespaceTreeRoot::default(); let mut id_buf = vec![]; for i in 0..10 { for j in 'a'..'d' { - id_buf.push(root.insert_or_find_namespace(vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter())); + id_buf.push( + root.insert_or_find_namespace( + vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] + .into_iter(), + ), + ); } } let mut result_buf = vec![]; @@ -759,7 +769,8 @@ pub mod namespaces { }, ), ] - "#] ].assert_debug_eq(&result_buf) + "#]] + .assert_debug_eq(&result_buf) } // test that after inserting lots of namespaces, all ids are unique and sequential #[test] @@ -768,7 +779,10 @@ pub mod namespaces { let mut ids: Vec = vec![]; for i in 0..10 { for j in 'a'..'d' { - let id = root.insert_or_find_namespace(vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter()); + let id = root.insert_or_find_namespace( + vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] + .into_iter(), + ); ids.push(id.into()); } } @@ -866,9 +880,12 @@ pub mod namespaces { /// Returns the ID of the namespace. pub fn insert_or_find_namespace( &mut self, - mut iter: Peekable, + mut iter: Peekable, assigner: &mut usize, - ) -> Option where I: Iterator> { + ) -> Option + where + I: Iterator>, + { let next_item = match iter.next() { Some(item) => item, None => return None, @@ -894,8 +911,12 @@ pub mod namespaces { } } } - - fn find_id(&self, id: NamespaceId, names_buf: Vec>) -> (Vec>, Rc<&NamespaceTreeNode>) { + + fn find_id( + &self, + id: NamespaceId, + names_buf: Vec>, + ) -> (Vec>, Rc<&NamespaceTreeNode>) { if self.id == id { return (names_buf, Rc::new(self)); } else { diff --git a/compiler/qsc_eval/src/lower.rs b/compiler/qsc_eval/src/lower.rs index 8abb4af009..f9ab30cb80 100644 --- a/compiler/qsc_eval/src/lower.rs +++ b/compiler/qsc_eval/src/lower.rs @@ -753,7 +753,10 @@ impl Lowerer { } fn lower_vec_ident(&mut self, name: &hir::VecIdent) -> fir::VecIdent { - name.iter().cloned().map(|ident| self.lower_ident(&ident)).collect() + name.iter() + .cloned() + .map(|ident| self.lower_ident(&ident)) + .collect() } } diff --git a/compiler/qsc_fir/src/global.rs b/compiler/qsc_fir/src/global.rs index 8f25af3a72..f0406c081b 100644 --- a/compiler/qsc_fir/src/global.rs +++ b/compiler/qsc_fir/src/global.rs @@ -5,7 +5,10 @@ use crate::{ fir::{Item, ItemId, ItemKind, Package, PackageId, Visibility}, ty::Scheme, }; -use qsc_data_structures::{index_map, namespaces::{NamespaceId, NamespaceTreeRoot}}; +use qsc_data_structures::{ + index_map, + namespaces::{NamespaceId, NamespaceTreeRoot}, +}; use rustc_hash::FxHashMap; use std::rc::Rc; @@ -55,7 +58,6 @@ impl Table { } } - impl FromIterator for Table { fn from_iter>(iter: T) -> Self { let mut tys: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default(); @@ -82,7 +84,6 @@ impl FromIterator for Table { tys, terms, } - } } diff --git a/compiler/qsc_fir/src/mut_visit.rs b/compiler/qsc_fir/src/mut_visit.rs index e7e1b5578c..97532eb9ec 100644 --- a/compiler/qsc_fir/src/mut_visit.rs +++ b/compiler/qsc_fir/src/mut_visit.rs @@ -3,7 +3,8 @@ use crate::fir::{ Block, BlockId, CallableDecl, CallableImpl, Expr, ExprId, ExprKind, Ident, Item, ItemKind, - Package, Pat, PatId, PatKind, SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StringComponent, VecIdent, + Package, Pat, PatId, PatKind, SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StringComponent, + VecIdent, }; pub trait MutVisitor<'a>: Sized { @@ -66,7 +67,7 @@ pub fn walk_item<'a>(vis: &mut impl MutVisitor<'a>, item: &'a mut Item) { match &mut item.kind { ItemKind::Callable(decl) => vis.visit_callable_decl(decl), ItemKind::Namespace(name, _) => vis.visit_vec_ident(name), - ItemKind::Ty(name, _) => vis.visit_ident(name), + ItemKind::Ty(name, _) => vis.visit_ident(name), }; } diff --git a/compiler/qsc_fir/src/visit.rs b/compiler/qsc_fir/src/visit.rs index dc16a1f258..81de9c0ffd 100644 --- a/compiler/qsc_fir/src/visit.rs +++ b/compiler/qsc_fir/src/visit.rs @@ -3,7 +3,8 @@ use crate::fir::{ Block, BlockId, CallableDecl, CallableImpl, Expr, ExprId, ExprKind, Ident, Item, ItemKind, - Package, Pat, PatId, PatKind, SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StringComponent, VecIdent, + Package, Pat, PatId, PatKind, SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StringComponent, + VecIdent, }; pub trait Visitor<'a>: Sized { @@ -67,7 +68,6 @@ pub fn walk_item<'a>(vis: &mut impl Visitor<'a>, item: &'a Item) { ItemKind::Callable(decl) => vis.visit_callable_decl(decl), ItemKind::Namespace(name, _) => vis.visit_vec_ident(name), ItemKind::Ty(name, _) => vis.visit_ident(name), - }; } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 100f5474ef..5d1ac9299b 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -642,7 +642,11 @@ impl With<'_> { impl AstVisitor<'_> for With<'_> { fn visit_namespace(&mut self, namespace: &ast::Namespace) { - let ns = self.resolver.globals.find_namespace(Into::>::into(namespace.name.clone())).expect("namespace should exist"); + let ns = self + .resolver + .globals + .find_namespace(Into::>::into(namespace.name.clone())) + .expect("namespace should exist"); let kind = ScopeKind::Namespace(ns); self.with_scope(namespace.span, kind, |visitor| { for item in &*namespace.items { @@ -1257,7 +1261,7 @@ fn get_scope_locals(scope: &Scope, offset: u32, vars: bool) -> Vec { } /// The return type represents the resolution of all implicit opens. -/// The namespace is returned along with the res, so that the namespace can be used to +/// The namespace is returned along with the res, so that the namespace can be used to /// report the ambiguity to the user. fn resolve_implicit_opens<'a, 'b>( kind: NameKind, diff --git a/language_service/src/compilation.rs b/language_service/src/compilation.rs index 8ef7fa96e9..f040b1917a 100644 --- a/language_service/src/compilation.rs +++ b/language_service/src/compilation.rs @@ -3,7 +3,16 @@ use log::trace; use qsc::{ - ast, compile::{self, Error}, display::Lookup, error::WithSource, hir::{self, PackageId}, incremental::Compiler, line_column::{Encoding, Position}, resolve, target::Profile, CompileUnit, LanguageFeatures, NamespaceId, PackageStore, PackageType, SourceMap, Span + ast, + compile::{self, Error}, + display::Lookup, + error::WithSource, + hir::{self, PackageId}, + incremental::Compiler, + line_column::{Encoding, Position}, + resolve, + target::Profile, + CompileUnit, LanguageFeatures, NamespaceId, PackageStore, PackageType, SourceMap, Span, }; use qsc_linter::LintConfig; use std::{rc::Rc, sync::Arc}; @@ -209,18 +218,15 @@ impl Compilation { self.user_package_id = new.user_package_id; self.errors = new.errors; } - - pub(crate) fn find_namespace_id(&self, ns: [&str; 3]) -> NamespaceId { - self - .package_store - .get(self.user_package_id) - .expect("user package should exist") - .ast - .namespaces - .find_namespace(ns.into_iter().map(|s| Rc::from(s)).collect::>()) - .expect("namespace should exist") - + pub(crate) fn find_namespace_id(&self, ns: [&str; 3]) -> NamespaceId { + self.package_store + .get(self.user_package_id) + .expect("user package should exist") + .ast + .namespaces + .find_namespace(ns.into_iter().map(|s| Rc::from(s)).collect::>()) + .expect("namespace should exist") } } diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index 0746757266..bb2c8f78ac 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -11,19 +11,19 @@ use qsc::ast::ast; use qsc::ast::visit::{self, Visitor}; use qsc::display::{CodeDisplay, Lookup}; +use core::prelude; use qsc::hir::{ItemKind, Package, PackageId, Visibility}; use qsc::line_column::{Encoding, Position, Range}; use qsc::resolve::{Local, LocalKind}; use qsc::{NamespaceId, NamespaceTreeNode}; use rustc_hash::FxHashSet; -use core::prelude; use std::collections::HashMap; use std::rc::Rc; const PRELUDE: [[&str; 3]; 3] = [ - ["Microsoft", "Quantum","Canon"], - ["Microsoft", "Quantum","Core"], - ["Microsoft", "Quantum","Intrinsic"] + ["Microsoft", "Quantum", "Canon"], + ["Microsoft", "Quantum", "Core"], + ["Microsoft", "Quantum", "Intrinsic"], ]; pub(crate) fn get_completions( @@ -73,7 +73,10 @@ pub(crate) fn get_completions( None => String::new(), }; - let mut prelude_ns_ids: Vec<_> = PRELUDE.into_iter().map(|ns| (ns.into_iter().map(Rc::from).collect(), None)).collect(); + let mut prelude_ns_ids: Vec<_> = PRELUDE + .into_iter() + .map(|ns| (ns.into_iter().map(Rc::from).collect(), None)) + .collect(); // The PRELUDE namespaces are always implicitly opened. context_finder.opens.append(&mut prelude_ns_ids); @@ -394,7 +397,7 @@ impl CompletionListBuilder { opens: &'a [(Vec>, Option>)], // The range at which to insert an open statement if one is needed insert_open_at: Option, - // The name of the current namespace, if any -- + // The name of the current namespace, if any -- current_namespace_name: Option>>, indent: &'a String, ) -> impl Iterator + 'a { @@ -449,7 +452,8 @@ impl CompletionListBuilder { let mut additional_edits = vec![]; let mut qualification: Option>> = None; match ¤t_namespace_name { - Some(curr_ns) if *curr_ns == Into::>::into(namespace) => {} + Some(curr_ns) + if *curr_ns == Into::>::into(namespace) => {} _ => { // open is an option of option of Rc // the first option tells if it found an open with the namespace name @@ -468,8 +472,7 @@ impl CompletionListBuilder { additional_edits.push(TextEdit { new_text: format!( "open {};{}", - namespace, - indent, + namespace, indent, ), range: start, }); @@ -669,9 +672,8 @@ impl Visitor<'_> for ContextFinder { } if let qsc::ast::ItemKind::Open(name, alias) = &*item.kind { - self.opens.push(( - name.into(), alias.as_ref().map(|alias| alias.name.clone()) - )); + self.opens + .push((name.into(), alias.as_ref().map(|alias| alias.name.clone()))); } if span_contains(item.span, self.offset) { From f62b9776b9d010c07f647ba781e8ed30bf417735 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 27 Mar 2024 11:12:39 -0700 Subject: [PATCH 34/76] refactor namespaces into their own files --- .../qsc_data_structures/src/namespaces.rs | 205 ++++++++++++++++++ .../src/namespaces/tests.rs | 0 2 files changed, 205 insertions(+) create mode 100644 compiler/qsc_data_structures/src/namespaces.rs create mode 100644 compiler/qsc_data_structures/src/namespaces/tests.rs diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs new file mode 100644 index 0000000000..3a7d53f3ec --- /dev/null +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -0,0 +1,205 @@ +#[cfg(test)] +mod tests; + +use std::{ + cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc, +}; + + +#[derive(Debug, Clone)] + +pub struct NamespaceTreeRoot { + assigner: usize, + tree: NamespaceTreeNode, +} + +/// An ID that corresponds to a namespace in the global scope. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] +pub struct NamespaceId(usize); +impl NamespaceId { + /// Create a new namespace ID. + pub fn new(value: usize) -> Self { + Self(value) + } +} + +impl From for NamespaceId { + fn from(value: usize) -> Self { + Self::new(value) + } +} + +impl From for usize { + fn from(value: NamespaceId) -> Self { + value.0 + } +} + +impl From<&NamespaceId> for usize { + fn from(value: &NamespaceId) -> Self { + value.0 + } +} + +impl Deref for NamespaceId { + type Target = usize; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for NamespaceId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Namespace {}", self.0) + } +} + +impl NamespaceTreeRoot { + /// Create a new namespace tree root. The assigner is used to assign new namespace IDs. + pub fn new(assigner: usize, tree: NamespaceTreeNode) -> Self { + Self { assigner, tree } + } + /// Get the namespace tree field. This is the root of the namespace tree. + pub fn tree(&self) -> &NamespaceTreeNode { + &self.tree + } + + /// Insert a namespace into the tree. If the namespace already exists, return its ID. + pub fn insert_or_find_namespace( + &mut self, + ns: impl IntoIterator>, + ) -> NamespaceId { + self.tree + .insert_or_find_namespace(ns.into_iter().peekable(), &mut self.assigner) + .expect("namespace creation should not fail") + } + + pub fn new_namespace_node( + &mut self, + children: HashMap, NamespaceTreeNode>, + ) -> NamespaceTreeNode { + self.assigner += 1; + NamespaceTreeNode { + id: NamespaceId::new(self.assigner), + children, + } + } + + pub fn find_namespace(&self, ns: impl Into>>) -> Option { + self.tree.find_namespace(ns) + } + + pub fn find_id(&self, id: &NamespaceId) -> (Vec>, Rc<&NamespaceTreeNode>) { + return self.tree.find_id(*id, vec![]); + } + + pub fn root_id(&self) -> NamespaceId { + self.tree.id + } +} + + +impl Default for NamespaceTreeRoot { + fn default() -> Self { + Self { + assigner: 0, + tree: NamespaceTreeNode { + children: HashMap::new(), + id: NamespaceId::new(0), + }, + } + } +} + +#[derive(Debug, Clone)] +pub struct NamespaceTreeNode { + pub children: HashMap, NamespaceTreeNode>, + pub id: NamespaceId, +} +impl NamespaceTreeNode { + pub fn new(id: NamespaceId, children: HashMap, NamespaceTreeNode>) -> Self { + Self { children, id } + } + pub fn children(&self) -> &HashMap, NamespaceTreeNode> { + &self.children + } + fn get(&self, component: &Rc) -> Option<&NamespaceTreeNode> { + self.children.get(component) + } + pub fn id(&self) -> NamespaceId { + self.id + } + fn contains(&self, ns: impl Into>>) -> bool { + self.find_namespace(ns).is_some() + } + fn find_namespace(&self, ns: impl Into>>) -> Option { + // look up a namespace in the tree and return the id + // do a breadth-first search through NamespaceTree for the namespace + // if it's not found, return None + let mut buf = Rc::new(self); + for component in ns.into().iter() { + if let Some(next_ns) = buf.get(component) { + buf = Rc::new(next_ns); + } else { + return None; + } + } + return Some(buf.id); + } + + /// If the namespace already exists, it will not be inserted. + /// Returns the ID of the namespace. + pub fn insert_or_find_namespace( + &mut self, + mut iter: Peekable, + assigner: &mut usize, + ) -> Option + where + I: Iterator>, + { + let next_item = match iter.next() { + Some(item) => item, + None => return None, + }; + println!("Inserting namespace {}", next_item); + + let next_node = self.children.get_mut(&next_item); + if let Some(mut next_node) = next_node { + return next_node.insert_or_find_namespace(iter, assigner); + } else { + println!("creating new node"); + *assigner += 1; + let mut new_node = + NamespaceTreeNode::new(NamespaceId::new(*assigner), HashMap::new()); + if iter.peek().is_none() { + let new_node_id = new_node.id; + self.children.insert(next_item, new_node); + return Some(new_node_id); + } else { + let id = new_node.insert_or_find_namespace(iter, assigner); + self.children.insert(next_item, new_node); + return id; + } + } + } + + fn find_id( + &self, + id: NamespaceId, + names_buf: Vec>, + ) -> (Vec>, Rc<&NamespaceTreeNode>) { + if self.id == id { + return (names_buf, Rc::new(self)); + } else { + for (name, node) in self.children.iter() { + let mut new_buf = names_buf.clone(); + new_buf.push(name.clone()); + let (names, node) = node.find_id(id, new_buf); + if names.len() > 0 { + return (names, node); + } + } + return (vec![], Rc::new(self)); + } + } +} diff --git a/compiler/qsc_data_structures/src/namespaces/tests.rs b/compiler/qsc_data_structures/src/namespaces/tests.rs new file mode 100644 index 0000000000..e69de29bb2 From 8a11e1de74f8c4eeafe3f5075e2c7735b6fb7ac6 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 27 Mar 2024 11:28:59 -0700 Subject: [PATCH 35/76] add forgotten test file --- .../src/namespaces/tests.rs | 720 ++++++++++++++++++ 1 file changed, 720 insertions(+) diff --git a/compiler/qsc_data_structures/src/namespaces/tests.rs b/compiler/qsc_data_structures/src/namespaces/tests.rs index e69de29bb2..325b9eaf7c 100644 --- a/compiler/qsc_data_structures/src/namespaces/tests.rs +++ b/compiler/qsc_data_structures/src/namespaces/tests.rs @@ -0,0 +1,720 @@ +use expect_test::expect; + +use super::*; +use std::collections::HashMap; + +#[test] +fn test_tree_construction() { + let mut root = NamespaceTreeRoot::default(); + for i in 0..10 { + for j in 'a'..'d' { + root.insert_or_find_namespace( + vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] + .into_iter(), + ); + } + } + expect![[r#" + NamespaceTreeRoot { + assigner: 40, + tree: NamespaceTreeNode { + children: { + "ns6": NamespaceTreeNode { + children: { + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 27, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 28, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 26, + ), + }, + }, + id: NamespaceId( + 25, + ), + }, + "ns3": NamespaceTreeNode { + children: { + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 14, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 16, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 15, + ), + }, + }, + id: NamespaceId( + 13, + ), + }, + "ns7": NamespaceTreeNode { + children: { + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 32, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 31, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 30, + ), + }, + }, + id: NamespaceId( + 29, + ), + }, + "ns8": NamespaceTreeNode { + children: { + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 35, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 36, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 34, + ), + }, + }, + id: NamespaceId( + 33, + ), + }, + "ns0": NamespaceTreeNode { + children: { + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 3, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 2, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 4, + ), + }, + }, + id: NamespaceId( + 1, + ), + }, + "ns4": NamespaceTreeNode { + children: { + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 20, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 18, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 19, + ), + }, + }, + id: NamespaceId( + 17, + ), + }, + "ns2": NamespaceTreeNode { + children: { + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 10, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 11, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 12, + ), + }, + }, + id: NamespaceId( + 9, + ), + }, + "ns5": NamespaceTreeNode { + children: { + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 22, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 23, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 24, + ), + }, + }, + id: NamespaceId( + 21, + ), + }, + "ns1": NamespaceTreeNode { + children: { + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 8, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 6, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 7, + ), + }, + }, + id: NamespaceId( + 5, + ), + }, + "ns9": NamespaceTreeNode { + children: { + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 38, + ), + }, + "nsb": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 39, + ), + }, + "nsc": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 40, + ), + }, + }, + id: NamespaceId( + 37, + ), + }, + }, + id: NamespaceId( + 0, + ), + }, + } + "#]] + .assert_debug_eq(&root); +} + +#[test] +fn test_find_id() { + let mut root = NamespaceTreeRoot::default(); + let mut id_buf = vec![]; + for i in 0..10 { + for j in 'a'..'d' { + id_buf.push( + root.insert_or_find_namespace( + vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] + .into_iter(), + ), + ); + } + } + let mut result_buf = vec![]; + for id in id_buf { + result_buf.push(root.find_id(&id)); + } + expect![[r#" + [ + ( + [ + "ns0", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 2, + ), + }, + ), + ( + [ + "ns0", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 3, + ), + }, + ), + ( + [ + "ns0", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 4, + ), + }, + ), + ( + [ + "ns1", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 6, + ), + }, + ), + ( + [ + "ns1", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 7, + ), + }, + ), + ( + [ + "ns1", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 8, + ), + }, + ), + ( + [ + "ns2", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 10, + ), + }, + ), + ( + [ + "ns2", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 11, + ), + }, + ), + ( + [ + "ns2", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 12, + ), + }, + ), + ( + [ + "ns3", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 14, + ), + }, + ), + ( + [ + "ns3", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 15, + ), + }, + ), + ( + [ + "ns3", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 16, + ), + }, + ), + ( + [ + "ns4", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 18, + ), + }, + ), + ( + [ + "ns4", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 19, + ), + }, + ), + ( + [ + "ns4", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 20, + ), + }, + ), + ( + [ + "ns5", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 22, + ), + }, + ), + ( + [ + "ns5", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 23, + ), + }, + ), + ( + [ + "ns5", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 24, + ), + }, + ), + ( + [ + "ns6", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 26, + ), + }, + ), + ( + [ + "ns6", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 27, + ), + }, + ), + ( + [ + "ns6", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 28, + ), + }, + ), + ( + [ + "ns7", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 30, + ), + }, + ), + ( + [ + "ns7", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 31, + ), + }, + ), + ( + [ + "ns7", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 32, + ), + }, + ), + ( + [ + "ns8", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 34, + ), + }, + ), + ( + [ + "ns8", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 35, + ), + }, + ), + ( + [ + "ns8", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 36, + ), + }, + ), + ( + [ + "ns9", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 38, + ), + }, + ), + ( + [ + "ns9", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 39, + ), + }, + ), + ( + [ + "ns9", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 40, + ), + }, + ), + ] + "#]] + .assert_debug_eq(&result_buf) +} +// test that after inserting lots of namespaces, all ids are unique and sequential +#[test] +fn test_insert_or_find_namespace() { + let mut root = NamespaceTreeRoot::default(); + let mut ids: Vec = vec![]; + for i in 0..10 { + for j in 'a'..'d' { + let id = root.insert_or_find_namespace( + vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] + .into_iter(), + ); + ids.push(id.into()); + } + } + let mut ids_sorted = ids.clone(); + ids_sorted.sort(); + ids_sorted.dedup(); + // there should be no duplicate or out-of-order ids + assert_eq!(ids_sorted, ids); + expect![[r#" + [ + 2, + 3, + 4, + 6, + 7, + 8, + 10, + 11, + 12, + 14, + 15, + 16, + 18, + 19, + 20, + 22, + 23, + 24, + 26, + 27, + 28, + 30, + 31, + 32, + 34, + 35, + 36, + 38, + 39, + 40, + ] + "#]] + .assert_debug_eq(&ids); +} From a41952e77542eb779e876c013cbcc184773793d6 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 27 Mar 2024 13:41:45 -0700 Subject: [PATCH 36/76] parsing tests pass --- compiler/qsc_ast/src/ast.rs | 20 +- compiler/qsc_data_structures/src/lib.rs | 927 +----------------------- compiler/qsc_fir/src/fir.rs | 21 +- compiler/qsc_hir/src/hir.rs | 20 +- compiler/qsc_parse/src/item/tests.rs | 30 +- compiler/qsc_parse/src/prim/tests.rs | 2 +- 6 files changed, 53 insertions(+), 967 deletions(-) diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index 7f4207bd14..eecb31ed20 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -1336,14 +1336,18 @@ impl From for Vec { impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let idents = self.0.iter(); - write!( - f, - "{}", - idents - .map(|i| i.name.to_string()) - .collect::>() - .join(".") - ) + let mut buf = Vec::with_capacity(self.0.len()); + + for ident in self.0.iter() { + buf.push(format!("{}", ident)); + } + if buf.len() > 1 { + // use square brackets only if there are more than one ident + write!(f, "[{}]", buf.join(", ")) + } else { + write!(f, "{}", buf[0]) + + } } } impl VecIdent { diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index 13859a1756..8e980c052b 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -7,929 +7,4 @@ pub mod index_map; pub mod language_features; pub mod line_column; pub mod span; -pub mod namespaces { - use std::{ - cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc, - }; - - #[derive(Debug, Clone)] - - pub struct NamespaceTreeRoot { - assigner: usize, - tree: NamespaceTreeNode, - } - - /// An ID that corresponds to a namespace in the global scope. - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] - pub struct NamespaceId(usize); - impl NamespaceId { - /// Create a new namespace ID. - pub fn new(value: usize) -> Self { - Self(value) - } - } - - impl From for NamespaceId { - fn from(value: usize) -> Self { - Self::new(value) - } - } - - impl From for usize { - fn from(value: NamespaceId) -> Self { - value.0 - } - } - - impl From<&NamespaceId> for usize { - fn from(value: &NamespaceId) -> Self { - value.0 - } - } - - impl Deref for NamespaceId { - type Target = usize; - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl Display for NamespaceId { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Namespace {}", self.0) - } - } - - impl NamespaceTreeRoot { - /// Create a new namespace tree root. The assigner is used to assign new namespace IDs. - pub fn new(assigner: usize, tree: NamespaceTreeNode) -> Self { - Self { assigner, tree } - } - /// Get the namespace tree field. This is the root of the namespace tree. - pub fn tree(&self) -> &NamespaceTreeNode { - &self.tree - } - - /// Insert a namespace into the tree. If the namespace already exists, return its ID. - pub fn insert_or_find_namespace( - &mut self, - ns: impl IntoIterator>, - ) -> NamespaceId { - self.tree - .insert_or_find_namespace(ns.into_iter().peekable(), &mut self.assigner) - .expect("namespace creation should not fail") - } - - pub fn new_namespace_node( - &mut self, - children: HashMap, NamespaceTreeNode>, - ) -> NamespaceTreeNode { - self.assigner += 1; - NamespaceTreeNode { - id: NamespaceId::new(self.assigner), - children, - } - } - - pub fn find_namespace(&self, ns: impl Into>>) -> Option { - self.tree.find_namespace(ns) - } - - pub fn find_id(&self, id: &NamespaceId) -> (Vec>, Rc<&NamespaceTreeNode>) { - return self.tree.find_id(*id, vec![]); - } - - pub fn root_id(&self) -> NamespaceId { - self.tree.id - } - } - - // write some unit tests for the above `find_id` method - #[cfg(test)] - mod tests { - use expect_test::expect; - - use super::*; - use std::collections::HashMap; - - #[test] - fn test_tree_construction() { - let mut root = NamespaceTreeRoot::default(); - for i in 0..10 { - for j in 'a'..'d' { - root.insert_or_find_namespace( - vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] - .into_iter(), - ); - } - } - expect![[r#" - NamespaceTreeRoot { - assigner: 40, - tree: NamespaceTreeNode { - children: { - "ns6": NamespaceTreeNode { - children: { - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 27, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 28, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 26, - ), - }, - }, - id: NamespaceId( - 25, - ), - }, - "ns3": NamespaceTreeNode { - children: { - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 14, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 16, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 15, - ), - }, - }, - id: NamespaceId( - 13, - ), - }, - "ns7": NamespaceTreeNode { - children: { - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 32, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 31, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 30, - ), - }, - }, - id: NamespaceId( - 29, - ), - }, - "ns8": NamespaceTreeNode { - children: { - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 35, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 36, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 34, - ), - }, - }, - id: NamespaceId( - 33, - ), - }, - "ns0": NamespaceTreeNode { - children: { - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 3, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 2, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 4, - ), - }, - }, - id: NamespaceId( - 1, - ), - }, - "ns4": NamespaceTreeNode { - children: { - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 20, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 18, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 19, - ), - }, - }, - id: NamespaceId( - 17, - ), - }, - "ns2": NamespaceTreeNode { - children: { - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 10, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 11, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 12, - ), - }, - }, - id: NamespaceId( - 9, - ), - }, - "ns5": NamespaceTreeNode { - children: { - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 22, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 23, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 24, - ), - }, - }, - id: NamespaceId( - 21, - ), - }, - "ns1": NamespaceTreeNode { - children: { - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 8, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 6, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 7, - ), - }, - }, - id: NamespaceId( - 5, - ), - }, - "ns9": NamespaceTreeNode { - children: { - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 38, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 39, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 40, - ), - }, - }, - id: NamespaceId( - 37, - ), - }, - }, - id: NamespaceId( - 0, - ), - }, - } - "#]] - .assert_debug_eq(&root); - } - - #[test] - fn test_find_id() { - let mut root = NamespaceTreeRoot::default(); - let mut id_buf = vec![]; - for i in 0..10 { - for j in 'a'..'d' { - id_buf.push( - root.insert_or_find_namespace( - vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] - .into_iter(), - ), - ); - } - } - let mut result_buf = vec![]; - for id in id_buf { - result_buf.push(root.find_id(&id)); - } - expect![[r#" - [ - ( - [ - "ns0", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 2, - ), - }, - ), - ( - [ - "ns0", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 3, - ), - }, - ), - ( - [ - "ns0", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 4, - ), - }, - ), - ( - [ - "ns1", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 6, - ), - }, - ), - ( - [ - "ns1", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 7, - ), - }, - ), - ( - [ - "ns1", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 8, - ), - }, - ), - ( - [ - "ns2", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 10, - ), - }, - ), - ( - [ - "ns2", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 11, - ), - }, - ), - ( - [ - "ns2", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 12, - ), - }, - ), - ( - [ - "ns3", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 14, - ), - }, - ), - ( - [ - "ns3", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 15, - ), - }, - ), - ( - [ - "ns3", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 16, - ), - }, - ), - ( - [ - "ns4", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 18, - ), - }, - ), - ( - [ - "ns4", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 19, - ), - }, - ), - ( - [ - "ns4", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 20, - ), - }, - ), - ( - [ - "ns5", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 22, - ), - }, - ), - ( - [ - "ns5", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 23, - ), - }, - ), - ( - [ - "ns5", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 24, - ), - }, - ), - ( - [ - "ns6", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 26, - ), - }, - ), - ( - [ - "ns6", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 27, - ), - }, - ), - ( - [ - "ns6", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 28, - ), - }, - ), - ( - [ - "ns7", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 30, - ), - }, - ), - ( - [ - "ns7", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 31, - ), - }, - ), - ( - [ - "ns7", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 32, - ), - }, - ), - ( - [ - "ns8", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 34, - ), - }, - ), - ( - [ - "ns8", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 35, - ), - }, - ), - ( - [ - "ns8", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 36, - ), - }, - ), - ( - [ - "ns9", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 38, - ), - }, - ), - ( - [ - "ns9", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 39, - ), - }, - ), - ( - [ - "ns9", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 40, - ), - }, - ), - ] - "#]] - .assert_debug_eq(&result_buf) - } - // test that after inserting lots of namespaces, all ids are unique and sequential - #[test] - fn test_insert_or_find_namespace() { - let mut root = NamespaceTreeRoot::default(); - let mut ids: Vec = vec![]; - for i in 0..10 { - for j in 'a'..'d' { - let id = root.insert_or_find_namespace( - vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] - .into_iter(), - ); - ids.push(id.into()); - } - } - let mut ids_sorted = ids.clone(); - ids_sorted.sort(); - ids_sorted.dedup(); - // there should be no duplicate or out-of-order ids - assert_eq!(ids_sorted, ids); - expect![[r#" - [ - 2, - 3, - 4, - 6, - 7, - 8, - 10, - 11, - 12, - 14, - 15, - 16, - 18, - 19, - 20, - 22, - 23, - 24, - 26, - 27, - 28, - 30, - 31, - 32, - 34, - 35, - 36, - 38, - 39, - 40, - ] - "#]] - .assert_debug_eq(&ids); - } - } - impl Default for NamespaceTreeRoot { - fn default() -> Self { - Self { - assigner: 0, - tree: NamespaceTreeNode { - children: HashMap::new(), - id: NamespaceId::new(0), - }, - } - } - } - - #[derive(Debug, Clone)] - pub struct NamespaceTreeNode { - pub children: HashMap, NamespaceTreeNode>, - pub id: NamespaceId, - } - impl NamespaceTreeNode { - pub fn new(id: NamespaceId, children: HashMap, NamespaceTreeNode>) -> Self { - Self { children, id } - } - pub fn children(&self) -> &HashMap, NamespaceTreeNode> { - &self.children - } - fn get(&self, component: &Rc) -> Option<&NamespaceTreeNode> { - self.children.get(component) - } - pub fn id(&self) -> NamespaceId { - self.id - } - fn contains(&self, ns: impl Into>>) -> bool { - self.find_namespace(ns).is_some() - } - fn find_namespace(&self, ns: impl Into>>) -> Option { - // look up a namespace in the tree and return the id - // do a breadth-first search through NamespaceTree for the namespace - // if it's not found, return None - let mut buf = Rc::new(self); - for component in ns.into().iter() { - if let Some(next_ns) = buf.get(component) { - buf = Rc::new(next_ns); - } else { - return None; - } - } - return Some(buf.id); - } - - /// If the namespace already exists, it will not be inserted. - /// Returns the ID of the namespace. - pub fn insert_or_find_namespace( - &mut self, - mut iter: Peekable, - assigner: &mut usize, - ) -> Option - where - I: Iterator>, - { - let next_item = match iter.next() { - Some(item) => item, - None => return None, - }; - println!("Inserting namespace {}", next_item); - - let next_node = self.children.get_mut(&next_item); - if let Some(mut next_node) = next_node { - return next_node.insert_or_find_namespace(iter, assigner); - } else { - println!("creating new node"); - *assigner += 1; - let mut new_node = - NamespaceTreeNode::new(NamespaceId::new(*assigner), HashMap::new()); - if iter.peek().is_none() { - let new_node_id = new_node.id; - self.children.insert(next_item, new_node); - return Some(new_node_id); - } else { - let id = new_node.insert_or_find_namespace(iter, assigner); - self.children.insert(next_item, new_node); - return id; - } - } - } - - fn find_id( - &self, - id: NamespaceId, - names_buf: Vec>, - ) -> (Vec>, Rc<&NamespaceTreeNode>) { - if self.id == id { - return (names_buf, Rc::new(self)); - } else { - for (name, node) in self.children.iter() { - let mut new_buf = names_buf.clone(); - new_buf.push(name.clone()); - let (names, node) = node.find_id(id, new_buf); - if names.len() > 0 { - return (names, node); - } - } - return (vec![], Rc::new(self)); - } - } - } -} +pub mod namespaces; diff --git a/compiler/qsc_fir/src/fir.rs b/compiler/qsc_fir/src/fir.rs index 8fc7f7d9c2..3fc4436088 100644 --- a/compiler/qsc_fir/src/fir.rs +++ b/compiler/qsc_fir/src/fir.rs @@ -1455,18 +1455,21 @@ impl FromIterator for VecIdent { VecIdent(iter.into_iter().collect()) } } - impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let idents = self.0.iter(); - write!( - f, - "{}", - idents - .map(|i| i.name.to_string()) - .collect::>() - .join(".") - ) + let mut buf = Vec::with_capacity(self.0.len()); + + for ident in self.0.iter() { + buf.push(format!("{}", ident)); + } + if buf.len() > 1 { + // use square brackets only if there are more than one ident + write!(f, "[{}]", buf.join(", ")) + } else { + write!(f, "{}", buf[0]) + + } } } impl VecIdent { diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 13a5f4b697..fdd7d22a67 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -1170,14 +1170,18 @@ impl FromIterator for VecIdent { impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let idents = self.0.iter(); - write!( - f, - "{}", - idents - .map(|i| i.name.to_string()) - .collect::>() - .join(".") - ) + let mut buf = Vec::with_capacity(self.0.len()); + + for ident in self.0.iter() { + buf.push(format!("{}", ident)); + } + if buf.len() > 1 { + // use square brackets only if there are more than one ident + write!(f, "[{}]", buf.join(", ")) + } else { + write!(f, "{}", buf[0]) + + } } } impl VecIdent { diff --git a/compiler/qsc_parse/src/item/tests.rs b/compiler/qsc_parse/src/item/tests.rs index d6c1de400b..2c9be50340 100644 --- a/compiler/qsc_parse/src/item/tests.rs +++ b/compiler/qsc_parse/src/item/tests.rs @@ -108,7 +108,7 @@ fn open_no_alias() { "open Foo.Bar.Baz;", &expect![[r#" Item _id_ [0-17]: - Open (Ident _id_ [5-16] "Foo.Bar.Baz")"#]], + Open ([Ident _id_ [5-8] "Foo", Ident _id_ [9-12] "Bar", Ident _id_ [13-16] "Baz"])"#]], ); } @@ -119,20 +119,20 @@ fn open_alias() { "open Foo.Bar.Baz as Baz;", &expect![[r#" Item _id_ [0-24]: - Open (Ident _id_ [5-16] "Foo.Bar.Baz") (Ident _id_ [20-23] "Baz")"#]], - ); -} - -#[test] -fn open_alias_dot() { - check( - parse, - "open Foo.Bar.Baz as Bar.Baz;", - &expect![[r#" - Item _id_ [0-28]: - Open (Ident _id_ [5-16] "Foo.Bar.Baz") (Ident _id_ [20-27] "Bar.Baz")"#]], - ); -} + Open ([Ident _id_ [5-8] "Foo", Ident _id_ [9-12] "Bar", Ident _id_ [13-16] "Baz"]) (Ident _id_ [20-23] "Baz")"#]], + ); +} +// TODO replicate below behavior? +// #[test] +// fn open_alias_dot() { +// check( +// parse, +// "open Foo.Bar.Baz as Bar.Baz;", +// &expect![[r#" +// Item _id_ [0-28]: +// Open (Ident _id_ [5-16] "Foo.Bar.Baz") (Ident _id_ [20-27] "Bar.Baz")"#]], +// ); +// } #[test] fn ty_decl() { diff --git a/compiler/qsc_parse/src/prim/tests.rs b/compiler/qsc_parse/src/prim/tests.rs index 04b664e81c..876dd2106d 100644 --- a/compiler/qsc_parse/src/prim/tests.rs +++ b/compiler/qsc_parse/src/prim/tests.rs @@ -100,7 +100,7 @@ fn path_triple() { check( path, "Foo.Bar.Baz", - &expect![[r#"Path _id_ [0-11] (Ident _id_ [0-7] "Foo.Bar") (Ident _id_ [8-11] "Baz")"#]], + &expect![[r#"Path _id_ [0-11] ([Ident _id_ [0-3] "Foo", Ident _id_ [4-7] "Bar"]) (Ident _id_ [8-11] "Baz")"#]], ); } From 81d1c6c8a99027ef7e88626136a2fb6866828dc6 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 27 Mar 2024 14:20:25 -0700 Subject: [PATCH 37/76] Working on making katas tests pass. WIP. Need to sort out how to handle the prelude. --- .../qsc_data_structures/src/namespaces.rs | 48 +++++++++---------- compiler/qsc_frontend/src/resolve.rs | 33 +++++++------ compiler/qsc_parse/src/item/tests.rs | 11 ----- compiler/qsc_parse/src/prim.rs | 16 +------ 4 files changed, 42 insertions(+), 66 deletions(-) diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index 3a7d53f3ec..dca8211f9a 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -1,10 +1,7 @@ #[cfg(test)] mod tests; -use std::{ - cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc, -}; - +use std::{cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; #[derive(Debug, Clone)] @@ -98,7 +95,6 @@ impl NamespaceTreeRoot { } } - impl Default for NamespaceTreeRoot { fn default() -> Self { Self { @@ -157,29 +153,29 @@ impl NamespaceTreeNode { where I: Iterator>, { - let next_item = match iter.next() { - Some(item) => item, - None => return None, - }; - println!("Inserting namespace {}", next_item); - + + let next_item = iter.next()?; + dbg!(&"insert_or_find_namespace", &next_item); let next_node = self.children.get_mut(&next_item); - if let Some(mut next_node) = next_node { - return next_node.insert_or_find_namespace(iter, assigner); - } else { - println!("creating new node"); - *assigner += 1; - let mut new_node = - NamespaceTreeNode::new(NamespaceId::new(*assigner), HashMap::new()); - if iter.peek().is_none() { - let new_node_id = new_node.id; - self.children.insert(next_item, new_node); - return Some(new_node_id); - } else { - let id = new_node.insert_or_find_namespace(iter, assigner); - self.children.insert(next_item, new_node); - return id; + match (next_node, iter.peek()) { + (Some(next_node), Some(_)) => { + return next_node.insert_or_find_namespace(iter, assigner); + } + (Some(next_node), None) => { + return Some(next_node.id); } + _ => {} + } + *assigner += 1; + let mut new_node = NamespaceTreeNode::new(NamespaceId::new(*assigner), HashMap::new()); + if iter.peek().is_none() { + let new_node_id = new_node.id; + self.children.insert(next_item, new_node); + Some(new_node_id) + } else { + let id = new_node.insert_or_find_namespace(iter, assigner); + self.children.insert(next_item, new_node); + id } } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 5d1ac9299b..f8caa01eda 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -35,12 +35,11 @@ use thiserror::Error; use crate::compile::preprocess::TrackedName; -const PRELUDE: &[&str] = &[ - "Microsoft.Quantum.Canon", - "Microsoft.Quantum.Core", - "Microsoft.Quantum.Intrinsic", - "Microsoft.Quantum.Measurement", -]; + const PRELUDE: [&[&str; 3]; 3] = [ + &["Microsoft", "Quantum", "Canon"], + &["Microsoft", "Quantum", "Core"], + &["Microsoft", "Quantum", "Intrinsic"], + ]; // All AST Path nodes get mapped // All AST Ident nodes get mapped, except those under AST Path nodes @@ -1083,6 +1082,12 @@ fn resolve<'a>( } else { None }; + // insert prelude into scope + for item in PRELUDE { + let ns = item.iter().map(|x| Rc::from(*x)).collect::>(); + globals.namespaces.insert_or_find_namespace(ns); + } + // let namespace = namespace.as_ref().map_or("", |i| &i.name); for scope in scopes { if namespace.is_none() { @@ -1110,11 +1115,11 @@ fn resolve<'a>( if candidates.is_empty() && namespace.is_none() { // Prelude shadows unopened globals. - let candidates = resolve_implicit_opens(kind, globals, PRELUDE, name_str); + let candidates = resolve_implicit_opens(kind, globals, PRELUDE.into_iter().map(|x| x.into_iter().map(|x| Rc::from(*x)).collect::>()).collect::>(), name_str); if candidates.len() > 1 { // If there are multiple candidates, sort them by namespace and return an error. let mut candidates: Vec<_> = candidates.into_iter().collect(); - candidates.sort_by_key(|x| x.1); + candidates.sort_by_key(|x| Into::::into(x.1)); let mut candidates = candidates .into_iter() .map(|candidate| candidate.1.to_string()); @@ -1263,17 +1268,17 @@ fn get_scope_locals(scope: &Scope, offset: u32, vars: bool) -> Vec { /// The return type represents the resolution of all implicit opens. /// The namespace is returned along with the res, so that the namespace can be used to /// report the ambiguity to the user. -fn resolve_implicit_opens<'a, 'b>( +fn resolve_implicit_opens<'b>( kind: NameKind, globals: &'b GlobalScope, - namespaces: impl IntoIterator, + namespaces: impl IntoIterator>>, name: &'b str, -) -> FxHashMap { +) -> FxHashMap { let mut candidates = FxHashMap::default(); for namespace in namespaces { - let namespace_id = globals.find_namespace(&[Rc::from(*namespace)]); - if let Some(&res) = globals.get(kind, todo!("this should become namespace id"), name) { - candidates.insert(res, *namespace); + let namespace_id = globals.find_namespace(namespace).expect("prelude should exist"); + if let Some(&res) = globals.get(kind, namespace_id, name) { + candidates.insert(res, namespace_id); } } candidates diff --git a/compiler/qsc_parse/src/item/tests.rs b/compiler/qsc_parse/src/item/tests.rs index 2c9be50340..4491d41a84 100644 --- a/compiler/qsc_parse/src/item/tests.rs +++ b/compiler/qsc_parse/src/item/tests.rs @@ -122,17 +122,6 @@ fn open_alias() { Open ([Ident _id_ [5-8] "Foo", Ident _id_ [9-12] "Bar", Ident _id_ [13-16] "Baz"]) (Ident _id_ [20-23] "Baz")"#]], ); } -// TODO replicate below behavior? -// #[test] -// fn open_alias_dot() { -// check( -// parse, -// "open Foo.Bar.Baz as Bar.Baz;", -// &expect![[r#" -// Item _id_ [0-28]: -// Open (Ident _id_ [5-16] "Foo.Bar.Baz") (Ident _id_ [20-27] "Bar.Baz")"#]], -// ); -// } #[test] fn ty_decl() { diff --git a/compiler/qsc_parse/src/prim.rs b/compiler/qsc_parse/src/prim.rs index d59bf4581c..79880035b9 100644 --- a/compiler/qsc_parse/src/prim.rs +++ b/compiler/qsc_parse/src/prim.rs @@ -89,9 +89,7 @@ pub(super) fn path(s: &mut ParserContext) -> Result> { let name = parts.pop().expect("path should have at least one part"); let namespace = match (parts.first(), parts.last()) { - (Some(first), Some(last)) => { - let lo = first.span.lo; - let hi = last.span.hi; + (Some(_), Some(_)) => { Some( parts .iter() @@ -248,18 +246,6 @@ fn advanced(s: &ParserContext, from: u32) -> bool { s.peek().span.lo > from } -fn join(mut strings: impl Iterator>, sep: &str) -> String { - let mut string = String::new(); - if let Some(s) = strings.next() { - string.push_str(s.as_ref()); - } - for s in strings { - string.push_str(sep); - string.push_str(s.as_ref()); - } - string -} - fn map_rule_name(name: &'static str, error: Error) -> Error { Error(match error.0 { ErrorKind::Rule(_, found, span) => ErrorKind::Rule(name, found, span), From 37196102bc7962407da24d1702f2dae88c344ff1 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 28 Mar 2024 13:48:52 -0700 Subject: [PATCH 38/76] namespaces seem to be...working? --- .../qsc_data_structures/src/namespaces.rs | 51 ++++++++++++++----- compiler/qsc_frontend/src/resolve.rs | 5 -- language_service/src/completion.rs | 2 +- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index dca8211f9a..bed28a18b0 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -3,19 +3,18 @@ mod tests; use std::{cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; -#[derive(Debug, Clone)] - -pub struct NamespaceTreeRoot { - assigner: usize, - tree: NamespaceTreeNode, -} +const PRELUDE: [[&str; 3]; 3] = [ + ["Microsoft", "Quantum", "Canon"], + ["Microsoft", "Quantum", "Core"], + ["Microsoft", "Quantum", "Intrinsic"], +]; /// An ID that corresponds to a namespace in the global scope. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)] pub struct NamespaceId(usize); impl NamespaceId { /// Create a new namespace ID. - pub fn new(value: usize) -> Self { + #[must_use] pub fn new(value: usize) -> Self { Self(value) } } @@ -51,12 +50,21 @@ impl Display for NamespaceId { } } +#[derive(Debug, Clone)] +pub struct NamespaceTreeRoot { + assigner: usize, + tree: NamespaceTreeNode, +} + impl NamespaceTreeRoot { /// Create a new namespace tree root. The assigner is used to assign new namespace IDs. - pub fn new(assigner: usize, tree: NamespaceTreeNode) -> Self { + #[must_use] + pub fn new_from_parts(assigner: usize, tree: NamespaceTreeNode) -> Self { Self { assigner, tree } } + /// Get the namespace tree field. This is the root of the namespace tree. + #[must_use] pub fn tree(&self) -> &NamespaceTreeNode { &self.tree } @@ -86,10 +94,12 @@ impl NamespaceTreeRoot { self.tree.find_namespace(ns) } + #[must_use] pub fn find_id(&self, id: &NamespaceId) -> (Vec>, Rc<&NamespaceTreeNode>) { return self.tree.find_id(*id, vec![]); } + #[must_use] pub fn root_id(&self) -> NamespaceId { self.tree.id } @@ -97,13 +107,19 @@ impl NamespaceTreeRoot { impl Default for NamespaceTreeRoot { fn default() -> Self { - Self { + let mut tree = Self { assigner: 0, tree: NamespaceTreeNode { children: HashMap::new(), id: NamespaceId::new(0), }, + }; + // insert the prelude namespaces using the `NamespaceTreeRoot` API + for ns in &PRELUDE { + let iter = ns.iter().map(|s| Rc::from(*s)).peekable(); + tree.insert_or_find_namespace(iter); } + tree } } @@ -113,34 +129,43 @@ pub struct NamespaceTreeNode { pub id: NamespaceId, } impl NamespaceTreeNode { + #[must_use] pub fn new(id: NamespaceId, children: HashMap, NamespaceTreeNode>) -> Self { Self { children, id } } + + #[must_use] pub fn children(&self) -> &HashMap, NamespaceTreeNode> { &self.children } + fn get(&self, component: &Rc) -> Option<&NamespaceTreeNode> { self.children.get(component) } + + #[must_use] pub fn id(&self) -> NamespaceId { self.id } - fn contains(&self, ns: impl Into>>) -> bool { + + #[must_use] + pub fn contains(&self, ns: impl Into>>) -> bool { self.find_namespace(ns).is_some() } + fn find_namespace(&self, ns: impl Into>>) -> Option { // look up a namespace in the tree and return the id // do a breadth-first search through NamespaceTree for the namespace // if it's not found, return None let mut buf = Rc::new(self); - for component in ns.into().iter() { + for component in &ns.into() { if let Some(next_ns) = buf.get(component) { buf = Rc::new(next_ns); } else { return None; } } - return Some(buf.id); + Some(buf.id) } /// If the namespace already exists, it will not be inserted. @@ -153,9 +178,7 @@ impl NamespaceTreeNode { where I: Iterator>, { - let next_item = iter.next()?; - dbg!(&"insert_or_find_namespace", &next_item); let next_node = self.children.get_mut(&next_item); match (next_node, iter.peek()) { (Some(next_node), Some(_)) => { diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index f8caa01eda..48a1ba380e 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1082,11 +1082,6 @@ fn resolve<'a>( } else { None }; - // insert prelude into scope - for item in PRELUDE { - let ns = item.iter().map(|x| Rc::from(*x)).collect::>(); - globals.namespaces.insert_or_find_namespace(ns); - } // let namespace = namespace.as_ref().map_or("", |i| &i.name); for scope in scopes { diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index bb2c8f78ac..49aba8be33 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -575,7 +575,7 @@ fn convert_ast_namespaces_into_hir_namespaces( hir_namespaces.insert(namespace.clone(), children); } - qsc::NamespaceTreeRoot::new(assigner, NamespaceTreeNode::new(root_id, hir_namespaces)) + qsc::NamespaceTreeRoot::new_from_parts(assigner, NamespaceTreeNode::new(root_id, hir_namespaces)) } /// Convert a local into a completion item From 86e99b96d47b7350755a670708ae880b78e187b4 Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 1 Apr 2024 09:50:17 -0700 Subject: [PATCH 39/76] mid-opens-refactor, might need to revert this --- .../qsc_data_structures/src/namespaces.rs | 4 +- compiler/qsc_frontend/src/resolve.rs | 91 ++++++++++++------- compiler/qsc_frontend/src/resolve/tests.rs | 25 +++++ 3 files changed, 88 insertions(+), 32 deletions(-) diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index bed28a18b0..9cba96d913 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -14,7 +14,8 @@ const PRELUDE: [[&str; 3]; 3] = [ pub struct NamespaceId(usize); impl NamespaceId { /// Create a new namespace ID. - #[must_use] pub fn new(value: usize) -> Self { + #[must_use] + pub fn new(value: usize) -> Self { Self(value) } } @@ -162,6 +163,7 @@ impl NamespaceTreeNode { if let Some(next_ns) = buf.get(component) { buf = Rc::new(next_ns); } else { + println!("Did not find component {component}. Options were {:?}.", buf.children.keys()); return None; } } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 48a1ba380e..f728a1db6d 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -13,7 +13,7 @@ use qsc_ast::{ }; use qsc_data_structures::{ index_map::IndexMap, - namespaces::{NamespaceId, NamespaceTreeRoot}, + namespaces::{NamespaceId, NamespaceTreeNode, NamespaceTreeRoot}, span::Span, }; use qsc_hir::{ @@ -127,8 +127,8 @@ pub struct Scope { /// the entire callable / namespace declaration. For blocks, this includes the braces. span: Span, kind: ScopeKind, - /// Open statements. The key is the namespace name or alias. - opens: FxHashMap>, + /// Open statements. The key is the namespace name or alias. The vector of opens is + opens: FxHashMap, /// Local newtype declarations. tys: FxHashMap, ItemId>, /// Local callable and newtype declarations. @@ -150,7 +150,7 @@ impl Scope { Self { span, kind, - opens: FxHashMap::default(), + opens: Default::default(), tys: FxHashMap::default(), terms: FxHashMap::default(), vars: FxHashMap::default(), @@ -280,6 +280,14 @@ impl GlobalScope { fn insert_or_find_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.namespaces.insert_or_find_namespace(name.into()) } + + fn find_id(&self, base_namespace: NamespaceId) -> (Vec>, Rc<&NamespaceTreeNode>) { + self.namespaces.find_id(&base_namespace) + } + + fn root_id(&self) -> NamespaceId { + self.namespaces.root_id() + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -289,7 +297,7 @@ enum ScopeKind { Block, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] enum NameKind { Ty, Term, @@ -513,11 +521,8 @@ impl Resolver { self.current_scope_mut() .opens .entry(id) - .or_default() - .push(Open { - namespace: id, - span: name.span(), - }); + .or_insert_with( || name.span()) + ; } else { self.errors .push(Error::NotFound(name.to_string(), name.span())); @@ -646,8 +651,14 @@ impl AstVisitor<'_> for With<'_> { .globals .find_namespace(Into::>::into(namespace.name.clone())) .expect("namespace should exist"); + let kind = ScopeKind::Namespace(ns); self.with_scope(namespace.span, kind, |visitor| { + // bind an open for the parent namespace + println!("Adding open for {}", namespace.name); + visitor.resolver.bind_open(&namespace.name, &None); + println!("Scope chain is {:?}", visitor.resolver.curr_scope_chain); + println!("Scopes are {:?}", visitor.resolver.locals.scopes); for item in &*namespace.items { if let ast::ItemKind::Open(name, alias) = &*item.kind { visitor.resolver.bind_open(name, alias); @@ -810,6 +821,8 @@ impl GlobalTable { core.insert(name, res); } + // Because of the `Default` implementation on `NamespaceTreeRoot`, the prelude is automatically + // included in the scope. let mut scope = GlobalScope::default(); let ns = scope.insert_or_find_namespace(vec![ Rc::from("Microsoft"), @@ -1073,34 +1086,45 @@ fn resolve<'a>( name: &Ident, namespace_name: &Option, ) -> Result { + println!("____________________________"); + + println!("Resolving {name} ({kind:?}) in {namespace_name:?}"); let scopes = scopes.collect::>(); + let mut candidates = FxHashMap::default(); let mut vars = true; + println!("Name string is {}", name.name); let name_str = &(*name.name); - let namespace = if let Some(namespace) = namespace_name { - globals.find_namespace(namespace) - } else { - None - }; + let mut namespace = globals.root_id(); + let mut opened_namespaces = Vec::new(); + for scope in &scopes { + if let ScopeKind::Namespace(l_namespace) = scope.kind { + println!("Adding open for {namespace:?}"); + namespace = l_namespace; + } + opened_namespaces.extend(&mut scope.opens.keys()); + } + + println!("There are {} scopes to search", scopes.len()); + // let namespace = namespace.as_ref().map_or("", |i| &i.name); + for scope in scopes { + dbg!(&scope.opens); + + println!("Opens with namespace names are {:?}", scope.opens.keys().map(|x| globals.find_id(*x)).collect::>()); + // for every child namespace of the opened namespace, insert that namespace - // let namespace = namespace.as_ref().map_or("", |i| &i.name); - for scope in scopes { - if namespace.is_none() { if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, name_str) { // Local declarations shadow everything. return Ok(res); } - } - if let Some(namespace) = namespace { - if let (Some(namespaces)) = scope.opens.get(&namespace) { - candidates = resolve_explicit_opens(kind, globals, namespaces, name_str); - if !candidates.is_empty() { - // Explicit opens shadow prelude and unopened globals. - break; - } + + candidates = resolve_explicit_opens(kind, globals, scope.opens.keys(), name_str); + if !candidates.is_empty() { + // Explicit opens shadow prelude and unopened globals. + break; } - } + if scope.kind == ScopeKind::Callable { // Since local callables are not closures, hide local variables in parent scopes. @@ -1108,7 +1132,8 @@ fn resolve<'a>( } } - if candidates.is_empty() && namespace.is_none() { + if candidates.is_empty() { + println!("Candidates was empty"); // Prelude shadows unopened globals. let candidates = resolve_implicit_opens(kind, globals, PRELUDE.into_iter().map(|x| x.into_iter().map(|x| Rc::from(*x)).collect::>()).collect::>(), name_str); if candidates.len() > 1 { @@ -1140,7 +1165,7 @@ fn resolve<'a>( if candidates.is_empty() { if let Some(&res) = globals.get( kind, - namespace.unwrap_or_else(|| globals.namespaces.root_id()), + namespace, name_str, ) { // An unopened global is the last resort. @@ -1218,6 +1243,7 @@ fn resolve_scope_locals( if let ScopeKind::Namespace(namespace) = &scope.kind { if let Some(&res) = globals.get(kind, *namespace, name) { + println!("NAMESPACE WAS FOUND with res {res:?}"); return Some(res); } } @@ -1282,12 +1308,15 @@ fn resolve_implicit_opens<'b>( fn resolve_explicit_opens<'a>( kind: NameKind, globals: &GlobalScope, - opens: impl IntoIterator, + opens: impl IntoIterator, name: &str, -) -> FxHashMap { +) -> FxHashMap { + println!("EXPLICIT OPENS Name is {name}"); let mut candidates = FxHashMap::default(); for open in opens { + println!("EXPLICIT OPENS Resolving explicit open {open:?}"); if let Some(&res) = globals.get(kind, open.namespace, name) { + println!("EXPLICIT OPENS Found res {res:?} for name {name}"); candidates.insert(res, open); } } diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index f0d193da16..6967cf60c2 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2654,3 +2654,28 @@ fn newtype_with_tuple_destructuring() { "#]], ); } + +#[test] +fn symbols_visible_according_to_hierarchy() { + check( + indoc! {" + namespace NamespaceA { + @EntryPoint() + function Main(): Int { + Func() + } + + function Func() : Int { + // should be visible, since we are in the + // Foo namespace + InnerOne.InnerTwo.Bar() + } + } + + namespace NamespaceA.InnerOne.InnerTwo { + function Bar() : Int { 6 } + } + "}, + &expect![[r#""#]], + ); +} \ No newline at end of file From d5e7011d918c633e40c946298f3a3df4ff4e8986 Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 1 Apr 2024 11:43:10 -0700 Subject: [PATCH 40/76] Revert "mid-opens-refactor, might need to revert this" This reverts commit 86e99b96d47b7350755a670708ae880b78e187b4. --- .../qsc_data_structures/src/namespaces.rs | 4 +- compiler/qsc_frontend/src/resolve.rs | 91 +++++++------------ compiler/qsc_frontend/src/resolve/tests.rs | 25 ----- 3 files changed, 32 insertions(+), 88 deletions(-) diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index 9cba96d913..bed28a18b0 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -14,8 +14,7 @@ const PRELUDE: [[&str; 3]; 3] = [ pub struct NamespaceId(usize); impl NamespaceId { /// Create a new namespace ID. - #[must_use] - pub fn new(value: usize) -> Self { + #[must_use] pub fn new(value: usize) -> Self { Self(value) } } @@ -163,7 +162,6 @@ impl NamespaceTreeNode { if let Some(next_ns) = buf.get(component) { buf = Rc::new(next_ns); } else { - println!("Did not find component {component}. Options were {:?}.", buf.children.keys()); return None; } } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index f728a1db6d..48a1ba380e 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -13,7 +13,7 @@ use qsc_ast::{ }; use qsc_data_structures::{ index_map::IndexMap, - namespaces::{NamespaceId, NamespaceTreeNode, NamespaceTreeRoot}, + namespaces::{NamespaceId, NamespaceTreeRoot}, span::Span, }; use qsc_hir::{ @@ -127,8 +127,8 @@ pub struct Scope { /// the entire callable / namespace declaration. For blocks, this includes the braces. span: Span, kind: ScopeKind, - /// Open statements. The key is the namespace name or alias. The vector of opens is - opens: FxHashMap, + /// Open statements. The key is the namespace name or alias. + opens: FxHashMap>, /// Local newtype declarations. tys: FxHashMap, ItemId>, /// Local callable and newtype declarations. @@ -150,7 +150,7 @@ impl Scope { Self { span, kind, - opens: Default::default(), + opens: FxHashMap::default(), tys: FxHashMap::default(), terms: FxHashMap::default(), vars: FxHashMap::default(), @@ -280,14 +280,6 @@ impl GlobalScope { fn insert_or_find_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.namespaces.insert_or_find_namespace(name.into()) } - - fn find_id(&self, base_namespace: NamespaceId) -> (Vec>, Rc<&NamespaceTreeNode>) { - self.namespaces.find_id(&base_namespace) - } - - fn root_id(&self) -> NamespaceId { - self.namespaces.root_id() - } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -297,7 +289,7 @@ enum ScopeKind { Block, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy)] enum NameKind { Ty, Term, @@ -521,8 +513,11 @@ impl Resolver { self.current_scope_mut() .opens .entry(id) - .or_insert_with( || name.span()) - ; + .or_default() + .push(Open { + namespace: id, + span: name.span(), + }); } else { self.errors .push(Error::NotFound(name.to_string(), name.span())); @@ -651,14 +646,8 @@ impl AstVisitor<'_> for With<'_> { .globals .find_namespace(Into::>::into(namespace.name.clone())) .expect("namespace should exist"); - let kind = ScopeKind::Namespace(ns); self.with_scope(namespace.span, kind, |visitor| { - // bind an open for the parent namespace - println!("Adding open for {}", namespace.name); - visitor.resolver.bind_open(&namespace.name, &None); - println!("Scope chain is {:?}", visitor.resolver.curr_scope_chain); - println!("Scopes are {:?}", visitor.resolver.locals.scopes); for item in &*namespace.items { if let ast::ItemKind::Open(name, alias) = &*item.kind { visitor.resolver.bind_open(name, alias); @@ -821,8 +810,6 @@ impl GlobalTable { core.insert(name, res); } - // Because of the `Default` implementation on `NamespaceTreeRoot`, the prelude is automatically - // included in the scope. let mut scope = GlobalScope::default(); let ns = scope.insert_or_find_namespace(vec![ Rc::from("Microsoft"), @@ -1086,45 +1073,34 @@ fn resolve<'a>( name: &Ident, namespace_name: &Option, ) -> Result { - println!("____________________________"); - - println!("Resolving {name} ({kind:?}) in {namespace_name:?}"); let scopes = scopes.collect::>(); - let mut candidates = FxHashMap::default(); let mut vars = true; - println!("Name string is {}", name.name); let name_str = &(*name.name); - let mut namespace = globals.root_id(); - let mut opened_namespaces = Vec::new(); - for scope in &scopes { - if let ScopeKind::Namespace(l_namespace) = scope.kind { - println!("Adding open for {namespace:?}"); - namespace = l_namespace; - } - opened_namespaces.extend(&mut scope.opens.keys()); - } - - println!("There are {} scopes to search", scopes.len()); - // let namespace = namespace.as_ref().map_or("", |i| &i.name); - for scope in scopes { - dbg!(&scope.opens); - - println!("Opens with namespace names are {:?}", scope.opens.keys().map(|x| globals.find_id(*x)).collect::>()); - // for every child namespace of the opened namespace, insert that namespace + let namespace = if let Some(namespace) = namespace_name { + globals.find_namespace(namespace) + } else { + None + }; + // let namespace = namespace.as_ref().map_or("", |i| &i.name); + for scope in scopes { + if namespace.is_none() { if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, name_str) { // Local declarations shadow everything. return Ok(res); } + } - - candidates = resolve_explicit_opens(kind, globals, scope.opens.keys(), name_str); - if !candidates.is_empty() { - // Explicit opens shadow prelude and unopened globals. - break; + if let Some(namespace) = namespace { + if let (Some(namespaces)) = scope.opens.get(&namespace) { + candidates = resolve_explicit_opens(kind, globals, namespaces, name_str); + if !candidates.is_empty() { + // Explicit opens shadow prelude and unopened globals. + break; + } } - + } if scope.kind == ScopeKind::Callable { // Since local callables are not closures, hide local variables in parent scopes. @@ -1132,8 +1108,7 @@ fn resolve<'a>( } } - if candidates.is_empty() { - println!("Candidates was empty"); + if candidates.is_empty() && namespace.is_none() { // Prelude shadows unopened globals. let candidates = resolve_implicit_opens(kind, globals, PRELUDE.into_iter().map(|x| x.into_iter().map(|x| Rc::from(*x)).collect::>()).collect::>(), name_str); if candidates.len() > 1 { @@ -1165,7 +1140,7 @@ fn resolve<'a>( if candidates.is_empty() { if let Some(&res) = globals.get( kind, - namespace, + namespace.unwrap_or_else(|| globals.namespaces.root_id()), name_str, ) { // An unopened global is the last resort. @@ -1243,7 +1218,6 @@ fn resolve_scope_locals( if let ScopeKind::Namespace(namespace) = &scope.kind { if let Some(&res) = globals.get(kind, *namespace, name) { - println!("NAMESPACE WAS FOUND with res {res:?}"); return Some(res); } } @@ -1308,15 +1282,12 @@ fn resolve_implicit_opens<'b>( fn resolve_explicit_opens<'a>( kind: NameKind, globals: &GlobalScope, - opens: impl IntoIterator, + opens: impl IntoIterator, name: &str, -) -> FxHashMap { - println!("EXPLICIT OPENS Name is {name}"); +) -> FxHashMap { let mut candidates = FxHashMap::default(); for open in opens { - println!("EXPLICIT OPENS Resolving explicit open {open:?}"); if let Some(&res) = globals.get(kind, open.namespace, name) { - println!("EXPLICIT OPENS Found res {res:?} for name {name}"); candidates.insert(res, open); } } diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index 6967cf60c2..f0d193da16 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2654,28 +2654,3 @@ fn newtype_with_tuple_destructuring() { "#]], ); } - -#[test] -fn symbols_visible_according_to_hierarchy() { - check( - indoc! {" - namespace NamespaceA { - @EntryPoint() - function Main(): Int { - Func() - } - - function Func() : Int { - // should be visible, since we are in the - // Foo namespace - InnerOne.InnerTwo.Bar() - } - } - - namespace NamespaceA.InnerOne.InnerTwo { - function Bar() : Int { 6 } - } - "}, - &expect![[r#""#]], - ); -} \ No newline at end of file From 966ab4bb8cb04e1b1994afacf2568f48ed72e69b Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 1 Apr 2024 13:59:32 -0700 Subject: [PATCH 41/76] hierarchical opens work --- .../qsc_data_structures/src/namespaces.rs | 2 +- compiler/qsc_frontend/src/compile/tests.rs | 38 -------- compiler/qsc_frontend/src/resolve.rs | 90 ++++++++++++------- 3 files changed, 57 insertions(+), 73 deletions(-) diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index bed28a18b0..68f16bfbff 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -153,7 +153,7 @@ impl NamespaceTreeNode { self.find_namespace(ns).is_some() } - fn find_namespace(&self, ns: impl Into>>) -> Option { + pub fn find_namespace(&self, ns: impl Into>>) -> Option { // look up a namespace in the tree and return the id // do a breadth-first search through NamespaceTree for the namespace // if it's not found, return None diff --git a/compiler/qsc_frontend/src/compile/tests.rs b/compiler/qsc_frontend/src/compile/tests.rs index fb93dbb0f9..4a2b560335 100644 --- a/compiler/qsc_frontend/src/compile/tests.rs +++ b/compiler/qsc_frontend/src/compile/tests.rs @@ -1292,42 +1292,4 @@ fn hierarchical_namespace_basic() { LanguageFeatures::default(), ); assert!(lib.errors.is_empty(), "{:#?}", lib.errors); - let lib = store.insert(lib); - - let sources = SourceMap::new( - [( - "test".into(), - indoc! {" - namespace Test { - operation Bar() : Unit { body intrinsic; } - } - "} - .into(), - )], - None, - ); - - let unit = compile( - &store, - &[lib], - sources, - RuntimeCapabilityFlags::all(), - LanguageFeatures::default(), - ); - expect![[r#" - [ - Error( - Resolve( - DuplicateIntrinsic( - "Bar", - Span { - lo: 31, - hi: 34, - }, - ), - ), - ), - ] - "#]] - .assert_debug_eq(&unit.errors); } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 48a1ba380e..7ff87caca7 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1077,38 +1077,59 @@ fn resolve<'a>( let mut candidates = FxHashMap::default(); let mut vars = true; let name_str = &(*name.name); - let namespace = if let Some(namespace) = namespace_name { - globals.find_namespace(namespace) - } else { - None - }; - - // let namespace = namespace.as_ref().map_or("", |i| &i.name); - for scope in scopes { - if namespace.is_none() { - if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, name_str) { - // Local declarations shadow everything. - return Ok(res); + if name_str == "Baz" { + dbg!(); + } + let mut candidate_namespaces = vec![globals.namespaces.root_id()]; + // here, we also need to check all opens to see if the namespace is in any of them + for open in scopes.iter().flat_map(|scope| scope.opens.values().flatten()) { + // insert each open into the list of places to check for this item + candidate_namespaces.push(open.namespace); + } + // search through each candidate namespace to find the items + for candidate_namespace in candidate_namespaces { + let candidate_namespace = globals.namespaces.find_id(&candidate_namespace).1; + let namespace = if let Some(namespace) = namespace_name { + let res = candidate_namespace.find_namespace(namespace); + let res_2 = res.is_some(); + let stringname = format!("{namespace}"); + res + } else { + None + }; + + // let namespace = namespace.as_ref().map_or("", |i| &i.name); + for scope in &scopes { + if namespace.is_none() { + if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, name_str) { + // Local declarations shadow everything. + return Ok(res); + } } - } - if let Some(namespace) = namespace { - if let (Some(namespaces)) = scope.opens.get(&namespace) { - candidates = resolve_explicit_opens(kind, globals, namespaces, name_str); - if !candidates.is_empty() { - // Explicit opens shadow prelude and unopened globals. - break; + if let Some(namespace) = namespace { + if let Some(namespaces) = scope.opens.get(&namespace) { + candidates = resolve_explicit_opens(kind, globals, namespaces, name_str); + if !candidates.is_empty() { + // Explicit opens shadow prelude and unopened globals. + break; + } + } + if let Some(res) = globals.get(kind, namespace, name_str) { + return Ok(res.clone()); + // candidates.insert(candidate., candidate_namespace); } } - } - if scope.kind == ScopeKind::Callable { - // Since local callables are not closures, hide local variables in parent scopes. - vars = false; + if scope.kind == ScopeKind::Callable { + // Since local callables are not closures, hide local variables in parent scopes. + vars = false; + } } + } - if candidates.is_empty() && namespace.is_none() { + if candidates.is_empty() && namespace_name.is_none() { // Prelude shadows unopened globals. let candidates = resolve_implicit_opens(kind, globals, PRELUDE.into_iter().map(|x| x.into_iter().map(|x| Rc::from(*x)).collect::>()).collect::>(), name_str); if candidates.len() > 1 { @@ -1137,16 +1158,17 @@ fn resolve<'a>( } } - if candidates.is_empty() { - if let Some(&res) = globals.get( - kind, - namespace.unwrap_or_else(|| globals.namespaces.root_id()), - name_str, - ) { - // An unopened global is the last resort. - return Ok(res); - } - } + // TODO + // if candidates.is_empty() { + // if let Some(&res) = globals.get( + // kind, + // namespace.unwrap_or_else(|| globals.namespaces.root_id()), + // name_str, + // ) { + // // An unopened global is the last resort. + // return Ok(res); + // } + // } if candidates.len() > 1 { // If there are multiple candidates, remove unimplemented items. This allows resolution to From 9fe7bdeee9cbe5cac581a133f09c2cdd775c886f Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 1 Apr 2024 14:11:11 -0700 Subject: [PATCH 42/76] fix display in duplicate definition error --- compiler/qsc_frontend/src/resolve.rs | 26 +++++++++++++--------- compiler/qsc_frontend/src/resolve/tests.rs | 23 +++++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 7ff87caca7..a6b08f4b23 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1000,11 +1000,14 @@ fn bind_global_item( .or_default() .entry(Rc::clone(&decl.name.name)) { - Entry::Occupied(_) => errors.push(Error::Duplicate( - decl.name.name.to_string(), - namespace.to_string(), - decl.name.span, - )), + Entry::Occupied(_) => { + let namespace_name = scope.namespaces.find_id(&namespace).0.join("."); + errors.push(Error::Duplicate( + decl.name.name.to_string(), + namespace_name.to_string(), + decl.name.span, + )) + }, Entry::Vacant(entry) => { entry.insert(res); } @@ -1040,11 +1043,14 @@ fn bind_global_item( .or_default() .entry(Rc::clone(&name.name)), ) { - (Entry::Occupied(_), _) | (_, Entry::Occupied(_)) => Err(vec![Error::Duplicate( - name.name.to_string(), - namespace.to_string(), - name.span, - )]), + (Entry::Occupied(_), _) | (_, Entry::Occupied(_)) => { + let namespace_name = scope.namespaces.find_id(&namespace).0.join("."); + Err(vec![Error::Duplicate( + name.name.to_string(), + namespace_name, + name.span, + )]) + }, (Entry::Vacant(term_entry), Entry::Vacant(ty_entry)) => { term_entry.insert(res); ty_entry.insert(res); diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index f0d193da16..e99650e3c1 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2654,3 +2654,26 @@ fn newtype_with_tuple_destructuring() { "#]], ); } + +#[test] +fn items_resolve_according_to_hierarchy() { + check( + indoc! {" +namespace Foo { + @EntryPoint() + function Main(): Int { + Foo() + } + + function Foo() : Int { + Bar.Baz.Bar() + } +} + +namespace Foo.Bar.Baz { + function Bar() : Int { 6 } +} +"}, + &expect![[r#" "#]], + ); +} \ No newline at end of file From 467beeb8871291a81d6400878fe15924a1d7e782 Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 1 Apr 2024 14:21:37 -0700 Subject: [PATCH 43/76] implicit hierarchy works --- compiler/qsc_frontend/src/resolve.rs | 1 + compiler/qsc_frontend/src/resolve/tests.rs | 57 +++++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index a6b08f4b23..5a6d766696 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -648,6 +648,7 @@ impl AstVisitor<'_> for With<'_> { .expect("namespace should exist"); let kind = ScopeKind::Namespace(ns); self.with_scope(namespace.span, kind, |visitor| { + visitor.resolver.bind_open(&namespace.name, &None); for item in &*namespace.items { if let ast::ItemKind::Open(name, alias) = &*item.kind { visitor.resolver.bind_open(name, alias); diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index e99650e3c1..96fa97dc7b 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2656,7 +2656,7 @@ fn newtype_with_tuple_destructuring() { } #[test] -fn items_resolve_according_to_hierarchy() { +fn items_resolve_according_to_implicit_hierarchy() { check( indoc! {" namespace Foo { @@ -2674,6 +2674,59 @@ namespace Foo.Bar.Baz { function Bar() : Int { 6 } } "}, - &expect![[r#" "#]], + &expect![[r#" + namespace Foo { + @EntryPoint() + function item1(): Int { + item2() + } + + function item2() : Int { + item4() + } + } + + namespace Foo.Bar.Baz { + function item4() : Int { 6 } + } + "#]], + ); +} +#[test] +fn basic_hierarchical_namespace() { + check( + indoc! {" + namespace Foo.Bar.Baz { + operation Quux() : Unit {} + } + namespace A { + open Foo; + operation Main() : Unit { + Bar.Baz.Quux(); + } + } + namespace B { + open Foo.Bar; + operation Main() : Unit { + Baz.Quux(); + } + }"}, + &expect![[r#" + namespace Foo.Bar.Baz { + operation item1() : Unit {} + } + namespace A { + open Foo; + operation item3() : Unit { + item1(); + } + } + namespace B { + open Foo.Bar; + operation item5() : Unit { + item1(); + } + }"#]], ); + } \ No newline at end of file From 71b5a555475e92bdc490f40551f3e14d4af208dd Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 1 Apr 2024 15:08:12 -0700 Subject: [PATCH 44/76] improve test clarity --- compiler/qsc_frontend/src/resolve/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index 96fa97dc7b..a6622f12e9 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2666,12 +2666,12 @@ namespace Foo { } function Foo() : Int { - Bar.Baz.Bar() + Bar.Baz.Quux() } } namespace Foo.Bar.Baz { - function Bar() : Int { 6 } + function Quux() : Int { 6 } } "}, &expect![[r#" From 1a25d48c6411f1b062b2c9378f24b60bdc1bde1b Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 1 Apr 2024 21:05:15 -0700 Subject: [PATCH 45/76] it works -- but tons of debug stuff everywhere --- compiler/qsc_ast/src/visit.rs | 3 + compiler/qsc_frontend/src/compile.rs | 1 + compiler/qsc_frontend/src/resolve.rs | 152 +++++++++++++++++++++------ compiler/qsc_hir/src/global.rs | 12 +++ 4 files changed, 136 insertions(+), 32 deletions(-) diff --git a/compiler/qsc_ast/src/visit.rs b/compiler/qsc_ast/src/visit.rs index 947c719570..e67d127de2 100644 --- a/compiler/qsc_ast/src/visit.rs +++ b/compiler/qsc_ast/src/visit.rs @@ -85,11 +85,14 @@ pub fn walk_package<'a>(vis: &mut impl Visitor<'a>, package: &'a Package) { } pub fn walk_namespace<'a>(vis: &mut impl Visitor<'a>, namespace: &'a Namespace) { + // possibly this visit is incorrect? vis.visit_vec_ident(&namespace.name); + // TODO(alex) HERE is where the errors are coming from -- namespace.items.iter().for_each(|i| vis.visit_item(i)); } pub fn walk_item<'a>(vis: &mut impl Visitor<'a>, item: &'a Item) { + // TODO figure out where in here the errors are coming from item.attrs.iter().for_each(|a| vis.visit_attr(a)); item.visibility.iter().for_each(|v| vis.visit_visibility(v)); match &*item.kind { diff --git a/compiler/qsc_frontend/src/compile.rs b/compiler/qsc_frontend/src/compile.rs index 70b930c928..9fccc6fa96 100644 --- a/compiler/qsc_frontend/src/compile.rs +++ b/compiler/qsc_frontend/src/compile.rs @@ -509,6 +509,7 @@ fn resolve_all( let mut errors = globals.add_local_package(assigner, package); let mut resolver = Resolver::new(globals, dropped_names); + // TODO(alex) no this is where they are coming from resolver.with(assigner).visit_package(package); let (names, locals, mut resolver_errors, namespaces) = resolver.into_result(); errors.append(&mut resolver_errors); diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 5a6d766696..f5c1717573 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -35,11 +35,11 @@ use thiserror::Error; use crate::compile::preprocess::TrackedName; - const PRELUDE: [&[&str; 3]; 3] = [ - &["Microsoft", "Quantum", "Canon"], - &["Microsoft", "Quantum", "Core"], - &["Microsoft", "Quantum", "Intrinsic"], - ]; +const PRELUDE: [&[&str; 3]; 3] = [ + &["Microsoft", "Quantum", "Canon"], + &["Microsoft", "Quantum", "Core"], + &["Microsoft", "Quantum", "Intrinsic"], +]; // All AST Path nodes get mapped // All AST Ident nodes get mapped, except those under AST Path nodes @@ -428,6 +428,12 @@ impl Resolver { let name = &path.name; let namespace = &path.namespace; + if name.name.to_string() == "Fact" { + dbg!(); + } + let x = format!("{name}"); + let namespace_string = format!("{namespace:?}"); + match resolve( kind, &self.globals, @@ -507,7 +513,19 @@ impl Resolver { fn bind_open(&mut self, name: &ast::VecIdent, alias: &Option>) { // TODO figure out how aliases are going to work - let id = self.globals.find_namespace(name).expect("ns should exist"); + let id = match self.globals.find_namespace(dbg!(name)) { + Some(id) => id, + None => { + self.errors.push(Error::NotFound( + name.iter() + .map(|x| x.name.to_string()) + .collect::>() + .join("."), + name.span(), + )); + return; + } + }; // let alias = alias.as_ref().map_or("".into(), |a| Rc::clone(&a.name)); if self.globals.namespaces.find_namespace(name).is_some() { self.current_scope_mut() @@ -654,7 +672,7 @@ impl AstVisitor<'_> for With<'_> { visitor.resolver.bind_open(name, alias); } } - + // errors show up in visitor in the below function ast_visit::walk_namespace(visitor, namespace); }); } @@ -880,6 +898,15 @@ impl GlobalTable { let namespace = self .scope .insert_or_find_namespace(global.namespace.clone()); + if id == PackageId::CORE + || Into::::into(id) == (Into::::into(PackageId::CORE) + 1usize) + { + println!( + "compiling {id}, adding item {} to namespace id {namespace}", + global.name + ); + dbg!(&global); + } match (global.kind, global.visibility) { (global::Kind::Ty(ty), hir::Visibility::Public) => { self.scope @@ -902,7 +929,18 @@ impl GlobalTable { } (global::Kind::Namespace, hir::Visibility::Public) => { // TODO ("Verify: does this need to have its items specifically inserted?"); - self.scope.insert_or_find_namespace(global.namespace); + let namespace_id = self.scope.insert_or_find_namespace(global.namespace); + // for item in + // self.scope + // .tys + // .entry(namespace_id) + // .or_default() + // .insert(global.name.clone(), Res::Item(global.id.clone(), global.status)); + // self.scope + // .terms + // .entry(namespace_id) + // .or_default() + // .insert(global.name.clone(), Res::Item(global.id.clone(), global.status.clone())); } (_, hir::Visibility::Internal) => {} } @@ -1008,7 +1046,7 @@ fn bind_global_item( namespace_name.to_string(), decl.name.span, )) - }, + } Entry::Vacant(entry) => { entry.insert(res); } @@ -1051,7 +1089,7 @@ fn bind_global_item( namespace_name, name.span, )]) - }, + } (Entry::Vacant(term_entry), Entry::Vacant(ty_entry)) => { term_entry.insert(res); ty_entry.insert(res); @@ -1073,6 +1111,7 @@ fn decl_is_intrinsic(decl: &ast::CallableDecl) -> bool { } } +/// TODO(alex): rename namespaces to show what are candidates and what are being passed in. Document the hell out of this. fn resolve<'a>( kind: NameKind, globals: &GlobalScope, @@ -1081,21 +1120,54 @@ fn resolve<'a>( namespace_name: &Option, ) -> Result { let scopes = scopes.collect::>(); - let mut candidates = FxHashMap::default(); + let mut candidates: FxHashMap = FxHashMap::default(); let mut vars = true; let name_str = &(*name.name); - if name_str == "Baz" { + if name_str == "Fact" { dbg!(); } - let mut candidate_namespaces = vec![globals.namespaces.root_id()]; + let namespace_name_str = format!("{namespace_name:?}"); + // the order of the namespaces in this vec is the order in which they will be searched, + // and that's how the shadowing rules are determined. + let mut candidate_namespaces = vec![]; // here, we also need to check all opens to see if the namespace is in any of them - for open in scopes.iter().flat_map(|scope| scope.opens.values().flatten()) { + for open in scopes + .iter() + .flat_map(|scope| scope.opens.values().flatten()) + { // insert each open into the list of places to check for this item candidate_namespaces.push(open.namespace); } + // add prelude to the list of candidate namespaces last, as they are the final fallback for a symbol + for prelude_namespace in PRELUDE { + candidate_namespaces.push( + globals + .namespaces + .find_namespace( + prelude_namespace + .into_iter() + .map(|x| -> Rc { Rc::from(*x) }) + .collect::>(), + ) + .expect("prelude namespaces should exist") + ); + } + // the top-level (root) namespace is the last priority to check. + candidate_namespaces.push(globals.namespaces.root_id()); + // FOR DEBUGGING + let mut str_buf = String::from("Going to search through the following namespaces: "); + for candidate_namespace in &candidate_namespaces { + let candidate_namespace = globals.namespaces.find_id(&candidate_namespace).0; + str_buf.push_str(&format!("{}", candidate_namespace.join("."))); + } + let str_buf = str_buf; + // END FOR DEBUGGING // search through each candidate namespace to find the items - for candidate_namespace in candidate_namespaces { - let candidate_namespace = globals.namespaces.find_id(&candidate_namespace).1; + for candidate_namespace_id in candidate_namespaces { + let debug_str_currently_searching = format!( + "Currently searching through namespace: {}", globals.namespaces.find_id(&candidate_namespace_id).0.join(".") + ); + let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; let namespace = if let Some(namespace) = namespace_name { let res = candidate_namespace.find_namespace(namespace); let res_2 = res.is_some(); @@ -1104,8 +1176,14 @@ fn resolve<'a>( } else { None }; + if name_str == "Fact" { + dbg!(); + } + + if let Some(res) = globals.get(kind, candidate_namespace_id, name_str) { + return Ok(res.clone()); + } - // let namespace = namespace.as_ref().map_or("", |i| &i.name); for scope in &scopes { if namespace.is_none() { if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, name_str) { @@ -1114,19 +1192,11 @@ fn resolve<'a>( } } - if let Some(namespace) = namespace { - if let Some(namespaces) = scope.opens.get(&namespace) { - candidates = resolve_explicit_opens(kind, globals, namespaces, name_str); - if !candidates.is_empty() { - // Explicit opens shadow prelude and unopened globals. - break; - } - } - if let Some(res) = globals.get(kind, namespace, name_str) { - return Ok(res.clone()); - // candidates.insert(candidate., candidate_namespace); - } - } + // candidates = resolve_explicit_opens(kind, globals, candidate_namespace, name_str); + // if !candidates.is_empty() { + // // Explicit opens shadow prelude and unopened globals. + // break; + // } if scope.kind == ScopeKind::Callable { // Since local callables are not closures, hide local variables in parent scopes. @@ -1134,11 +1204,24 @@ fn resolve<'a>( } } + // // check this candidate namespace's top level items + // if let Some(res) = globals.get(kind, candidate_namespace_id, name_str) { + // candidates.insert(res.clone(), open.unwrap_or_else(|| + // Open { namespace: candidate_namespace_id, span: Default::default() },)); + // } } if candidates.is_empty() && namespace_name.is_none() { // Prelude shadows unopened globals. - let candidates = resolve_implicit_opens(kind, globals, PRELUDE.into_iter().map(|x| x.into_iter().map(|x| Rc::from(*x)).collect::>()).collect::>(), name_str); + let candidates = resolve_implicit_opens( + kind, + globals, + PRELUDE + .into_iter() + .map(|x| x.into_iter().map(|x| Rc::from(*x)).collect::>()) + .collect::>(), + name_str, + ); if candidates.len() > 1 { // If there are multiple candidates, sort them by namespace and return an error. let mut candidates: Vec<_> = candidates.into_iter().collect(); @@ -1227,6 +1310,8 @@ fn resolve_scope_locals( name: &str, ) -> Option { if vars { + // TODO(alex) these should have all the declarations + let debug_str = format!("{:?}", scope.terms); match kind { NameKind::Term => { if let Some(&(_, id)) = scope.vars.get(name) { @@ -1300,7 +1385,10 @@ fn resolve_implicit_opens<'b>( ) -> FxHashMap { let mut candidates = FxHashMap::default(); for namespace in namespaces { - let namespace_id = globals.find_namespace(namespace).expect("prelude should exist"); + let namespace_id = globals + .find_namespace(namespace) + .expect("prelude should exist"); + let debug_str = format!("{:?}", globals.terms.get(&namespace_id)); if let Some(&res) = globals.get(kind, namespace_id, name) { candidates.insert(res, namespace_id); } diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index 3dae96cd11..5a0a7dfc5a 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -15,6 +15,7 @@ use qsc_data_structures::{ use rustc_hash::FxHashMap; use std::{cell::RefCell, collections::HashMap, rc::Rc}; +#[derive(Debug)] pub struct Global { pub namespace: Vec>, pub name: Rc, @@ -29,6 +30,17 @@ pub enum Kind { Term(Term), } +impl std::fmt::Debug for Kind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Kind::Namespace => write!(f, "Namespace"), + Kind::Ty(ty) => write!(f, "Ty({})", ty.id), + Kind::Term(term) => write!(f, "Term({})", term.id), + } + } + +} + pub struct Ty { pub id: ItemId, } From 1a3c8381a8e5d280740698c49e2963aafcee519f Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 1 Apr 2024 21:17:04 -0700 Subject: [PATCH 46/76] remove lots of debug cruft --- .../qsc_data_structures/src/namespaces.rs | 4 +- compiler/qsc_frontend/src/resolve.rs | 96 ++++--------------- language_service/src/completion.rs | 3 +- 3 files changed, 21 insertions(+), 82 deletions(-) diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index 68f16bfbff..211ca4cb7e 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -3,10 +3,12 @@ mod tests; use std::{cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; -const PRELUDE: [[&str; 3]; 3] = [ +const PRELUDE: [[&str; 3]; 4] = [ ["Microsoft", "Quantum", "Canon"], ["Microsoft", "Quantum", "Core"], ["Microsoft", "Quantum", "Intrinsic"], + ["Microsoft", "Quantum", "Measurement"], + ]; /// An ID that corresponds to a namespace in the global scope. diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index f5c1717573..faf51b29b1 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -24,9 +24,7 @@ use qsc_hir::{ }; use rustc_hash::{FxHashMap, FxHashSet}; use std::{ - cell::RefCell, - collections::{hash_map::Entry, HashMap}, - fmt::Display, + collections::{hash_map::Entry}, rc::Rc, str::FromStr, vec, @@ -35,10 +33,12 @@ use thiserror::Error; use crate::compile::preprocess::TrackedName; -const PRELUDE: [&[&str; 3]; 3] = [ +// TODO dedup this? +const PRELUDE: [&[&str; 3]; 4] = [ &["Microsoft", "Quantum", "Canon"], &["Microsoft", "Quantum", "Core"], &["Microsoft", "Quantum", "Intrinsic"], + &["Microsoft", "Quantum", "Measurement"], ]; // All AST Path nodes get mapped @@ -382,7 +382,7 @@ impl Resolver { pub(super) fn bind_fragments(&mut self, ast: &ast::Package, assigner: &mut Assigner) { for node in &mut ast.nodes.iter() { match node { - ast::TopLevelNode::Namespace(namespace) => { + TopLevelNode::Namespace(namespace) => { bind_global_items( &mut self.names, &mut self.globals, @@ -391,7 +391,7 @@ impl Resolver { &mut self.errors, ); } - ast::TopLevelNode::Stmt(stmt) => { + TopLevelNode::Stmt(stmt) => { if let ast::StmtKind::Item(item) = stmt.kind.as_ref() { self.bind_local_item(assigner, item); } @@ -428,12 +428,6 @@ impl Resolver { let name = &path.name; let namespace = &path.namespace; - if name.name.to_string() == "Fact" { - dbg!(); - } - let x = format!("{name}"); - let namespace_string = format!("{namespace:?}"); - match resolve( kind, &self.globals, @@ -511,9 +505,9 @@ impl Resolver { } } - fn bind_open(&mut self, name: &ast::VecIdent, alias: &Option>) { + fn bind_open(&mut self, name: &VecIdent, alias: &Option>) { // TODO figure out how aliases are going to work - let id = match self.globals.find_namespace(dbg!(name)) { + let id = match self.globals.find_namespace(name) { Some(id) => id, None => { self.errors.push(Error::NotFound( @@ -684,7 +678,7 @@ impl AstVisitor<'_> for With<'_> { } } - fn visit_callable_decl(&mut self, decl: &ast::CallableDecl) { + fn visit_callable_decl(&mut self, decl: &CallableDecl) { fn collect_param_names(pat: &ast::Pat, names: &mut FxHashSet>) { match &*pat.kind { ast::PatKind::Bind(name, _) => { @@ -711,7 +705,7 @@ impl AstVisitor<'_> for With<'_> { } fn visit_spec_decl(&mut self, decl: &ast::SpecDecl) { - if let ast::SpecBody::Impl(input, block) = &decl.body { + if let SpecBody::Impl(input, block) = &decl.body { self.with_spec_pat(block.span, ScopeKind::Block, input, |visitor| { visitor.visit_block(block); }); @@ -858,21 +852,6 @@ impl GlobalTable { let mut errors = Vec::new(); for node in &*package.nodes { match node { - // if a namespace is nested, create child namespaces - TopLevelNode::Namespace(namespace) => { - let namespace_id = self - .scope - .namespaces - .insert_or_find_namespace(Into::>::into(namespace.name.clone())); - - bind_global_items( - &mut self.names, - &mut self.scope, - namespace, - assigner, - &mut errors, - ); - } TopLevelNode::Namespace(namespace) => { bind_global_items( &mut self.names, @@ -898,15 +877,7 @@ impl GlobalTable { let namespace = self .scope .insert_or_find_namespace(global.namespace.clone()); - if id == PackageId::CORE - || Into::::into(id) == (Into::::into(PackageId::CORE) + 1usize) - { - println!( - "compiling {id}, adding item {} to namespace id {namespace}", - global.name - ); - dbg!(&global); - } + match (global.kind, global.visibility) { (global::Kind::Ty(ty), hir::Visibility::Public) => { self.scope @@ -928,19 +899,7 @@ impl GlobalTable { } } (global::Kind::Namespace, hir::Visibility::Public) => { - // TODO ("Verify: does this need to have its items specifically inserted?"); - let namespace_id = self.scope.insert_or_find_namespace(global.namespace); - // for item in - // self.scope - // .tys - // .entry(namespace_id) - // .or_default() - // .insert(global.name.clone(), Res::Item(global.id.clone(), global.status)); - // self.scope - // .terms - // .entry(namespace_id) - // .or_default() - // .insert(global.name.clone(), Res::Item(global.id.clone(), global.status.clone())); + self.scope.insert_or_find_namespace(global.namespace); } (_, hir::Visibility::Internal) => {} } @@ -948,7 +907,7 @@ impl GlobalTable { } } -/// Given some namespace `namespace`, add all the globals declared within it to the global sc4pe. +/// Given some namespace `namespace`, add all the globals declared within it to the global scope. fn bind_global_items( names: &mut IndexMap, scope: &mut GlobalScope, @@ -1101,7 +1060,7 @@ fn bind_global_item( } } -fn decl_is_intrinsic(decl: &ast::CallableDecl) -> bool { +fn decl_is_intrinsic(decl: &CallableDecl) -> bool { if let CallableBody::Specs(specs) = decl.body.as_ref() { specs .iter() @@ -1123,10 +1082,6 @@ fn resolve<'a>( let mut candidates: FxHashMap = FxHashMap::default(); let mut vars = true; let name_str = &(*name.name); - if name_str == "Fact" { - dbg!(); - } - let namespace_name_str = format!("{namespace_name:?}"); // the order of the namespaces in this vec is the order in which they will be searched, // and that's how the shadowing rules are determined. let mut candidate_namespaces = vec![]; @@ -1154,31 +1109,15 @@ fn resolve<'a>( } // the top-level (root) namespace is the last priority to check. candidate_namespaces.push(globals.namespaces.root_id()); - // FOR DEBUGGING - let mut str_buf = String::from("Going to search through the following namespaces: "); - for candidate_namespace in &candidate_namespaces { - let candidate_namespace = globals.namespaces.find_id(&candidate_namespace).0; - str_buf.push_str(&format!("{}", candidate_namespace.join("."))); - } - let str_buf = str_buf; - // END FOR DEBUGGING + // search through each candidate namespace to find the items for candidate_namespace_id in candidate_namespaces { - let debug_str_currently_searching = format!( - "Currently searching through namespace: {}", globals.namespaces.find_id(&candidate_namespace_id).0.join(".") - ); let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; let namespace = if let Some(namespace) = namespace_name { - let res = candidate_namespace.find_namespace(namespace); - let res_2 = res.is_some(); - let stringname = format!("{namespace}"); - res + candidate_namespace.find_namespace(namespace) } else { None }; - if name_str == "Fact" { - dbg!(); - } if let Some(res) = globals.get(kind, candidate_namespace_id, name_str) { return Ok(res.clone()); @@ -1310,8 +1249,6 @@ fn resolve_scope_locals( name: &str, ) -> Option { if vars { - // TODO(alex) these should have all the declarations - let debug_str = format!("{:?}", scope.terms); match kind { NameKind::Term => { if let Some(&(_, id)) = scope.vars.get(name) { @@ -1388,7 +1325,6 @@ fn resolve_implicit_opens<'b>( let namespace_id = globals .find_namespace(namespace) .expect("prelude should exist"); - let debug_str = format!("{:?}", globals.terms.get(&namespace_id)); if let Some(&res) = globals.get(kind, namespace_id, name) { candidates.insert(res, namespace_id); } diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index 49aba8be33..794f87b66d 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -20,10 +20,11 @@ use rustc_hash::FxHashSet; use std::collections::HashMap; use std::rc::Rc; -const PRELUDE: [[&str; 3]; 3] = [ +const PRELUDE: [[&str; 3]; 4] = [ ["Microsoft", "Quantum", "Canon"], ["Microsoft", "Quantum", "Core"], ["Microsoft", "Quantum", "Intrinsic"], + ["Microsoft", "Quantum", "Measurement"], ]; pub(crate) fn get_completions( From 5f0d8bad4823d47ed75f25dacfa37b57371940aa Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 1 Apr 2024 21:25:21 -0700 Subject: [PATCH 47/76] fmt and add hierarchical namespace resolution back in --- compiler/qsc_ast/src/ast.rs | 5 +-- compiler/qsc_data_structures/src/lib.rs | 2 +- .../qsc_data_structures/src/namespaces.rs | 4 +- .../src/namespaces/tests.rs | 15 +++---- compiler/qsc_fir/src/fir.rs | 5 +-- compiler/qsc_frontend/src/resolve.rs | 42 ++++++++----------- compiler/qsc_frontend/src/resolve/tests.rs | 3 +- compiler/qsc_hir/src/global.rs | 1 - compiler/qsc_hir/src/hir.rs | 5 +-- compiler/qsc_parse/src/prim.rs | 24 +++++------ compiler/qsc_parse/src/prim/tests.rs | 4 +- language_service/src/completion.rs | 5 ++- 12 files changed, 51 insertions(+), 64 deletions(-) diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index eecb31ed20..c47aa127ef 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -1339,14 +1339,13 @@ impl Display for VecIdent { let mut buf = Vec::with_capacity(self.0.len()); for ident in self.0.iter() { - buf.push(format!("{}", ident)); + buf.push(format!("{}", ident)); } if buf.len() > 1 { // use square brackets only if there are more than one ident - write!(f, "[{}]", buf.join(", ")) + write!(f, "[{}]", buf.join(", ")) } else { write!(f, "{}", buf[0]) - } } } diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index 8e980c052b..8b8e4520f4 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -6,5 +6,5 @@ pub mod functors; pub mod index_map; pub mod language_features; pub mod line_column; -pub mod span; pub mod namespaces; +pub mod span; diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index 211ca4cb7e..0ff9e3d372 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -8,7 +8,6 @@ const PRELUDE: [[&str; 3]; 4] = [ ["Microsoft", "Quantum", "Core"], ["Microsoft", "Quantum", "Intrinsic"], ["Microsoft", "Quantum", "Measurement"], - ]; /// An ID that corresponds to a namespace in the global scope. @@ -16,7 +15,8 @@ const PRELUDE: [[&str; 3]; 4] = [ pub struct NamespaceId(usize); impl NamespaceId { /// Create a new namespace ID. - #[must_use] pub fn new(value: usize) -> Self { + #[must_use] + pub fn new(value: usize) -> Self { Self(value) } } diff --git a/compiler/qsc_data_structures/src/namespaces/tests.rs b/compiler/qsc_data_structures/src/namespaces/tests.rs index 325b9eaf7c..fa2eeb9fbc 100644 --- a/compiler/qsc_data_structures/src/namespaces/tests.rs +++ b/compiler/qsc_data_structures/src/namespaces/tests.rs @@ -9,8 +9,7 @@ fn test_tree_construction() { for i in 0..10 { for j in 'a'..'d' { root.insert_or_find_namespace( - vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] - .into_iter(), + vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter(), ); } } @@ -285,12 +284,9 @@ fn test_find_id() { let mut id_buf = vec![]; for i in 0..10 { for j in 'a'..'d' { - id_buf.push( - root.insert_or_find_namespace( - vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] - .into_iter(), - ), - ); + id_buf.push(root.insert_or_find_namespace( + vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter(), + )); } } let mut result_buf = vec![]; @@ -671,8 +667,7 @@ fn test_insert_or_find_namespace() { for i in 0..10 { for j in 'a'..'d' { let id = root.insert_or_find_namespace( - vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))] - .into_iter(), + vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter(), ); ids.push(id.into()); } diff --git a/compiler/qsc_fir/src/fir.rs b/compiler/qsc_fir/src/fir.rs index 3fc4436088..340d57b3d0 100644 --- a/compiler/qsc_fir/src/fir.rs +++ b/compiler/qsc_fir/src/fir.rs @@ -1461,14 +1461,13 @@ impl Display for VecIdent { let mut buf = Vec::with_capacity(self.0.len()); for ident in self.0.iter() { - buf.push(format!("{}", ident)); + buf.push(format!("{}", ident)); } if buf.len() > 1 { // use square brackets only if there are more than one ident - write!(f, "[{}]", buf.join(", ")) + write!(f, "[{}]", buf.join(", ")) } else { write!(f, "{}", buf[0]) - } } } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index faf51b29b1..1613dcbfa1 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -23,12 +23,7 @@ use qsc_hir::{ ty::{ParamId, Prim}, }; use rustc_hash::{FxHashMap, FxHashSet}; -use std::{ - collections::{hash_map::Entry}, - rc::Rc, - str::FromStr, - vec, -}; +use std::{collections::hash_map::Entry, rc::Rc, str::FromStr, vec}; use thiserror::Error; use crate::compile::preprocess::TrackedName; @@ -899,7 +894,7 @@ impl GlobalTable { } } (global::Kind::Namespace, hir::Visibility::Public) => { - self.scope.insert_or_find_namespace(global.namespace); + self.scope.insert_or_find_namespace(global.namespace); } (_, hir::Visibility::Internal) => {} } @@ -1075,13 +1070,13 @@ fn resolve<'a>( kind: NameKind, globals: &GlobalScope, scopes: impl Iterator, - name: &Ident, - namespace_name: &Option, + provided_symbol_name: &Ident, + provided_namespace_name: &Option, ) -> Result { let scopes = scopes.collect::>(); - let mut candidates: FxHashMap = FxHashMap::default(); + let mut candidates: FxHashMap = FxHashMap::default(); let mut vars = true; - let name_str = &(*name.name); + let name_str = &(*provided_symbol_name.name); // the order of the namespaces in this vec is the order in which they will be searched, // and that's how the shadowing rules are determined. let mut candidate_namespaces = vec![]; @@ -1104,7 +1099,7 @@ fn resolve<'a>( .map(|x| -> Rc { Rc::from(*x) }) .collect::>(), ) - .expect("prelude namespaces should exist") + .expect("prelude namespaces should exist"), ); } // the top-level (root) namespace is the last priority to check. @@ -1113,7 +1108,7 @@ fn resolve<'a>( // search through each candidate namespace to find the items for candidate_namespace_id in candidate_namespaces { let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; - let namespace = if let Some(namespace) = namespace_name { + let namespace = if let Some(namespace) = provided_namespace_name { candidate_namespace.find_namespace(namespace) } else { None @@ -1130,12 +1125,11 @@ fn resolve<'a>( return Ok(res); } } - - // candidates = resolve_explicit_opens(kind, globals, candidate_namespace, name_str); - // if !candidates.is_empty() { - // // Explicit opens shadow prelude and unopened globals. - // break; - // } + if let Some(namespace) = namespace { + if let Some(res) = globals.get(kind, namespace, name_str) { + return Ok(res.clone()); + } + } if scope.kind == ScopeKind::Callable { // Since local callables are not closures, hide local variables in parent scopes. @@ -1150,7 +1144,7 @@ fn resolve<'a>( // } } - if candidates.is_empty() && namespace_name.is_none() { + if candidates.is_empty() && provided_namespace_name.is_none() { // Prelude shadows unopened globals. let candidates = resolve_implicit_opens( kind, @@ -1175,8 +1169,8 @@ fn resolve<'a>( .next() .expect("infallible as per length check above"); return Err(Error::AmbiguousPrelude { - span: name.span, - name: name.name.to_string(), + span: provided_symbol_name.span, + name: provided_symbol_name.name.to_string(), candidate_a, candidate_b, }); @@ -1223,13 +1217,13 @@ fn resolve<'a>( name: name_str.to_string(), first_open: first_open_ns.join("."), second_open: second_open_ns.join("."), - name_span: name.span, + name_span: provided_symbol_name.span, first_open_span: opens[0].span, second_open_span: opens[1].span, }) } else { single(candidates.into_keys()) - .ok_or_else(|| Error::NotFound(name_str.to_string(), name.span)) + .ok_or_else(|| Error::NotFound(name_str.to_string(), provided_symbol_name.span)) } } diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index a6622f12e9..c1f9c8db65 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2728,5 +2728,4 @@ fn basic_hierarchical_namespace() { } }"#]], ); - -} \ No newline at end of file +} diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index 5a0a7dfc5a..0a1532b1f7 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -38,7 +38,6 @@ impl std::fmt::Debug for Kind { Kind::Term(term) => write!(f, "Term({})", term.id), } } - } pub struct Ty { diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index fdd7d22a67..8ea884e4cf 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -1173,14 +1173,13 @@ impl Display for VecIdent { let mut buf = Vec::with_capacity(self.0.len()); for ident in self.0.iter() { - buf.push(format!("{}", ident)); + buf.push(format!("{}", ident)); } if buf.len() > 1 { // use square brackets only if there are more than one ident - write!(f, "[{}]", buf.join(", ")) + write!(f, "[{}]", buf.join(", ")) } else { write!(f, "{}", buf[0]) - } } } diff --git a/compiler/qsc_parse/src/prim.rs b/compiler/qsc_parse/src/prim.rs index 79880035b9..5cbc35ca29 100644 --- a/compiler/qsc_parse/src/prim.rs +++ b/compiler/qsc_parse/src/prim.rs @@ -89,19 +89,17 @@ pub(super) fn path(s: &mut ParserContext) -> Result> { let name = parts.pop().expect("path should have at least one part"); let namespace = match (parts.first(), parts.last()) { - (Some(_), Some(_)) => { - Some( - parts - .iter() - .map(|part| Ident { - id: NodeId::default(), - span: part.span, - name: part.name.clone(), - }) - .collect::>() - .into(), - ) - } + (Some(_), Some(_)) => Some( + parts + .iter() + .map(|part| Ident { + id: NodeId::default(), + span: part.span, + name: part.name.clone(), + }) + .collect::>() + .into(), + ), _ => None, }; diff --git a/compiler/qsc_parse/src/prim/tests.rs b/compiler/qsc_parse/src/prim/tests.rs index 876dd2106d..870daee91a 100644 --- a/compiler/qsc_parse/src/prim/tests.rs +++ b/compiler/qsc_parse/src/prim/tests.rs @@ -100,7 +100,9 @@ fn path_triple() { check( path, "Foo.Bar.Baz", - &expect![[r#"Path _id_ [0-11] ([Ident _id_ [0-3] "Foo", Ident _id_ [4-7] "Bar"]) (Ident _id_ [8-11] "Baz")"#]], + &expect![[ + r#"Path _id_ [0-11] ([Ident _id_ [0-3] "Foo", Ident _id_ [4-7] "Bar"]) (Ident _id_ [8-11] "Baz")"# + ]], ); } diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index 794f87b66d..582740e24b 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -576,7 +576,10 @@ fn convert_ast_namespaces_into_hir_namespaces( hir_namespaces.insert(namespace.clone(), children); } - qsc::NamespaceTreeRoot::new_from_parts(assigner, NamespaceTreeNode::new(root_id, hir_namespaces)) + qsc::NamespaceTreeRoot::new_from_parts( + assigner, + NamespaceTreeNode::new(root_id, hir_namespaces), + ) } /// Convert a local into a completion item From 149070e1e9874a799ccbf191e4f0793ddf9f184c Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 2 Apr 2024 14:39:34 -0700 Subject: [PATCH 48/76] fix interpolation tests --- compiler/qsc_ast/src/visit.rs | 2 - compiler/qsc_frontend/src/compile.rs | 1 - compiler/qsc_frontend/src/resolve.rs | 120 +++++++++++----------- compiler/qsc_frontend/src/typeck/tests.rs | 2 +- 4 files changed, 62 insertions(+), 63 deletions(-) diff --git a/compiler/qsc_ast/src/visit.rs b/compiler/qsc_ast/src/visit.rs index e67d127de2..0fb9e4cbcb 100644 --- a/compiler/qsc_ast/src/visit.rs +++ b/compiler/qsc_ast/src/visit.rs @@ -87,12 +87,10 @@ pub fn walk_package<'a>(vis: &mut impl Visitor<'a>, package: &'a Package) { pub fn walk_namespace<'a>(vis: &mut impl Visitor<'a>, namespace: &'a Namespace) { // possibly this visit is incorrect? vis.visit_vec_ident(&namespace.name); - // TODO(alex) HERE is where the errors are coming from -- namespace.items.iter().for_each(|i| vis.visit_item(i)); } pub fn walk_item<'a>(vis: &mut impl Visitor<'a>, item: &'a Item) { - // TODO figure out where in here the errors are coming from item.attrs.iter().for_each(|a| vis.visit_attr(a)); item.visibility.iter().for_each(|v| vis.visit_visibility(v)); match &*item.kind { diff --git a/compiler/qsc_frontend/src/compile.rs b/compiler/qsc_frontend/src/compile.rs index 9fccc6fa96..70b930c928 100644 --- a/compiler/qsc_frontend/src/compile.rs +++ b/compiler/qsc_frontend/src/compile.rs @@ -509,7 +509,6 @@ fn resolve_all( let mut errors = globals.add_local_package(assigner, package); let mut resolver = Resolver::new(globals, dropped_names); - // TODO(alex) no this is where they are coming from resolver.with(assigner).visit_package(package); let (names, locals, mut resolver_errors, namespaces) = resolver.into_result(); errors.append(&mut resolver_errors); diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 1613dcbfa1..eed9b91521 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -652,7 +652,7 @@ impl AstVisitor<'_> for With<'_> { .resolver .globals .find_namespace(Into::>::into(namespace.name.clone())) - .expect("namespace should exist"); + .expect("namespace should exist by this point"); let kind = ScopeKind::Namespace(ns); self.with_scope(namespace.span, kind, |visitor| { visitor.resolver.bind_open(&namespace.name, &None); @@ -661,7 +661,6 @@ impl AstVisitor<'_> for With<'_> { visitor.resolver.bind_open(name, alias); } } - // errors show up in visitor in the below function ast_visit::walk_namespace(visitor, namespace); }); } @@ -1076,72 +1075,55 @@ fn resolve<'a>( let scopes = scopes.collect::>(); let mut candidates: FxHashMap = FxHashMap::default(); let mut vars = true; - let name_str = &(*provided_symbol_name.name); + let provided_symbol_str = &(*provided_symbol_name.name); + if let Some(provided_namespace_name) = provided_namespace_name { + if provided_namespace_name.0.get(0).map(|x| x.name.to_string()) == Some(String::from("A")) { + dbg!() + } + }; + if provided_symbol_str == "Bar" { + dbg!(); + } // the order of the namespaces in this vec is the order in which they will be searched, // and that's how the shadowing rules are determined. - let mut candidate_namespaces = vec![]; - // here, we also need to check all opens to see if the namespace is in any of them - for open in scopes - .iter() - .flat_map(|scope| scope.opens.values().flatten()) - { - // insert each open into the list of places to check for this item - candidate_namespaces.push(open.namespace); - } - // add prelude to the list of candidate namespaces last, as they are the final fallback for a symbol - for prelude_namespace in PRELUDE { - candidate_namespaces.push( - globals - .namespaces - .find_namespace( - prelude_namespace - .into_iter() - .map(|x| -> Rc { Rc::from(*x) }) - .collect::>(), - ) - .expect("prelude namespaces should exist"), - ); - } - // the top-level (root) namespace is the last priority to check. - candidate_namespaces.push(globals.namespaces.root_id()); + let candidate_namespaces = calculate_candidate_namespaces(globals, &scopes); // search through each candidate namespace to find the items for candidate_namespace_id in candidate_namespaces { - let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; + let (candidate_namespace_str, candidate_namespace) = + globals.namespaces.find_id(&candidate_namespace_id); let namespace = if let Some(namespace) = provided_namespace_name { - candidate_namespace.find_namespace(namespace) + if let Some(namespace) = candidate_namespace.find_namespace(namespace) { + if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { + return Ok(res.clone()); + } + Some(namespace) + } else { + None + } } else { None }; - if let Some(res) = globals.get(kind, candidate_namespace_id, name_str) { + if let Some(res) = globals.get(kind, candidate_namespace_id, provided_symbol_str) { return Ok(res.clone()); } for scope in &scopes { if namespace.is_none() { - if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, name_str) { + if let Some(res) = + resolve_scope_locals(kind, globals, scope, vars, provided_symbol_str) + { // Local declarations shadow everything. return Ok(res); } } - if let Some(namespace) = namespace { - if let Some(res) = globals.get(kind, namespace, name_str) { - return Ok(res.clone()); - } - } if scope.kind == ScopeKind::Callable { // Since local callables are not closures, hide local variables in parent scopes. vars = false; } } - - // // check this candidate namespace's top level items - // if let Some(res) = globals.get(kind, candidate_namespace_id, name_str) { - // candidates.insert(res.clone(), open.unwrap_or_else(|| - // Open { namespace: candidate_namespace_id, span: Default::default() },)); - // } } if candidates.is_empty() && provided_namespace_name.is_none() { @@ -1153,7 +1135,7 @@ fn resolve<'a>( .into_iter() .map(|x| x.into_iter().map(|x| Rc::from(*x)).collect::>()) .collect::>(), - name_str, + provided_symbol_str, ); if candidates.len() > 1 { // If there are multiple candidates, sort them by namespace and return an error. @@ -1181,18 +1163,6 @@ fn resolve<'a>( } } - // TODO - // if candidates.is_empty() { - // if let Some(&res) = globals.get( - // kind, - // namespace.unwrap_or_else(|| globals.namespaces.root_id()), - // name_str, - // ) { - // // An unopened global is the last resort. - // return Ok(res); - // } - // } - if candidates.len() > 1 { // If there are multiple candidates, remove unimplemented items. This allows resolution to // succeed in cases where both an older, unimplemented API and newer, implemented API with the @@ -1214,7 +1184,7 @@ fn resolve<'a>( let (first_open_ns, _) = globals.namespaces.find_id(&opens[0].namespace); let (second_open_ns, _) = globals.namespaces.find_id(&opens[1].namespace); Err(Error::Ambiguous { - name: name_str.to_string(), + name: provided_symbol_str.to_string(), first_open: first_open_ns.join("."), second_open: second_open_ns.join("."), name_span: provided_symbol_name.span, @@ -1222,9 +1192,41 @@ fn resolve<'a>( second_open_span: opens[1].span, }) } else { - single(candidates.into_keys()) - .ok_or_else(|| Error::NotFound(name_str.to_string(), provided_symbol_name.span)) + single(candidates.into_keys()).ok_or_else(|| { + Error::NotFound(provided_symbol_str.to_string(), provided_symbol_name.span) + }) + } +} + +/// Given a list of scopes, and the global scope, calculate an ordered list of the namespaces to check +/// for a symbol. +fn calculate_candidate_namespaces(globals: &GlobalScope, scopes: &Vec<&Scope>) -> Vec { + let mut candidate_namespaces = vec![]; + // here, we also need to check all opens to see if the namespace is in any of them + for open in scopes + .iter() + .flat_map(|scope| scope.opens.values().flatten()) + { + // insert each open into the list of places to check for this item + candidate_namespaces.push(open.namespace); } + // add prelude to the list of candidate namespaces last, as they are the final fallback for a symbol + for prelude_namespace in PRELUDE { + candidate_namespaces.push( + globals + .namespaces + .find_namespace( + prelude_namespace + .into_iter() + .map(|x| -> Rc { Rc::from(*x) }) + .collect::>(), + ) + .expect("prelude namespaces should exist"), + ); + } + // the top-level (root) namespace is the last priority to check. + candidate_namespaces.push(globals.namespaces.root_id()); + candidate_namespaces } /// Implements shadowing rules within a single scope. diff --git a/compiler/qsc_frontend/src/typeck/tests.rs b/compiler/qsc_frontend/src/typeck/tests.rs index bdafc29a75..c18d6ccb08 100644 --- a/compiler/qsc_frontend/src/typeck/tests.rs +++ b/compiler/qsc_frontend/src/typeck/tests.rs @@ -86,11 +86,11 @@ fn compile(input: &str, entry_expr: &str) -> (Package, super::Table, Vec Date: Tue, 2 Apr 2024 14:42:15 -0700 Subject: [PATCH 49/76] simplify beefy part of resolve function --- compiler/qsc_frontend/src/resolve.rs | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index eed9b91521..dc26e692f9 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1076,41 +1076,26 @@ fn resolve<'a>( let mut candidates: FxHashMap = FxHashMap::default(); let mut vars = true; let provided_symbol_str = &(*provided_symbol_name.name); - if let Some(provided_namespace_name) = provided_namespace_name { - if provided_namespace_name.0.get(0).map(|x| x.name.to_string()) == Some(String::from("A")) { - dbg!() - } - }; - if provided_symbol_str == "Bar" { - dbg!(); - } // the order of the namespaces in this vec is the order in which they will be searched, // and that's how the shadowing rules are determined. let candidate_namespaces = calculate_candidate_namespaces(globals, &scopes); - // search through each candidate namespace to find the items - for candidate_namespace_id in candidate_namespaces { - let (candidate_namespace_str, candidate_namespace) = - globals.namespaces.find_id(&candidate_namespace_id); - let namespace = if let Some(namespace) = provided_namespace_name { - if let Some(namespace) = candidate_namespace.find_namespace(namespace) { +// search through each candidate namespace to find the items + for candidate_namespace_id in candidate_namespaces { + let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; + + if let Some(namespace) = provided_namespace_name.as_ref().and_then(|ns| candidate_namespace.find_namespace(ns)) { if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { return Ok(res.clone()); } - Some(namespace) - } else { - None } - } else { - None - }; if let Some(res) = globals.get(kind, candidate_namespace_id, provided_symbol_str) { return Ok(res.clone()); } for scope in &scopes { - if namespace.is_none() { + if provided_namespace_name.is_none() { if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, provided_symbol_str) { From 69a51377b54428308163a7d9fc773d9e2e4bf344 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 2 Apr 2024 15:23:45 -0700 Subject: [PATCH 50/76] fix renamer for namespaces --- .../qsc_frontend/src/incremental/tests.rs | 22 +- compiler/qsc_frontend/src/resolve.rs | 6 + compiler/qsc_frontend/src/resolve/tests.rs | 353 ++++++++++-------- compiler/qsc_frontend/src/typeck/tests.rs | 88 ++--- 4 files changed, 260 insertions(+), 209 deletions(-) diff --git a/compiler/qsc_frontend/src/incremental/tests.rs b/compiler/qsc_frontend/src/incremental/tests.rs index d9ea8a47ff..2d5053230d 100644 --- a/compiler/qsc_frontend/src/incremental/tests.rs +++ b/compiler/qsc_frontend/src/incremental/tests.rs @@ -43,7 +43,7 @@ fn one_callable() { output: Type 7 [35-39]: Path: Path 8 [35-39] (Ident 9 [35-39] "Unit") body: Block: Block 10 [40-42]: names: - node_id:2,node_id:5,node_id:8, + node_id:1,node_id:5,node_id:8, terms: node_id:6,node_id:10, locals: @@ -67,9 +67,25 @@ fn one_callable() { hi: 44, }, kind: Namespace( - "Foo", + NamespaceId( + 9, + ), ), - opens: {}, + opens: { + NamespaceId( + 9, + ): [ + Open { + namespace: NamespaceId( + 9, + ), + span: Span { + lo: 10, + hi: 13, + }, + }, + ], + }, tys: {}, terms: {}, vars: {}, diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index dc26e692f9..33156c2cc3 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -25,6 +25,7 @@ use qsc_hir::{ use rustc_hash::{FxHashMap, FxHashSet}; use std::{collections::hash_map::Entry, rc::Rc, str::FromStr, vec}; use thiserror::Error; +use qsc_data_structures::namespaces::NamespaceTreeNode; use crate::compile::preprocess::TrackedName; @@ -653,8 +654,12 @@ impl AstVisitor<'_> for With<'_> { .globals .find_namespace(Into::>::into(namespace.name.clone())) .expect("namespace should exist by this point"); + let kind = ScopeKind::Namespace(ns); self.with_scope(namespace.span, kind, |visitor| { + // the below line ensures that this namespace opens itself, in case + // we are re-opening a namespace. This is important, as without this, + // a re-opened namespace would only have knowledge of its scopes. visitor.resolver.bind_open(&namespace.name, &None); for item in &*namespace.items { if let ast::ItemKind::Open(name, alias) = &*item.kind { @@ -663,6 +668,7 @@ impl AstVisitor<'_> for With<'_> { } ast_visit::walk_namespace(visitor, namespace); }); + } fn visit_attr(&mut self, attr: &ast::Attr) { diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index c1f9c8db65..f350aedd06 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -20,31 +20,55 @@ use qsc_ast::{ use qsc_data_structures::{language_features::LanguageFeatures, span::Span}; use qsc_hir::assigner::Assigner as HirAssigner; use std::fmt::Write; +use qsc_ast::ast::VecIdent; +use qsc_data_structures::namespaces::{NamespaceId, NamespaceTreeRoot}; + +enum Change { + Res(Res), + NamespaceId(NamespaceId), +} + +impl From for Change { + fn from(res: Res) -> Self { + Self::Res(res) + } +} + +impl From for Change { + fn from(ns_id: NamespaceId) -> Self { + Self::NamespaceId(ns_id) + } +} struct Renamer<'a> { names: &'a Names, - changes: Vec<(Span, Res)>, + changes: Vec<(Span, Change)>, + namespaces: NamespaceTreeRoot, } impl<'a> Renamer<'a> { - fn new(names: &'a Names) -> Self { + fn new(names: &'a Names, namespaces: NamespaceTreeRoot,) -> Self { Self { names, changes: Vec::new(), + namespaces, } } fn rename(&self, input: &mut String) { - for (span, res) in self.changes.iter().rev() { - let name = match res { - Res::Item(item, _) => match item.package { - None => format!("item{}", item.item), - Some(package) => format!("package{package}_item{}", item.item), + for (span, change) in self.changes.iter().rev() { + let name = match change { + Change::Res(res) => match res { + Res::Item(item, _) => match item.package { + None => format!("item{}", item.item), + Some(package) => format!("package{package}_item{}", item.item), + }, + Res::Local(node) => format!("local{node}"), + Res::PrimTy(prim) => format!("{prim:?}"), + Res::UnitTy => "Unit".to_string(), + Res::Param(id) => format!("param{id}"), }, - Res::Local(node) => format!("local{node}"), - Res::PrimTy(prim) => format!("{prim:?}"), - Res::UnitTy => "Unit".to_string(), - Res::Param(id) => format!("param{id}"), + Change::NamespaceId(ns_id) => format!("namespace{}", Into::::into(ns_id)), }; input.replace_range((span.lo as usize)..(span.hi as usize), &name); } @@ -54,7 +78,7 @@ impl<'a> Renamer<'a> { impl Visitor<'_> for Renamer<'_> { fn visit_path(&mut self, path: &Path) { if let Some(&id) = self.names.get(path.id) { - self.changes.push((path.span, id)); + self.changes.push((path.span, id.into())); } else { visit::walk_path(self, path); } @@ -62,9 +86,14 @@ impl Visitor<'_> for Renamer<'_> { fn visit_ident(&mut self, ident: &Ident) { if let Some(&id) = self.names.get(ident.id) { - self.changes.push((ident.span, id)); + self.changes.push((ident.span, id.into())); } } + + fn visit_vec_ident(&mut self, vec_ident: &VecIdent) { + let ns_id = self.namespaces.find_namespace(vec_ident).unwrap(); + self.changes.push((vec_ident.span(), ns_id.into())); + } } fn check(input: &str, expect: &Expect) { @@ -72,8 +101,8 @@ fn check(input: &str, expect: &Expect) { } fn resolve_names(input: &str) -> String { - let (package, names, _, errors) = compile(input, LanguageFeatures::default()); - let mut renamer = Renamer::new(&names); + let (package, names, _, errors, namespaces) = compile(input, LanguageFeatures::default()); + let mut renamer = Renamer::new(&names, namespaces); renamer.visit_package(&package); let mut output = input.to_string(); renamer.rename(&mut output); @@ -89,7 +118,7 @@ fn resolve_names(input: &str) -> String { fn compile( input: &str, language_features: LanguageFeatures, -) -> (Package, Names, Locals, Vec) { +) -> (Package, Names, Locals, Vec, NamespaceTreeRoot) { let (namespaces, parse_errors) = qsc_parse::namespaces(input, language_features); assert!(parse_errors.is_empty(), "parse failed: {parse_errors:#?}"); let mut package = Package { @@ -113,9 +142,9 @@ fn compile( let mut errors = globals.add_local_package(&mut assigner, &package); let mut resolver = Resolver::new(globals, dropped_names); resolver.with(&mut assigner).visit_package(&package); - let (names, locals, mut resolve_errors, _namespaces) = resolver.into_result(); + let (names, locals, mut resolve_errors, namespaces) = resolver.into_result(); errors.append(&mut resolve_errors); - (package, names, locals, errors) + (package, names, locals, errors, namespaces) } #[test] @@ -131,7 +160,7 @@ fn global_callable() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} function item2() : Unit { @@ -153,7 +182,7 @@ fn global_callable_recursive() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { item1(); } @@ -175,7 +204,7 @@ fn global_callable_internal() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { internal function item1() : Unit {} function item2() : Unit { @@ -196,7 +225,7 @@ fn global_callable_duplicate_error() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} operation item2() : Unit {} } @@ -221,11 +250,11 @@ fn global_path() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { + namespace namespace8 { function item3() : Unit { item1(); } @@ -251,12 +280,12 @@ fn open_namespace() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { - open Foo; + namespace namespace8 { + open namespace7; function item3() : Unit { item1(); @@ -283,12 +312,12 @@ fn open_alias() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { - open Foo as F; + namespace namespace8 { + open namespace7 as F; function item3() : Unit { item1(); @@ -313,11 +342,11 @@ fn prelude_callable() { } "}, &expect![[r#" - namespace item0 { + namespace namespace4 { function item1() : Unit {} } - namespace item2 { + namespace namespace7 { function item3() : Unit { item1(); } @@ -343,11 +372,11 @@ fn parent_namespace_shadows_prelude() { } "}, &expect![[r#" - namespace item0 { + namespace namespace4 { function item1() : Unit {} } - namespace item2 { + namespace namespace7 { function item3() : Unit {} function item4() : Unit { @@ -379,16 +408,16 @@ fn open_shadows_prelude() { } "}, &expect![[r#" - namespace item0 { + namespace namespace4 { function item1() : Unit {} } - namespace item2 { + namespace namespace7 { function item3() : Unit {} } - namespace item4 { - open Foo; + namespace namespace8 { + open namespace7; function item5() : Unit { item3(); @@ -417,17 +446,17 @@ fn ambiguous_prelude() { } "}, &expect![[r#" - namespace item0 { + namespace namespace3 { function item1() : Unit {} } - namespace item2 { + namespace namespace4 { function item3() : Unit {} } - namespace item4 { + namespace namespace7 { function item5() : Unit { - A(); + item1(); } } @@ -448,7 +477,7 @@ fn local_var() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Int { let local13 = 0; local13 @@ -474,7 +503,7 @@ fn shadow_local() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Int { let local13 = 0; let local17 = { @@ -499,7 +528,7 @@ fn callable_param() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1(local8 : Int) : Int { local8 } @@ -521,7 +550,7 @@ fn spec_param() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1(local8 : Qubit) : (Qubit[], Qubit) { controlled (local23, ...) { (local23, local8) @@ -548,7 +577,7 @@ fn spec_param_shadow_disallowed() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1(local8 : Qubit[]) : Qubit[] { controlled (local20, ...) { local20 @@ -579,13 +608,13 @@ fn local_shadows_global() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} function item2() : Int { item1(); let local27 = 1; - local27 + item1 } } "#]], @@ -605,7 +634,7 @@ fn shadow_same_block() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Int { let local13 = 0; let local17 = local13 + 1; @@ -635,12 +664,12 @@ fn parent_namespace_shadows_open() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { - open Foo; + namespace namespace8 { + open namespace7; function item3() : Unit {} @@ -673,16 +702,16 @@ fn open_alias_shadows_global() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { + namespace namespace8 { function item3() : Unit {} } - namespace item4 { - open Foo as Bar; + namespace namespace9 { + open namespace7 as Bar; function item5() : Unit { item1(); @@ -701,7 +730,7 @@ fn shadowing_disallowed_within_parameters() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1(local8: Int, local13: Double, local18: Bool) : Unit {} } @@ -721,7 +750,7 @@ fn shadowing_disallowed_within_local_binding() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1() : Unit { let (local14, local16, local18) = (1, 2, 3); } @@ -743,7 +772,7 @@ fn shadowing_disallowed_within_for_loop() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1() : Unit { for (local15, local17, local19) in [(1, 1, 1)] {} } @@ -765,7 +794,7 @@ fn shadowing_disallowed_within_lambda_param() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1() : Unit { let local13 = (local17, local19, local21) -> local21 + local19 + 1; } @@ -799,17 +828,17 @@ fn merged_aliases() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { + namespace namespace8 { function item3() : Unit {} } - namespace item4 { - open Foo as Alias; - open Bar as Alias; + namespace namespace9 { + open namespace7 as Alias; + open namespace8 as Alias; function item5() : Unit { item1(); @@ -830,7 +859,7 @@ fn ty_decl() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = Unit; function item2(local14 : item1) : Unit {} } @@ -848,7 +877,7 @@ fn ty_decl_duplicate_error() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = Unit; newtype item2 = Bool; } @@ -867,7 +896,7 @@ fn ty_decl_duplicate_error_on_built_in_ty() { } "}, &expect![[r#" - namespace item0 { + namespace namespace4 { newtype item1 = Unit; } @@ -886,7 +915,7 @@ fn ty_decl_in_ty_decl() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = Unit; newtype item2 = item1; } @@ -903,7 +932,7 @@ fn ty_decl_recursive() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = item1; } "#]], @@ -923,7 +952,7 @@ fn ty_decl_cons() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = Unit; function item2() : item1 { @@ -945,7 +974,7 @@ fn unknown_term() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { B(); } @@ -965,7 +994,7 @@ fn unknown_ty() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1(local8 : B) : Unit {} } @@ -1038,11 +1067,11 @@ fn open_ambiguous_tys() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = Unit; } - namespace item2 { + namespace namespace8 { newtype item3 = Unit; } @@ -1080,20 +1109,20 @@ fn merged_aliases_ambiguous_terms() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { + namespace namespace8 { function item3() : Unit {} } - namespace item4 { - open Foo as Alias; - open Bar as Alias; + namespace namespace9 { + open namespace7 as Alias; + open namespace8 as Alias; function item5() : Unit { - Alias.A(); + item3(); } } @@ -1122,19 +1151,19 @@ fn merged_aliases_ambiguous_tys() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = Unit; } - namespace item2 { + namespace namespace8 { newtype item3 = Unit; } - namespace item4 { - open Foo as Alias; - open Bar as Alias; + namespace namespace9 { + open namespace7 as Alias; + open namespace8 as Alias; - function item5(local30 : Alias.A) : Unit {} + function item5(local30 : item3) : Unit {} } // Ambiguous { name: "A", first_open: "Foo", second_open: "Bar", name_span: Span { lo: 170, hi: 171 }, first_open_span: Span { lo: 107, hi: 110 }, second_open_span: Span { lo: 130, hi: 133 } } @@ -1153,7 +1182,7 @@ fn lambda_param() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { let local13 = local16 -> local16 + 1; } @@ -1175,7 +1204,7 @@ fn lambda_shadows_local() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Int { let local13 = 1; let local17 = local20 -> local20 + 1; @@ -1199,7 +1228,7 @@ fn for_loop_range() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { for local14 in 0..9 { let _ = local14; @@ -1223,7 +1252,7 @@ fn for_loop_var() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1(local8 : Int[]) : Unit { for local20 in local8 { let _ = local20; @@ -1248,7 +1277,7 @@ fn repeat_until() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1() : Unit { mutable local13 = false; repeat { @@ -1277,7 +1306,7 @@ fn repeat_until_fixup() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1() : Unit { mutable local13 = false; repeat { @@ -1308,7 +1337,7 @@ fn repeat_until_fixup_scoping() { } }"}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1() : Unit { repeat { mutable local16 = false; @@ -1340,7 +1369,7 @@ fn use_qubit() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1(local8 : Qubit) : Unit { body intrinsic; } @@ -1369,7 +1398,7 @@ fn use_qubit_block() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1(local8 : Qubit) : Unit { body intrinsic; } @@ -1400,7 +1429,7 @@ fn use_qubit_block_qubit_restricted_to_block_scope() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1(local8 : Qubit) : Unit { body intrinsic; } @@ -1429,7 +1458,7 @@ fn local_function() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Int { function item2() : Int { 2 } item2() + 1 @@ -1451,7 +1480,7 @@ fn local_function_use_before_declare() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : () { item2(); function item2() : () {} @@ -1475,7 +1504,7 @@ fn local_function_is_really_local() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : () { function item3() : () {} item3(); @@ -1501,7 +1530,7 @@ fn local_function_is_not_closure() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : () { let local11 = 2; function item2() : Int { x } @@ -1525,7 +1554,7 @@ fn local_type() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : () { newtype item2 = Int; let local18 = item2(5); @@ -1543,8 +1572,8 @@ fn local_open() { namespace B { function Bar() : () {} } "}, &expect![[r#" - namespace item0 { function item1() : () { open B; item3(); } } - namespace item2 { function item3() : () {} } + namespace namespace7 { function item1() : () { open namespace8; item3(); } } + namespace namespace8 { function item3() : () {} } "#]], ); } @@ -1561,12 +1590,12 @@ fn local_open_shadows_parent_item() { namespace B { function Bar() : () {} } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : () {} - function item2() : () { open B; item4(); } + function item2() : () { open namespace8; item4(); } } - namespace item3 { function item4() : () {} } + namespace namespace8 { function item4() : () {} } "#]], ); } @@ -1584,13 +1613,13 @@ fn local_open_shadows_parent_open() { namespace C { function Bar() : () {} } "}, &expect![[r#" - namespace item0 { - open B; - function item1() : () { open C; item5(); } + namespace namespace7 { + open namespace8; + function item1() : () { open namespace9; item5(); } } - namespace item2 { function item3() : () {} } - namespace item4 { function item5() : () {} } + namespace namespace8 { function item3() : () {} } + namespace namespace9 { function item5() : () {} } "#]], ); } @@ -1608,7 +1637,7 @@ fn update_array_index_var() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : () { let local11 = [2]; let local16 = 0; @@ -1632,7 +1661,7 @@ fn update_array_index_expr() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : () { let local11 = [2]; let local16 = 0; @@ -1657,7 +1686,7 @@ fn update_udt_known_field_name() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = (First : Int, Second : Int); function item2() : () { @@ -1683,7 +1712,7 @@ fn update_udt_known_field_name_expr() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = (First : Int, Second : Int); function item2() : () { @@ -1711,7 +1740,7 @@ fn update_udt_unknown_field_name() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = (First : Int, Second : Int); function item2() : () { @@ -1739,7 +1768,7 @@ fn update_udt_unknown_field_name_known_global() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = (First : Int, Second : Int); function item2() : () {} @@ -1782,11 +1811,11 @@ fn empty_namespace_works() { namespace B {} "}, &expect![[r#" - namespace item0 { - open B; + namespace namespace7 { + open namespace8; function item1(): Unit{} } - namespace item2 {} + namespace namespace8 {} "#]], ); } @@ -1809,14 +1838,14 @@ fn cyclic_namespace_dependency_supported() { } "}, &expect![[r#" - namespace item0 { - open B; + namespace namespace7 { + open namespace8; operation item1() : Unit { item3(); } } - namespace item2 { - open A; + namespace namespace8 { + open namespace7; operation item3() : Unit { item1(); } @@ -1841,7 +1870,7 @@ fn bind_items_in_repeat() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1() : Unit { repeat { function item2() : Unit {} @@ -1868,7 +1897,7 @@ fn bind_items_in_qubit_use_block() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1() : Unit { use local13 = Qubit() { function item2() : Unit {} @@ -1893,7 +1922,7 @@ fn use_bound_item_in_another_bound_item() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { function item2() : Unit { item3(); @@ -1916,7 +1945,7 @@ fn use_unbound_generic() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1(local9: 'U) : 'U { local9 } @@ -1938,7 +1967,7 @@ fn resolve_local_generic() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1(local9: param0) : param0 { local9 } @@ -1961,7 +1990,7 @@ fn dropped_callable() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { @Config(Base) function Dropped() : Unit {} @@ -1970,7 +1999,7 @@ fn dropped_callable() { } } - // NotAvailable("Dropped", "A.Dropped", Span { lo: 100, hi: 107 }) + // NotAvailable("Dropped", "Ident 2 [10-11] \"A\".Dropped", Span { lo: 100, hi: 107 }) "#]], ); } @@ -2002,7 +2031,7 @@ fn multiple_definition_dropped_is_not_found() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { @Config(Unrestricted) operation item1() : Unit {} @Config(Base) @@ -2012,13 +2041,13 @@ fn multiple_definition_dropped_is_not_found() { @Config(Unrestricted) operation item2() : Unit {} } - namespace item3 { + namespace namespace8 { operation item4() : Unit { B(); C(); } operation item5() : Unit { - open A; + open namespace7; item1(); item2(); } @@ -2046,12 +2075,12 @@ fn disallow_duplicate_intrinsic() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { operation item1() : Unit { body intrinsic; } } - namespace item2 { + namespace namespace8 { operation item3() : Unit { body intrinsic; } @@ -2081,15 +2110,15 @@ fn disallow_duplicate_intrinsic_and_non_intrinsic_collision() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { internal operation item1() : Unit { body intrinsic; } } - namespace item2 { + namespace namespace8 { operation item3() : Unit {} } - namespace item4 { + namespace namespace8 { operation item5() : Unit { body intrinsic; } @@ -2112,7 +2141,7 @@ fn check_locals(input: &str, expect: &Expect) { let cursor_offset = parts[0].len() as u32; let source = parts.join(""); - let (_, _, locals, _) = compile(&source, LanguageFeatures::default()); + let (_, _, locals, _, _) = compile(&source, LanguageFeatures::default()); let locals = locals.get_all_at_offset(cursor_offset); let actual = locals.iter().fold(String::new(), |mut output, l| { @@ -2417,7 +2446,7 @@ fn use_after_scope() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { { let local16 = 42; @@ -2446,7 +2475,7 @@ fn nested_function_definition() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { function item2() : Unit { function item3() : Unit {} @@ -2477,7 +2506,7 @@ fn variable_in_nested_blocks() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { { let local16 = 10; @@ -2510,11 +2539,11 @@ fn function_call_with_namespace_alias() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { - open Foo as F; + namespace namespace8 { + open namespace7 as F; function item3() : Unit { item1(); } @@ -2538,7 +2567,7 @@ fn type_alias_in_function_scope() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { newtype item3 = Int; let local20 : item3 = item3(5); @@ -2569,7 +2598,7 @@ fn lambda_inside_lambda() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit { let local13 = () -> { let local20 = (local24) -> local24 + 1; @@ -2598,13 +2627,13 @@ fn nested_namespaces_with_same_function_name() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { + namespace namespace8 { function item3() : Unit {} function item4() : Unit { - item1(); + item3(); item3(); // Should call Bar.A without needing to qualify } } @@ -2621,7 +2650,7 @@ fn newtype_with_invalid_field_type() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = (Re: Real, Im: Imaginary); // Imaginary is not a valid type } @@ -2644,7 +2673,7 @@ fn newtype_with_tuple_destructuring() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { newtype item1 = (First: Int, Second: Int); function item2(local21: item1) : Int { let (local32, local34) = local21; @@ -2675,7 +2704,7 @@ namespace Foo.Bar.Baz { } "}, &expect![[r#" - namespace Foo { + namespace namespace7 { @EntryPoint() function item1(): Int { item2() @@ -2686,7 +2715,7 @@ namespace Foo.Bar.Baz { } } - namespace Foo.Bar.Baz { + namespace namespace9 { function item4() : Int { 6 } } "#]], @@ -2712,17 +2741,17 @@ fn basic_hierarchical_namespace() { } }"}, &expect![[r#" - namespace Foo.Bar.Baz { + namespace namespace9 { operation item1() : Unit {} } - namespace A { - open Foo; + namespace namespace10 { + open namespace7; operation item3() : Unit { item1(); } } - namespace B { - open Foo.Bar; + namespace namespace11 { + open namespace8; operation item5() : Unit { item1(); } diff --git a/compiler/qsc_frontend/src/typeck/tests.rs b/compiler/qsc_frontend/src/typeck/tests.rs index c18d6ccb08..4604b226e8 100644 --- a/compiler/qsc_frontend/src/typeck/tests.rs +++ b/compiler/qsc_frontend/src/typeck/tests.rs @@ -283,17 +283,17 @@ fn call_generic_length() { } "}, "Length([true, false, true])", - &expect![[r#" - #7 58-69 "(xs : 'T[])" : ? - #8 59-68 "xs : 'T[]" : ? - #17 98-125 "Length([true, false, true])" : Int - #18 98-104 "Length" : (Bool[] -> Int) - #21 104-125 "([true, false, true])" : Bool[] - #22 105-124 "[true, false, true]" : Bool[] - #23 106-110 "true" : Bool - #24 112-117 "false" : Bool - #25 119-123 "true" : Bool - "#]], + &expect![[r##" + #9 58-69 "(xs : 'T[])" : ? + #10 59-68 "xs : 'T[]" : ? + #19 98-125 "Length([true, false, true])" : Int + #20 98-104 "Length" : (Bool[] -> Int) + #23 104-125 "([true, false, true])" : Bool[] + #24 105-124 "[true, false, true]" : Bool[] + #25 106-110 "true" : Bool + #26 112-117 "false" : Bool + #27 119-123 "true" : Bool + "##]], ); } @@ -327,15 +327,15 @@ fn int_as_double_error() { } "}, "Microsoft.Quantum.Convert.IntAsDouble(false)", - &expect![[r#" - #6 62-71 "(a : Int)" : ? - #7 63-70 "a : Int" : ? - #16 103-147 "Microsoft.Quantum.Convert.IntAsDouble(false)" : Double - #17 103-140 "Microsoft.Quantum.Convert.IntAsDouble" : (Int -> Double) - #21 140-147 "(false)" : Bool - #22 141-146 "false" : Bool + &expect![[r##" + #8 62-71 "(a : Int)" : ? + #9 63-70 "a : Int" : ? + #18 103-147 "Microsoft.Quantum.Convert.IntAsDouble(false)" : Double + #19 103-140 "Microsoft.Quantum.Convert.IntAsDouble" : (Int -> Double) + #25 140-147 "(false)" : Bool + #26 141-146 "false" : Bool Error(Type(Error(TyMismatch("Int", "Bool", Span { lo: 103, hi: 147 })))) - "#]], + "##]], ); } @@ -348,19 +348,19 @@ fn length_type_error() { } "}, "Length((1, 2, 3))", - &expect![[r#" - #7 58-69 "(xs : 'T[])" : ? - #8 59-68 "xs : 'T[]" : ? - #17 98-115 "Length((1, 2, 3))" : Int - #18 98-104 "Length" : (?0[] -> Int) - #21 104-115 "((1, 2, 3))" : (Int, Int, Int) - #22 105-114 "(1, 2, 3)" : (Int, Int, Int) - #23 106-107 "1" : Int - #24 109-110 "2" : Int - #25 112-113 "3" : Int + &expect![[r##" + #9 58-69 "(xs : 'T[])" : ? + #10 59-68 "xs : 'T[]" : ? + #19 98-115 "Length((1, 2, 3))" : Int + #20 98-104 "Length" : (?0[] -> Int) + #23 104-115 "((1, 2, 3))" : (Int, Int, Int) + #24 105-114 "(1, 2, 3)" : (Int, Int, Int) + #25 106-107 "1" : Int + #26 109-110 "2" : Int + #27 112-113 "3" : Int Error(Type(Error(TyMismatch("?[]", "(Int, Int, Int)", Span { lo: 98, hi: 115 })))) Error(Type(Error(AmbiguousTy(Span { lo: 98, hi: 104 })))) - "#]], + "##]], ); } @@ -376,21 +376,21 @@ fn single_arg_for_tuple() { use q = Qubit(); Ry(q); }"}, - &expect![[r#" - #6 56-87 "(theta : Double, qubit : Qubit)" : (Double, Qubit) - #7 57-71 "theta : Double" : Double - #12 73-86 "qubit : Qubit" : Qubit - #21 106-108 "{}" : Unit - #22 111-146 "{\n use q = Qubit();\n Ry(q);\n}" : Unit - #23 111-146 "{\n use q = Qubit();\n Ry(q);\n}" : Unit - #25 121-122 "q" : Qubit - #27 125-132 "Qubit()" : Qubit - #29 138-143 "Ry(q)" : Unit - #30 138-140 "Ry" : ((Double, Qubit) => Unit is Adj + Ctl) - #33 140-143 "(q)" : Qubit - #34 141-142 "q" : Qubit + &expect![[r##" + #8 56-87 "(theta : Double, qubit : Qubit)" : (Double, Qubit) + #9 57-71 "theta : Double" : Double + #14 73-86 "qubit : Qubit" : Qubit + #23 106-108 "{}" : Unit + #24 111-146 "{\n use q = Qubit();\n Ry(q);\n}" : Unit + #25 111-146 "{\n use q = Qubit();\n Ry(q);\n}" : Unit + #27 121-122 "q" : Qubit + #29 125-132 "Qubit()" : Qubit + #31 138-143 "Ry(q)" : Unit + #32 138-140 "Ry" : ((Double, Qubit) => Unit is Adj + Ctl) + #35 140-143 "(q)" : Qubit + #36 141-142 "q" : Qubit Error(Type(Error(TyMismatch("(Double, Qubit)", "Qubit", Span { lo: 138, hi: 143 })))) - "#]], + "##]], ); } From 1206b0b354826f95289d87ac0a1b170e73b68074 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 2 Apr 2024 16:07:14 -0700 Subject: [PATCH 51/76] just need to sort out shadowing --- compiler/qsc_frontend/src/resolve.rs | 30 ++++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 33156c2cc3..9ac11f4132 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -11,6 +11,7 @@ use qsc_ast::{ }, visit::{self as ast_visit, walk_attr, Visitor as AstVisitor}, }; +use qsc_data_structures::namespaces::NamespaceTreeNode; use qsc_data_structures::{ index_map::IndexMap, namespaces::{NamespaceId, NamespaceTreeRoot}, @@ -25,7 +26,6 @@ use qsc_hir::{ use rustc_hash::{FxHashMap, FxHashSet}; use std::{collections::hash_map::Entry, rc::Rc, str::FromStr, vec}; use thiserror::Error; -use qsc_data_structures::namespaces::NamespaceTreeNode; use crate::compile::preprocess::TrackedName; @@ -668,7 +668,6 @@ impl AstVisitor<'_> for With<'_> { } ast_visit::walk_namespace(visitor, namespace); }); - } fn visit_attr(&mut self, attr: &ast::Attr) { @@ -1079,25 +1078,34 @@ fn resolve<'a>( provided_namespace_name: &Option, ) -> Result { let scopes = scopes.collect::>(); - let mut candidates: FxHashMap = FxHashMap::default(); + let mut candidates: FxHashMap = FxHashMap::default(); let mut vars = true; let provided_symbol_str = &(*provided_symbol_name.name); // the order of the namespaces in this vec is the order in which they will be searched, // and that's how the shadowing rules are determined. let candidate_namespaces = calculate_candidate_namespaces(globals, &scopes); -// search through each candidate namespace to find the items - for candidate_namespace_id in candidate_namespaces { - let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; + // search through each candidate namespace to find the items + for candidate_namespace_id in candidate_namespaces { + let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; - if let Some(namespace) = provided_namespace_name.as_ref().and_then(|ns| candidate_namespace.find_namespace(ns)) { - if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { - return Ok(res.clone()); - } + if let Some(namespace) = provided_namespace_name + .as_ref() + .and_then(|ns| candidate_namespace.find_namespace(ns)) + { + if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { + candidates.insert(res.clone(), Open { + namespace, + span: provided_symbol_name.span, + }); } + } if let Some(res) = globals.get(kind, candidate_namespace_id, provided_symbol_str) { - return Ok(res.clone()); + candidates.insert(res.clone(), Open { + namespace: candidate_namespace_id, + span: provided_symbol_name.span, + }); } for scope in &scopes { From 8c08c616e2e63983519e4d1bbba0c9498b89aac7 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 08:17:26 -0700 Subject: [PATCH 52/76] fix shadowing rules --- compiler/qsc_frontend/src/resolve.rs | 273 ++++++++++++--------- compiler/qsc_frontend/src/resolve/tests.rs | 2 +- 2 files changed, 155 insertions(+), 120 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 9ac11f4132..a70cc9c582 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -25,6 +25,7 @@ use qsc_hir::{ }; use rustc_hash::{FxHashMap, FxHashSet}; use std::{collections::hash_map::Entry, rc::Rc, str::FromStr, vec}; +use std::cmp::Ordering; use thiserror::Error; use crate::compile::preprocess::TrackedName; @@ -248,12 +249,6 @@ pub enum LocalKind { pub struct GlobalScope { tys: FxHashMap, Res>>, terms: FxHashMap, Res>>, - // this is basically an index map, where indices are used as - // namespace ids - // TODO maybe what we can do here is only store top-level namespaces here - // and bury the rest in the hierarchy? - // or, store the hierarchical structure right here on the below field, - // and then map the namespace IDs to a vec of entries namespaces: NamespaceTreeRoot, intrinsics: FxHashSet>, } @@ -297,6 +292,31 @@ struct Open { span: Span, } +impl Eq for Open {} + +impl PartialEq for Open { + fn eq(&self, other: &Self) -> bool { + self.namespace == other.namespace + } +} + +impl PartialOrd for Open { + fn partial_cmp(&self, other: &Self) -> Option { + let a: usize = self.namespace.into(); + let b: usize = other.namespace.into(); + a.partial_cmp(&b) + } +} + +impl Ord for Open { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + let a: usize = self.namespace.into(); + let b: usize = other.namespace.into(); + a.cmp(&b) + } + +} + pub(super) struct Resolver { names: Names, dropped_names: Vec, @@ -1078,71 +1098,54 @@ fn resolve<'a>( provided_namespace_name: &Option, ) -> Result { let scopes = scopes.collect::>(); - let mut candidates: FxHashMap = FxHashMap::default(); let mut vars = true; let provided_symbol_str = &(*provided_symbol_name.name); - // the order of the namespaces in this vec is the order in which they will be searched, - // and that's how the shadowing rules are determined. - let candidate_namespaces = calculate_candidate_namespaces(globals, &scopes); - // search through each candidate namespace to find the items - for candidate_namespace_id in candidate_namespaces { - let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; - - if let Some(namespace) = provided_namespace_name - .as_ref() - .and_then(|ns| candidate_namespace.find_namespace(ns)) - { - if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { - candidates.insert(res.clone(), Open { - namespace, - span: provided_symbol_name.span, - }); + for scope in &scopes { + if provided_namespace_name.is_none() { + if let Some(res) = + resolve_scope_locals(kind, globals, scope, vars, provided_symbol_str) + { + // Local declarations shadow everything. + return Ok(res); } } - - if let Some(res) = globals.get(kind, candidate_namespace_id, provided_symbol_str) { - candidates.insert(res.clone(), Open { - namespace: candidate_namespace_id, - span: provided_symbol_name.span, - }); + let scope_opens = scope.opens.iter().map(|(_, opens)| opens).flatten().map(|open| open.namespace).collect::>(); + let explicit_open_candidates = find_symbol_in_namespaces(kind, globals, provided_symbol_name, provided_namespace_name, provided_symbol_str, scope_opens); + if explicit_open_candidates.len() == 1 { + return Ok(single(explicit_open_candidates.into_keys()).unwrap()); + } else if explicit_open_candidates.len() > 1 { + return ambiguous_symbol_error(globals, provided_symbol_name, provided_symbol_str, explicit_open_candidates); } - for scope in &scopes { - if provided_namespace_name.is_none() { - if let Some(res) = - resolve_scope_locals(kind, globals, scope, vars, provided_symbol_str) - { - // Local declarations shadow everything. - return Ok(res); - } - } - - if scope.kind == ScopeKind::Callable { - // Since local callables are not closures, hide local variables in parent scopes. - vars = false; - } + if scope.kind == ScopeKind::Callable { + // Since local callables are not closures, hide local variables in parent scopes. + vars = false; } } - if candidates.is_empty() && provided_namespace_name.is_none() { - // Prelude shadows unopened globals. - let candidates = resolve_implicit_opens( - kind, - globals, - PRELUDE - .into_iter() - .map(|x| x.into_iter().map(|x| Rc::from(*x)).collect::>()) - .collect::>(), - provided_symbol_str, - ); - if candidates.len() > 1 { + + let CandidateNamespaces { + explicit_opens, prelude, root_id + } = calculate_candidate_namespaces(globals, &scopes); + + + // then, check prelude + if provided_namespace_name.is_none() { + let prelude_candidates = find_symbol_in_namespaces(kind, globals, provided_symbol_name, provided_namespace_name, provided_symbol_str, prelude); + + if prelude_candidates.len() > 1 { // If there are multiple candidates, sort them by namespace and return an error. - let mut candidates: Vec<_> = candidates.into_iter().collect(); - candidates.sort_by_key(|x| Into::::into(x.1)); + let mut candidates: Vec<_> = prelude_candidates.into_iter().collect(); let mut candidates = candidates .into_iter() - .map(|candidate| candidate.1.to_string()); + .map(|candidate| { + let ns_name = globals.namespaces.find_id(&candidate.1.namespace).0.join("."); + ns_name + }).collect::>(); + candidates.sort(); + + let mut candidates = candidates.into_iter(); let candidate_a = candidates .next() .expect("infallible as per length check above"); @@ -1157,61 +1160,110 @@ fn resolve<'a>( }); } // if there is a candidate, return it - if let Some((res, _)) = single(candidates) { + if let Some((res, _)) = single(prelude_candidates) { return Ok(res); } } - if candidates.len() > 1 { - // If there are multiple candidates, remove unimplemented items. This allows resolution to - // succeed in cases where both an older, unimplemented API and newer, implemented API with the - // same name are both in scope without forcing the user to fully qualify the name. - let mut removals = Vec::new(); - for res in candidates.keys() { - if let Res::Item(_, ItemStatus::Unimplemented) = res { - removals.push(*res); + // lastly, check unopened globals + let global_candidates = find_symbol_in_namespaces(kind, globals, provided_symbol_name, provided_namespace_name, provided_symbol_str, vec![root_id]); + if global_candidates.len() == 1 { + return Ok(single(global_candidates.into_keys()).unwrap()); + } else if global_candidates.len() > 1 { + return ambiguous_symbol_error(globals, provided_symbol_name, provided_symbol_str, global_candidates); + } + + Err(Error::NotFound(provided_symbol_str.to_string(), provided_symbol_name.span)) +} + +fn ambiguous_symbol_error(globals: &GlobalScope, provided_symbol_name: &Ident, provided_symbol_str: &str, candidates: FxHashMap) -> Result { + let mut opens: Vec<_> = candidates.into_values().collect(); + opens.sort_unstable_by_key(|open| open.span); + let (first_open_ns, _) = globals.namespaces.find_id(&opens[0].namespace); + let (second_open_ns, _) = globals.namespaces.find_id(&opens[1].namespace); + Err(Error::Ambiguous { + name: provided_symbol_str.to_string(), + first_open: first_open_ns.join("."), + second_open: second_open_ns.join("."), + name_span: provided_symbol_name.span, + first_open_span: opens[0].span, + second_open_span: opens[1].span, + }) +} + +fn find_symbol_in_namespaces(kind: NameKind, globals: &GlobalScope, provided_symbol_name: &Ident, provided_namespace_name: &Option, provided_symbol_str: &str, explicit_opens: Vec) -> FxHashMap { + let mut candidates = FxHashMap::default(); +// search through each candidate namespace to find the items + for candidate_namespace_id in explicit_opens { + let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; + + if let Some(namespace) = provided_namespace_name + .as_ref() + .and_then(|ns| candidate_namespace.find_namespace(ns)) + { + if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { + candidates.insert(res.clone(), Open { + namespace, + span: provided_symbol_name.span, + }); } } - for res in removals { - candidates.remove(&res); + + if let Some(res) = globals.get(kind, candidate_namespace_id, provided_symbol_str) { + candidates.insert(res.clone(), Open { + namespace: candidate_namespace_id, + span: provided_symbol_name.span, + }); } } - - if candidates.len() > 1 { - let mut opens: Vec<_> = candidates.into_values().collect(); - opens.sort_unstable_by_key(|open| open.span); - let (first_open_ns, _) = globals.namespaces.find_id(&opens[0].namespace); - let (second_open_ns, _) = globals.namespaces.find_id(&opens[1].namespace); - Err(Error::Ambiguous { - name: provided_symbol_str.to_string(), - first_open: first_open_ns.join("."), - second_open: second_open_ns.join("."), - name_span: provided_symbol_name.span, - first_open_span: opens[0].span, - second_open_span: opens[1].span, - }) - } else { - single(candidates.into_keys()).ok_or_else(|| { - Error::NotFound(provided_symbol_str.to_string(), provided_symbol_name.span) - }) + // If there are multiple candidates, remove unimplemented items. This allows resolution to + // succeed in cases where both an older, unimplemented API and newer, implemented API with the + // same name are both in scope without forcing the user to fully qualify the name. + let mut removals = Vec::new(); + for res in candidates.keys() { + if let Res::Item(_, ItemStatus::Unimplemented) = res { + removals.push(*res); + } + } + for res in removals { + candidates.remove(&res); } + candidates +} + +/// Shadowing rules are as follows: +/// - Local variables shadow everything. They are the first priority. +/// - Next, we check open statements for an explicit open. +/// - Then, we check the prelude. +/// - Lastly, we check the global namespace. +struct CandidateNamespaces { + /// the outer vec is shadowing equality. That is, if there is an ambiguity within the inner vec, + /// then it is an ambiguous symbol error. If there is ambiguity among outer vecs, then the first + /// one takes precedence + explicit_opens: Vec>, + prelude: Vec, + root_id: NamespaceId, } /// Given a list of scopes, and the global scope, calculate an ordered list of the namespaces to check /// for a symbol. -fn calculate_candidate_namespaces(globals: &GlobalScope, scopes: &Vec<&Scope>) -> Vec { - let mut candidate_namespaces = vec![]; +fn calculate_candidate_namespaces(globals: &GlobalScope, scopes: &Vec<&Scope>) -> CandidateNamespaces { + let mut explicit_opens = vec![]; // here, we also need to check all opens to see if the namespace is in any of them - for open in scopes - .iter() - .flat_map(|scope| scope.opens.values().flatten()) - { - // insert each open into the list of places to check for this item - candidate_namespaces.push(open.namespace); + for scope in scopes { + let mut scope_buf = vec![]; + for open in scope.opens.values().flatten() { + // insert each open into the list of places to check for this item + scope_buf.push(open.namespace); + } + explicit_opens.push(scope_buf); } + + let mut prelude = vec![]; + // add prelude to the list of candidate namespaces last, as they are the final fallback for a symbol for prelude_namespace in PRELUDE { - candidate_namespaces.push( + prelude.push( globals .namespaces .find_namespace( @@ -1223,9 +1275,12 @@ fn calculate_candidate_namespaces(globals: &GlobalScope, scopes: &Vec<&Scope>) - .expect("prelude namespaces should exist"), ); } - // the top-level (root) namespace is the last priority to check. - candidate_namespaces.push(globals.namespaces.root_id()); - candidate_namespaces + + CandidateNamespaces { + explicit_opens, + prelude, + root_id: globals.namespaces.root_id(), + } } /// Implements shadowing rules within a single scope. @@ -1306,26 +1361,6 @@ fn get_scope_locals(scope: &Scope, offset: u32, vars: bool) -> Vec { names } -/// The return type represents the resolution of all implicit opens. -/// The namespace is returned along with the res, so that the namespace can be used to -/// report the ambiguity to the user. -fn resolve_implicit_opens<'b>( - kind: NameKind, - globals: &'b GlobalScope, - namespaces: impl IntoIterator>>, - name: &'b str, -) -> FxHashMap { - let mut candidates = FxHashMap::default(); - for namespace in namespaces { - let namespace_id = globals - .find_namespace(namespace) - .expect("prelude should exist"); - if let Some(&res) = globals.get(kind, namespace_id, name) { - candidates.insert(res, namespace_id); - } - } - candidates -} fn resolve_explicit_opens<'a>( kind: NameKind, diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index f350aedd06..b425525776 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -614,7 +614,7 @@ fn local_shadows_global() { function item2() : Int { item1(); let local27 = 1; - item1 + local27 } } "#]], From 7eae70883d028a179d653b9b3a70c71d6a7fa34f Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 08:24:44 -0700 Subject: [PATCH 53/76] fix removal of unimplemented items from resolution --- compiler/qsc_frontend/src/resolve.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index a70cc9c582..1f895dc449 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1089,7 +1089,8 @@ fn decl_is_intrinsic(decl: &CallableDecl) -> bool { } } -/// TODO(alex): rename namespaces to show what are candidates and what are being passed in. Document the hell out of this. +/// TODO(alex): rename namespaces to show what are candidates and what are being passed in. Document this, especially detailed shadowing +/// rules. fn resolve<'a>( kind: NameKind, globals: &GlobalScope, @@ -1216,17 +1217,19 @@ fn find_symbol_in_namespaces(kind: NameKind, globals: &GlobalScope, provided_sym }); } } - // If there are multiple candidates, remove unimplemented items. This allows resolution to - // succeed in cases where both an older, unimplemented API and newer, implemented API with the - // same name are both in scope without forcing the user to fully qualify the name. - let mut removals = Vec::new(); - for res in candidates.keys() { - if let Res::Item(_, ItemStatus::Unimplemented) = res { - removals.push(*res); + if candidates.len() > 1 { + // If there are multiple candidates, remove unimplemented items. This allows resolution to + // succeed in cases where both an older, unimplemented API and newer, implemented API with the + // same name are both in scope without forcing the user to fully qualify the name. + let mut removals = Vec::new(); + for res in candidates.keys() { + if let Res::Item(_, ItemStatus::Unimplemented) = res { + removals.push(*res); + } + } + for res in removals { + candidates.remove(&res); } - } - for res in removals { - candidates.remove(&res); } candidates } From 552f34646b13cc7a3879ee4d3aecbe0b4e18c07e Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 08:27:27 -0700 Subject: [PATCH 54/76] fix ambiguous prelude --- compiler/qsc_frontend/src/resolve/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index b425525776..887acfa70c 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -456,7 +456,7 @@ fn ambiguous_prelude() { namespace namespace7 { function item5() : Unit { - item1(); + A(); } } From a1be75ba2d310d2a7f1c58b3524d324128f3fc06 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 09:33:59 -0700 Subject: [PATCH 55/76] refactor symbol finder to be generic; fix ambiguous opens test --- compiler/qsc_frontend/src/resolve.rs | 151 ++++++++++++--------- compiler/qsc_frontend/src/resolve/tests.rs | 16 +-- 2 files changed, 94 insertions(+), 73 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 1f895dc449..4e3d1086e5 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -24,8 +24,8 @@ use qsc_hir::{ ty::{ParamId, Prim}, }; use rustc_hash::{FxHashMap, FxHashSet}; -use std::{collections::hash_map::Entry, rc::Rc, str::FromStr, vec}; use std::cmp::Ordering; +use std::{collections::hash_map::Entry, rc::Rc, str::FromStr, vec}; use thiserror::Error; use crate::compile::preprocess::TrackedName; @@ -314,7 +314,6 @@ impl Ord for Open { let b: usize = other.namespace.into(); a.cmp(&b) } - } pub(super) struct Resolver { @@ -1104,19 +1103,36 @@ fn resolve<'a>( for scope in &scopes { if provided_namespace_name.is_none() { - if let Some(res) = - resolve_scope_locals(kind, globals, scope, vars, provided_symbol_str) + if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, provided_symbol_str) { // Local declarations shadow everything. return Ok(res); } } - let scope_opens = scope.opens.iter().map(|(_, opens)| opens).flatten().map(|open| open.namespace).collect::>(); - let explicit_open_candidates = find_symbol_in_namespaces(kind, globals, provided_symbol_name, provided_namespace_name, provided_symbol_str, scope_opens); + let scope_opens = scope + .opens + .iter() + .map(|(_, opens)| opens) + .flatten() + .cloned() + .collect::>(); + let explicit_open_candidates = find_symbol_in_namespaces( + kind, + globals, + provided_symbol_name, + provided_namespace_name, + provided_symbol_str, + scope_opens.into_iter().map(|open @ Open { namespace, .. }| (namespace, open)), + ); if explicit_open_candidates.len() == 1 { return Ok(single(explicit_open_candidates.into_keys()).unwrap()); } else if explicit_open_candidates.len() > 1 { - return ambiguous_symbol_error(globals, provided_symbol_name, provided_symbol_str, explicit_open_candidates); + return ambiguous_symbol_error( + globals, + provided_symbol_name, + provided_symbol_str, + explicit_open_candidates, + ); } if scope.kind == ScopeKind::Callable { @@ -1125,25 +1141,27 @@ fn resolve<'a>( } } - - let CandidateNamespaces { - explicit_opens, prelude, root_id - } = calculate_candidate_namespaces(globals, &scopes); - - + let prelude = prelude_namespaces(globals); // then, check prelude if provided_namespace_name.is_none() { - let prelude_candidates = find_symbol_in_namespaces(kind, globals, provided_symbol_name, provided_namespace_name, provided_symbol_str, prelude); + let prelude_candidates = find_symbol_in_namespaces( + kind, + globals, + provided_symbol_name, + provided_namespace_name, + provided_symbol_str, + prelude.into_iter().map(|(a, b)| (b, a)), + ); if prelude_candidates.len() > 1 { // If there are multiple candidates, sort them by namespace and return an error. let mut candidates: Vec<_> = prelude_candidates.into_iter().collect(); let mut candidates = candidates .into_iter() - .map(|candidate| { - let ns_name = globals.namespaces.find_id(&candidate.1.namespace).0.join("."); + .map(|(candidate, ns_name)| { ns_name - }).collect::>(); + }) + .collect::>(); candidates.sort(); let mut candidates = candidates.into_iter(); @@ -1167,17 +1185,32 @@ fn resolve<'a>( } // lastly, check unopened globals - let global_candidates = find_symbol_in_namespaces(kind, globals, provided_symbol_name, provided_namespace_name, provided_symbol_str, vec![root_id]); + let global_candidates = find_symbol_in_namespaces( + kind, + globals, + provided_symbol_name, + provided_namespace_name, + provided_symbol_str, + vec![(globals.namespaces.root_id(), ())].into_iter(), + ); + // we don't have to worry about having extra candidates here, as we are only looking at the root, + // and that's only one namespace. individual namespaces cannot have duplicate declarations. if global_candidates.len() == 1 { return Ok(single(global_candidates.into_keys()).unwrap()); - } else if global_candidates.len() > 1 { - return ambiguous_symbol_error(globals, provided_symbol_name, provided_symbol_str, global_candidates); } - Err(Error::NotFound(provided_symbol_str.to_string(), provided_symbol_name.span)) + Err(Error::NotFound( + provided_symbol_str.to_string(), + provided_symbol_name.span, + )) } -fn ambiguous_symbol_error(globals: &GlobalScope, provided_symbol_name: &Ident, provided_symbol_str: &str, candidates: FxHashMap) -> Result { +fn ambiguous_symbol_error( + globals: &GlobalScope, + provided_symbol_name: &Ident, + provided_symbol_str: &str, + candidates: FxHashMap, +) -> Result { let mut opens: Vec<_> = candidates.into_values().collect(); opens.sort_unstable_by_key(|open| open.span); let (first_open_ns, _) = globals.namespaces.find_id(&opens[0].namespace); @@ -1192,29 +1225,34 @@ fn ambiguous_symbol_error(globals: &GlobalScope, provided_symbol_name: &Ident, p }) } -fn find_symbol_in_namespaces(kind: NameKind, globals: &GlobalScope, provided_symbol_name: &Ident, provided_namespace_name: &Option, provided_symbol_str: &str, explicit_opens: Vec) -> FxHashMap { +fn find_symbol_in_namespaces( + kind: NameKind, + globals: &GlobalScope, + provided_symbol_name: &Ident, + provided_namespace_name: &Option, + provided_symbol_str: &str, + namespaces_to_search: T, +) -> FxHashMap +where + T: Iterator, + O: Clone +{ let mut candidates = FxHashMap::default(); -// search through each candidate namespace to find the items - for candidate_namespace_id in explicit_opens { + // search through each candidate namespace to find the items + for (candidate_namespace_id, open) in namespaces_to_search + { let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; - if let Some(namespace) = provided_namespace_name - .as_ref() - .and_then(|ns| candidate_namespace.find_namespace(ns)) - { - if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { - candidates.insert(res.clone(), Open { - namespace, - span: provided_symbol_name.span, - }); + if let Some(ref provided_namespace_name) = provided_namespace_name { + if let Some(namespace) = candidate_namespace.find_namespace(provided_namespace_name) { + if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { + candidates.insert(res.clone(), open.clone()); + } } } if let Some(res) = globals.get(kind, candidate_namespace_id, provided_symbol_str) { - candidates.insert(res.clone(), Open { - namespace: candidate_namespace_id, - span: provided_symbol_name.span, - }); + candidates.insert(res.clone(), open); } } if candidates.len() > 1 { @@ -1248,42 +1286,26 @@ struct CandidateNamespaces { root_id: NamespaceId, } -/// Given a list of scopes, and the global scope, calculate an ordered list of the namespaces to check -/// for a symbol. -fn calculate_candidate_namespaces(globals: &GlobalScope, scopes: &Vec<&Scope>) -> CandidateNamespaces { - let mut explicit_opens = vec![]; - // here, we also need to check all opens to see if the namespace is in any of them - for scope in scopes { - let mut scope_buf = vec![]; - for open in scope.opens.values().flatten() { - // insert each open into the list of places to check for this item - scope_buf.push(open.namespace); - } - explicit_opens.push(scope_buf); - } +fn prelude_namespaces(globals: &GlobalScope) -> Vec<(String, NamespaceId)> { let mut prelude = vec![]; // add prelude to the list of candidate namespaces last, as they are the final fallback for a symbol for prelude_namespace in PRELUDE { + let prelude_name = prelude_namespace + .into_iter() + .map(|x| -> Rc { Rc::from(*x) }) + .collect::>(); prelude.push( - globals + (prelude_name.join("."), globals .namespaces - .find_namespace( - prelude_namespace - .into_iter() - .map(|x| -> Rc { Rc::from(*x) }) - .collect::>(), + .find_namespace(prelude_name +, ) - .expect("prelude namespaces should exist"), + .expect("prelude namespaces should exist")), ); } - - CandidateNamespaces { - explicit_opens, - prelude, - root_id: globals.namespaces.root_id(), - } + prelude } /// Implements shadowing rules within a single scope. @@ -1364,7 +1386,6 @@ fn get_scope_locals(scope: &Scope, offset: u32, vars: bool) -> Vec { names } - fn resolve_explicit_opens<'a>( kind: NameKind, globals: &GlobalScope, diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index 887acfa70c..f167a7b8c1 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -1025,17 +1025,17 @@ fn open_ambiguous_terms() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { function item1() : Unit {} } - namespace item2 { + namespace namespace8 { function item3() : Unit {} } - namespace item4 { - open Foo; - open Bar; + namespace namespace9 { + open namespace7; + open namespace8; function item5() : Unit { A(); @@ -1075,9 +1075,9 @@ fn open_ambiguous_tys() { newtype item3 = Unit; } - namespace item4 { - open Foo; - open Bar; + namespace namespace9 { + open namespace7; + open namespace8; function item5(local28 : A) : Unit {} } From e80f15416784d8c8fef540dec24ea00234424d9d Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 09:39:48 -0700 Subject: [PATCH 56/76] fmt --- compiler/qsc_frontend/src/resolve.rs | 29 ++++++++++------------ compiler/qsc_frontend/src/resolve/tests.rs | 6 ++--- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 4e3d1086e5..29089b0b82 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1122,7 +1122,9 @@ fn resolve<'a>( provided_symbol_name, provided_namespace_name, provided_symbol_str, - scope_opens.into_iter().map(|open @ Open { namespace, .. }| (namespace, open)), + scope_opens + .into_iter() + .map(|open @ Open { namespace, .. }| (namespace, open)), ); if explicit_open_candidates.len() == 1 { return Ok(single(explicit_open_candidates.into_keys()).unwrap()); @@ -1158,9 +1160,7 @@ fn resolve<'a>( let mut candidates: Vec<_> = prelude_candidates.into_iter().collect(); let mut candidates = candidates .into_iter() - .map(|(candidate, ns_name)| { - ns_name - }) + .map(|(candidate, ns_name)| ns_name) .collect::>(); candidates.sort(); @@ -1235,12 +1235,11 @@ fn find_symbol_in_namespaces( ) -> FxHashMap where T: Iterator, - O: Clone + O: Clone, { let mut candidates = FxHashMap::default(); // search through each candidate namespace to find the items - for (candidate_namespace_id, open) in namespaces_to_search - { + for (candidate_namespace_id, open) in namespaces_to_search { let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; if let Some(ref provided_namespace_name) = provided_namespace_name { @@ -1286,24 +1285,22 @@ struct CandidateNamespaces { root_id: NamespaceId, } - fn prelude_namespaces(globals: &GlobalScope) -> Vec<(String, NamespaceId)> { let mut prelude = vec![]; // add prelude to the list of candidate namespaces last, as they are the final fallback for a symbol for prelude_namespace in PRELUDE { - let prelude_name = prelude_namespace + let prelude_name = prelude_namespace .into_iter() .map(|x| -> Rc { Rc::from(*x) }) .collect::>(); - prelude.push( - (prelude_name.join("."), globals + prelude.push(( + prelude_name.join("."), + globals .namespaces - .find_namespace(prelude_name -, - ) - .expect("prelude namespaces should exist")), - ); + .find_namespace(prelude_name) + .expect("prelude namespaces should exist"), + )); } prelude } diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index f167a7b8c1..1695ee7588 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -11,17 +11,17 @@ use crate::{ }; use expect_test::{expect, Expect}; use indoc::indoc; +use qsc_ast::ast::VecIdent; use qsc_ast::{ assigner::Assigner as AstAssigner, ast::{Ident, NodeId, Package, Path, TopLevelNode}, mut_visit::MutVisitor, visit::{self, Visitor}, }; +use qsc_data_structures::namespaces::{NamespaceId, NamespaceTreeRoot}; use qsc_data_structures::{language_features::LanguageFeatures, span::Span}; use qsc_hir::assigner::Assigner as HirAssigner; use std::fmt::Write; -use qsc_ast::ast::VecIdent; -use qsc_data_structures::namespaces::{NamespaceId, NamespaceTreeRoot}; enum Change { Res(Res), @@ -47,7 +47,7 @@ struct Renamer<'a> { } impl<'a> Renamer<'a> { - fn new(names: &'a Names, namespaces: NamespaceTreeRoot,) -> Self { + fn new(names: &'a Names, namespaces: NamespaceTreeRoot) -> Self { Self { names, changes: Vec::new(), From 2674be35c689fcdab553cce93d4c86b1f5d86541 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 18:28:29 -0700 Subject: [PATCH 57/76] replicate merged namespace alias behavior --- .../qsc_frontend/src/incremental/tests.rs | 6 +-- compiler/qsc_frontend/src/resolve.rs | 44 +++++++++++++++---- compiler/qsc_frontend/src/resolve/tests.rs | 30 +++++++++++-- 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/compiler/qsc_frontend/src/incremental/tests.rs b/compiler/qsc_frontend/src/incremental/tests.rs index 2d5053230d..e7277ca2c6 100644 --- a/compiler/qsc_frontend/src/incremental/tests.rs +++ b/compiler/qsc_frontend/src/incremental/tests.rs @@ -72,9 +72,9 @@ fn one_callable() { ), ), opens: { - NamespaceId( - 9, - ): [ + [ + "Foo", + ]: [ Open { namespace: NamespaceId( 9, diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 29089b0b82..126241acd9 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -125,7 +125,7 @@ pub struct Scope { span: Span, kind: ScopeKind, /// Open statements. The key is the namespace name or alias. - opens: FxHashMap>, + opens: FxHashMap>, Vec>, /// Local newtype declarations. tys: FxHashMap, ItemId>, /// Local callable and newtype declarations. @@ -521,7 +521,6 @@ impl Resolver { } fn bind_open(&mut self, name: &VecIdent, alias: &Option>) { - // TODO figure out how aliases are going to work let id = match self.globals.find_namespace(name) { Some(id) => id, None => { @@ -535,11 +534,13 @@ impl Resolver { return; } }; - // let alias = alias.as_ref().map_or("".into(), |a| Rc::clone(&a.name)); + let alias = alias + .as_ref() + .map_or(name.into(), |a| vec![Rc::clone(&a.name)]); if self.globals.namespaces.find_namespace(name).is_some() { self.current_scope_mut() .opens - .entry(id) + .entry(alias) .or_default() .push(Open { namespace: id, @@ -1109,12 +1110,17 @@ fn resolve<'a>( return Ok(res); } } + let aliases = FxHashMap::from_iter(scope.opens.iter().map(|(alias, opens)| { + ( + alias.clone(), + opens.iter().cloned().map(|x| (x.namespace, x)).collect(), + ) + })); + let scope_opens = scope .opens .iter() - .map(|(_, opens)| opens) - .flatten() - .cloned() + .flat_map(|(_, open)| open.clone()) .collect::>(); let explicit_open_candidates = find_symbol_in_namespaces( kind, @@ -1124,7 +1130,8 @@ fn resolve<'a>( provided_symbol_str, scope_opens .into_iter() - .map(|open @ Open { namespace, .. }| (namespace, open)), + .map(|open @ Open { namespace, .. }| (namespace, open.clone())), + aliases, ); if explicit_open_candidates.len() == 1 { return Ok(single(explicit_open_candidates.into_keys()).unwrap()); @@ -1153,6 +1160,8 @@ fn resolve<'a>( provided_namespace_name, provided_symbol_str, prelude.into_iter().map(|(a, b)| (b, a)), + // there are no aliases in the prelude + Default::default(), ); if prelude_candidates.len() > 1 { @@ -1192,6 +1201,8 @@ fn resolve<'a>( provided_namespace_name, provided_symbol_str, vec![(globals.namespaces.root_id(), ())].into_iter(), + // there are no aliases in globals + Default::default(), ); // we don't have to worry about having extra candidates here, as we are only looking at the root, // and that's only one namespace. individual namespaces cannot have duplicate declarations. @@ -1232,11 +1243,26 @@ fn find_symbol_in_namespaces( provided_namespace_name: &Option, provided_symbol_str: &str, namespaces_to_search: T, + aliases: FxHashMap>, Vec<(NamespaceId, O)>>, ) -> FxHashMap where T: Iterator, - O: Clone, + O: Clone + std::fmt::Debug, { + // replace aliases with real namespaces + if let Some(provided_namespace_name) = provided_namespace_name { + if let Some(opens) = aliases.get(&(Into::>>::into(provided_namespace_name))) { + let mut candidates = vec![]; + for (ns_id, open) in opens { + if let Some(res) = globals.get(kind, *ns_id, provided_symbol_str) { + candidates.push((res.clone(), open.clone())); + } + } + if !candidates.is_empty() { + return candidates.into_iter().collect(); + } + } + } let mut candidates = FxHashMap::default(); // search through each candidate namespace to find the items for (candidate_namespace_id, open) in namespaces_to_search { diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index 1695ee7588..dcdf921f94 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -11,7 +11,7 @@ use crate::{ }; use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_ast::ast::VecIdent; +use qsc_ast::ast::{Item, ItemKind, VecIdent}; use qsc_ast::{ assigner::Assigner as AstAssigner, ast::{Ident, NodeId, Package, Path, TopLevelNode}, @@ -21,7 +21,9 @@ use qsc_ast::{ use qsc_data_structures::namespaces::{NamespaceId, NamespaceTreeRoot}; use qsc_data_structures::{language_features::LanguageFeatures, span::Span}; use qsc_hir::assigner::Assigner as HirAssigner; +use rustc_hash::FxHashMap; use std::fmt::Write; +use std::rc::Rc; enum Change { Res(Res), @@ -44,6 +46,7 @@ struct Renamer<'a> { names: &'a Names, changes: Vec<(Span, Change)>, namespaces: NamespaceTreeRoot, + aliases: FxHashMap>, NamespaceId>, } impl<'a> Renamer<'a> { @@ -52,6 +55,7 @@ impl<'a> Renamer<'a> { names, changes: Vec::new(), namespaces, + aliases: Default::default(), } } @@ -90,8 +94,26 @@ impl Visitor<'_> for Renamer<'_> { } } + fn visit_item(&mut self, item: &'_ Item) { + match &*item.kind { + ItemKind::Open(namespace, Some(alias)) => { + let ns_id = self.namespaces.find_namespace(namespace).unwrap(); + self.aliases.insert(vec![alias.name.clone()], ns_id); + } + _ => (), + } + visit::walk_item(self, item); + } + fn visit_vec_ident(&mut self, vec_ident: &VecIdent) { - let ns_id = self.namespaces.find_namespace(vec_ident).unwrap(); + let ns_id = match self.namespaces.find_namespace(vec_ident) { + Some(x) => x, + None => self + .aliases + .get(&(Into::>>::into(vec_ident))) + .copied() + .unwrap_or_else(|| panic!("Namespace not found: {:?}", vec_ident)), + }; self.changes.push((vec_ident.span(), ns_id.into())); } } @@ -1122,7 +1144,7 @@ fn merged_aliases_ambiguous_terms() { open namespace8 as Alias; function item5() : Unit { - item3(); + namespace8.A(); } } @@ -1163,7 +1185,7 @@ fn merged_aliases_ambiguous_tys() { open namespace7 as Alias; open namespace8 as Alias; - function item5(local30 : item3) : Unit {} + function item5(local30 : namespace8.A) : Unit {} } // Ambiguous { name: "A", first_open: "Foo", second_open: "Bar", name_span: Span { lo: 170, hi: 171 }, first_open_span: Span { lo: 107, hi: 110 }, second_open_span: Span { lo: 130, hi: 133 } } From 70faf28d12b019942d76eddc13928534f208a0aa Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 18:33:40 -0700 Subject: [PATCH 58/76] all frontend tests pass --- compiler/qsc_frontend/src/resolve/tests.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index dcdf921f94..857d3b347a 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -108,11 +108,14 @@ impl Visitor<'_> for Renamer<'_> { fn visit_vec_ident(&mut self, vec_ident: &VecIdent) { let ns_id = match self.namespaces.find_namespace(vec_ident) { Some(x) => x, - None => self + None => match self .aliases .get(&(Into::>>::into(vec_ident))) .copied() - .unwrap_or_else(|| panic!("Namespace not found: {:?}", vec_ident)), + { + Some(x) => x, + None => return, + } }; self.changes.push((vec_ident.span(), ns_id.into())); } @@ -1813,7 +1816,7 @@ fn unknown_namespace() { } "}, &expect![[r#" - namespace item0 { + namespace namespace7 { open Microsoft.Quantum.Fake; } From d9c6c7e5827e6f71500f07aaaba2590c06d4c245 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 18:44:57 -0700 Subject: [PATCH 59/76] clear up warnings --- compiler/qsc_ast/src/ast.rs | 6 +++- .../qsc_data_structures/src/namespaces.rs | 2 +- compiler/qsc_fir/src/fir.rs | 6 +++- compiler/qsc_frontend/src/resolve.rs | 32 +++---------------- compiler/qsc_hir/src/global.rs | 4 +-- compiler/qsc_hir/src/hir.rs | 10 +++++- language_service/src/compilation.rs | 1 + 7 files changed, 28 insertions(+), 33 deletions(-) diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index c47aa127ef..b60a86feb9 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -1306,6 +1306,9 @@ pub struct Ident { pub name: Rc, } +/// A [VecIdent] represents a sequence of idents. It provides a helpful abstraction +/// that is more powerful than a simple `Vec`, and is primarily used to represent +/// dot-separated paths. #[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub struct VecIdent(pub Vec); @@ -1335,7 +1338,6 @@ impl From for Vec { impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let idents = self.0.iter(); let mut buf = Vec::with_capacity(self.0.len()); for ident in self.0.iter() { @@ -1350,10 +1352,12 @@ impl Display for VecIdent { } } impl VecIdent { + /// constructs an iter over the [Ident]s that this contains. pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { self.0.iter() } + /// the conjoined span of all idents in the VecIdent pub fn span(&self) -> Span { Span { lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index 0ff9e3d372..9312cfc8cb 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests; -use std::{cell::RefCell, collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; +use std::{ collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; const PRELUDE: [[&str; 3]; 4] = [ ["Microsoft", "Quantum", "Canon"], diff --git a/compiler/qsc_fir/src/fir.rs b/compiler/qsc_fir/src/fir.rs index 340d57b3d0..b69abd302c 100644 --- a/compiler/qsc_fir/src/fir.rs +++ b/compiler/qsc_fir/src/fir.rs @@ -1423,6 +1423,9 @@ impl Display for PatKind { } } +/// A [VecIdent] represents a sequence of idents. It provides a helpful abstraction +/// that is more powerful than a simple `Vec`, and is primarily used to represent +/// dot-separated paths. #[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub struct VecIdent(pub Vec); @@ -1457,7 +1460,6 @@ impl FromIterator for VecIdent { } impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let idents = self.0.iter(); let mut buf = Vec::with_capacity(self.0.len()); for ident in self.0.iter() { @@ -1472,10 +1474,12 @@ impl Display for VecIdent { } } impl VecIdent { + /// constructs an iter over the [Ident]s that this contains. pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { self.0.iter() } + /// the conjoined span of all idents in the VecIdent pub fn span(&self) -> Span { Span { lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 126241acd9..c770d5328b 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1091,6 +1091,11 @@ fn decl_is_intrinsic(decl: &CallableDecl) -> bool { /// TODO(alex): rename namespaces to show what are candidates and what are being passed in. Document this, especially detailed shadowing /// rules. +/// Shadowing rules are as follows: +/// - Local variables shadow everything. They are the first priority. +/// - Next, we check open statements for an explicit open. +/// - Then, we check the prelude. +/// - Lastly, we check the global namespace. fn resolve<'a>( kind: NameKind, globals: &GlobalScope, @@ -1297,19 +1302,6 @@ where candidates } -/// Shadowing rules are as follows: -/// - Local variables shadow everything. They are the first priority. -/// - Next, we check open statements for an explicit open. -/// - Then, we check the prelude. -/// - Lastly, we check the global namespace. -struct CandidateNamespaces { - /// the outer vec is shadowing equality. That is, if there is an ambiguity within the inner vec, - /// then it is an ambiguous symbol error. If there is ambiguity among outer vecs, then the first - /// one takes precedence - explicit_opens: Vec>, - prelude: Vec, - root_id: NamespaceId, -} fn prelude_namespaces(globals: &GlobalScope) -> Vec<(String, NamespaceId)> { let mut prelude = vec![]; @@ -1409,20 +1401,6 @@ fn get_scope_locals(scope: &Scope, offset: u32, vars: bool) -> Vec { names } -fn resolve_explicit_opens<'a>( - kind: NameKind, - globals: &GlobalScope, - opens: impl IntoIterator, - name: &str, -) -> FxHashMap { - let mut candidates = FxHashMap::default(); - for open in opens { - if let Some(&res) = globals.get(kind, open.namespace, name) { - candidates.insert(res, open); - } - } - candidates -} /// Creates an [`ItemId`] for an item that is local to this package (internal to it). fn intrapackage(item: LocalItemId) -> ItemId { diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index 0a1532b1f7..388945443d 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -3,7 +3,7 @@ use crate::{ hir::{ - Ident, Item, ItemId, ItemKind, ItemStatus, Package, PackageId, SpecBody, SpecGen, VecIdent, + Item, ItemId, ItemKind, ItemStatus, Package, PackageId, SpecBody, SpecGen, Visibility, }, ty::Scheme, @@ -13,7 +13,7 @@ use qsc_data_structures::{ namespaces::{NamespaceId, NamespaceTreeRoot}, }; use rustc_hash::FxHashMap; -use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use std::rc::Rc; #[derive(Debug)] pub struct Global { diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 8ea884e4cf..4c7e174748 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -1134,6 +1134,9 @@ impl Display for QubitInitKind { } } +/// A [VecIdent] represents a sequence of idents. It provides a helpful abstraction +/// that is more powerful than a simple `Vec`, and is primarily used to represent +/// dot-separated paths. #[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub struct VecIdent(pub Vec); @@ -1169,7 +1172,6 @@ impl FromIterator for VecIdent { impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let idents = self.0.iter(); let mut buf = Vec::with_capacity(self.0.len()); for ident in self.0.iter() { @@ -1184,10 +1186,12 @@ impl Display for VecIdent { } } impl VecIdent { + /// constructs an iter over the [Ident]s that this contains. pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { self.0.iter() } + /// the conjoined span of all idents in the VecIdent pub fn span(&self) -> Span { Span { lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), @@ -1195,10 +1199,12 @@ impl VecIdent { } } + /// Whether or not the first ident in this [VecIdent] matches `arg` pub fn starts_with(&self, arg: &str) -> bool { self.0.first().map(|i| &*i.name == arg).unwrap_or_default() } + /// Whether or not the first `n` idents in this [VecIdent] match `arg` pub fn starts_with_sequence(&self, arg: &[&str]) -> bool { if arg.len() > self.0.len() { return false; @@ -1211,6 +1217,8 @@ impl VecIdent { return true; } + /// The stringified dot-separated path of the idents in this [VecIdent] + /// E.g. `a.b.c` pub fn name(&self) -> String { self.0 .iter() diff --git a/language_service/src/compilation.rs b/language_service/src/compilation.rs index f040b1917a..14f696cdfa 100644 --- a/language_service/src/compilation.rs +++ b/language_service/src/compilation.rs @@ -219,6 +219,7 @@ impl Compilation { self.errors = new.errors; } + #[allow(dead_code)] pub(crate) fn find_namespace_id(&self, ns: [&str; 3]) -> NamespaceId { self.package_store .get(self.user_package_id) From 3c13840038d74dcd935601304aa56a56612b2171 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 18:56:08 -0700 Subject: [PATCH 60/76] remove dead code --- .../qsc_data_structures/src/namespaces.rs | 2 +- .../src/namespaces/tests.rs | 1 - compiler/qsc_frontend/src/compile/tests.rs | 2 +- compiler/qsc_frontend/src/resolve.rs | 12 ++---- compiler/qsc_frontend/src/resolve/tests.rs | 2 +- compiler/qsc_frontend/src/typeck/tests.rs | 1 - compiler/qsc_hir/src/global.rs | 5 +-- language_service/src/completion.rs | 37 +------------------ 8 files changed, 9 insertions(+), 53 deletions(-) diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index 9312cfc8cb..8c37d325ac 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests; -use std::{ collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; +use std::{collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; const PRELUDE: [[&str; 3]; 4] = [ ["Microsoft", "Quantum", "Canon"], diff --git a/compiler/qsc_data_structures/src/namespaces/tests.rs b/compiler/qsc_data_structures/src/namespaces/tests.rs index fa2eeb9fbc..dd8b97aac6 100644 --- a/compiler/qsc_data_structures/src/namespaces/tests.rs +++ b/compiler/qsc_data_structures/src/namespaces/tests.rs @@ -1,7 +1,6 @@ use expect_test::expect; use super::*; -use std::collections::HashMap; #[test] fn test_tree_construction() { diff --git a/compiler/qsc_frontend/src/compile/tests.rs b/compiler/qsc_frontend/src/compile/tests.rs index 4a2b560335..65ef000ae7 100644 --- a/compiler/qsc_frontend/src/compile/tests.rs +++ b/compiler/qsc_frontend/src/compile/tests.rs @@ -1283,7 +1283,7 @@ fn hierarchical_namespace_basic() { None, ); - let mut store = PackageStore::new(super::core()); + let store = PackageStore::new(super::core()); let lib = compile( &store, &[], diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index c770d5328b..1ae3493996 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -11,7 +11,7 @@ use qsc_ast::{ }, visit::{self as ast_visit, walk_attr, Visitor as AstVisitor}, }; -use qsc_data_structures::namespaces::NamespaceTreeNode; + use qsc_data_structures::{ index_map::IndexMap, namespaces::{NamespaceId, NamespaceTreeRoot}, @@ -1130,7 +1130,6 @@ fn resolve<'a>( let explicit_open_candidates = find_symbol_in_namespaces( kind, globals, - provided_symbol_name, provided_namespace_name, provided_symbol_str, scope_opens @@ -1161,7 +1160,6 @@ fn resolve<'a>( let prelude_candidates = find_symbol_in_namespaces( kind, globals, - provided_symbol_name, provided_namespace_name, provided_symbol_str, prelude.into_iter().map(|(a, b)| (b, a)), @@ -1171,10 +1169,10 @@ fn resolve<'a>( if prelude_candidates.len() > 1 { // If there are multiple candidates, sort them by namespace and return an error. - let mut candidates: Vec<_> = prelude_candidates.into_iter().collect(); + let candidates: Vec<_> = prelude_candidates.into_iter().collect(); let mut candidates = candidates .into_iter() - .map(|(candidate, ns_name)| ns_name) + .map(|(_candidate, ns_name)| ns_name) .collect::>(); candidates.sort(); @@ -1202,7 +1200,6 @@ fn resolve<'a>( let global_candidates = find_symbol_in_namespaces( kind, globals, - provided_symbol_name, provided_namespace_name, provided_symbol_str, vec![(globals.namespaces.root_id(), ())].into_iter(), @@ -1244,7 +1241,6 @@ fn ambiguous_symbol_error( fn find_symbol_in_namespaces( kind: NameKind, globals: &GlobalScope, - provided_symbol_name: &Ident, provided_namespace_name: &Option, provided_symbol_str: &str, namespaces_to_search: T, @@ -1302,7 +1298,6 @@ where candidates } - fn prelude_namespaces(globals: &GlobalScope) -> Vec<(String, NamespaceId)> { let mut prelude = vec![]; @@ -1401,7 +1396,6 @@ fn get_scope_locals(scope: &Scope, offset: u32, vars: bool) -> Vec { names } - /// Creates an [`ItemId`] for an item that is local to this package (internal to it). fn intrapackage(item: LocalItemId) -> ItemId { ItemId { diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index 857d3b347a..6f96f3dec7 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -115,7 +115,7 @@ impl Visitor<'_> for Renamer<'_> { { Some(x) => x, None => return, - } + }, }; self.changes.push((vec_ident.span(), ns_id.into())); } diff --git a/compiler/qsc_frontend/src/typeck/tests.rs b/compiler/qsc_frontend/src/typeck/tests.rs index 4604b226e8..e439a4d6ad 100644 --- a/compiler/qsc_frontend/src/typeck/tests.rs +++ b/compiler/qsc_frontend/src/typeck/tests.rs @@ -86,7 +86,6 @@ fn compile(input: &str, entry_expr: &str) -> (Package, super::Table, Vec qsc::NamespaceTreeRoot { - let mut hir_namespaces: HashMap<_, _> = Default::default(); - let mut assigner: usize = 0; - let tree = namespaces.tree(); - let root_id = tree.id(); - - for (namespace, qsc::NamespaceTreeNode { children, id }) in tree.children() { - let children = qsc::NamespaceTreeNode::new(*id, children.clone()); - let id = Into::::into(id); - if id > assigner { - assigner = id + 1; - }; - hir_namespaces.insert(namespace.clone(), children); - } - - qsc::NamespaceTreeRoot::new_from_parts( - assigner, - NamespaceTreeNode::new(root_id, hir_namespaces), - ) -} - /// Convert a local into a completion item fn local_completion( candidate: &Local, From 42a9051ea8db5b4870b4fcdbf9669c66fe4c362d Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 19:18:15 -0700 Subject: [PATCH 61/76] clippy on frontend --- Cargo.lock | 1 + compiler/qsc_ast/src/ast.rs | 19 +- compiler/qsc_ast/src/mut_visit.rs | 8 +- compiler/qsc_ast/src/visit.rs | 2 +- compiler/qsc_data_structures/Cargo.toml | 1 + .../qsc_data_structures/src/namespaces.rs | 39 +- .../src/namespaces/tests.rs | 802 +++++------------- compiler/qsc_eval/src/lower.rs | 2 +- compiler/qsc_fir/src/fir.rs | 18 +- compiler/qsc_fir/src/global.rs | 2 +- compiler/qsc_frontend/src/resolve.rs | 92 +- compiler/qsc_frontend/src/resolve/tests.rs | 13 +- compiler/qsc_hir/src/global.rs | 2 +- compiler/qsc_hir/src/hir.rs | 31 +- compiler/qsc_hir/src/mut_visit.rs | 4 +- language_service/src/compilation.rs | 2 +- language_service/src/completion.rs | 3 +- 17 files changed, 333 insertions(+), 708 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0d6081860..0535d06718 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -992,6 +992,7 @@ dependencies = [ "bitflags 2.4.2", "expect-test", "miette", + "rustc-hash", "serde", ] diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index b60a86feb9..15f1b0e74b 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -1306,7 +1306,7 @@ pub struct Ident { pub name: Rc, } -/// A [VecIdent] represents a sequence of idents. It provides a helpful abstraction +/// A [`VecIdent`] represents a sequence of idents. It provides a helpful abstraction /// that is more powerful than a simple `Vec`, and is primarily used to represent /// dot-separated paths. #[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] @@ -1340,8 +1340,8 @@ impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut buf = Vec::with_capacity(self.0.len()); - for ident in self.0.iter() { - buf.push(format!("{}", ident)); + for ident in &self.0 { + buf.push(format!("{ident}")); } if buf.len() > 1 { // use square brackets only if there are more than one ident @@ -1351,13 +1351,22 @@ impl Display for VecIdent { } } } + +impl<'a> IntoIterator for &'a VecIdent { + type IntoIter = std::slice::Iter<'a, Ident>; + type Item = &'a Ident; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} impl VecIdent { /// constructs an iter over the [Ident]s that this contains. - pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { + pub fn iter(&self) -> std::slice::Iter<'_, Ident> { self.0.iter() } - /// the conjoined span of all idents in the VecIdent + /// the conjoined span of all idents in the `VecIdent` + #[must_use] pub fn span(&self) -> Span { Span { lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), diff --git a/compiler/qsc_ast/src/mut_visit.rs b/compiler/qsc_ast/src/mut_visit.rs index 9eb22e6e2e..fcd8431d62 100644 --- a/compiler/qsc_ast/src/mut_visit.rs +++ b/compiler/qsc_ast/src/mut_visit.rs @@ -338,10 +338,10 @@ pub fn walk_qubit_init(vis: &mut impl MutVisitor, init: &mut QubitInit) { pub fn walk_path(vis: &mut impl MutVisitor, path: &mut Path) { vis.visit_span(&mut path.span); if let Some(ref mut namespace) = path.namespace { - vis.visit_vec_ident(namespace) + vis.visit_vec_ident(namespace); } if let Some(ref mut ns) = path.namespace { - vis.visit_vec_ident(ns) + vis.visit_vec_ident(ns); } vis.visit_ident(&mut path.name); } @@ -350,7 +350,7 @@ pub fn walk_ident(vis: &mut impl MutVisitor, ident: &mut Ident) { vis.visit_span(&mut ident.span); } pub fn walk_vec_ident(vis: &mut impl MutVisitor, ident: &mut crate::ast::VecIdent) { - for ref mut ident in ident.0.iter_mut() { - vis.visit_ident(ident) + for ref mut ident in &mut ident.0 { + vis.visit_ident(ident); } } diff --git a/compiler/qsc_ast/src/visit.rs b/compiler/qsc_ast/src/visit.rs index 0fb9e4cbcb..900694d8f2 100644 --- a/compiler/qsc_ast/src/visit.rs +++ b/compiler/qsc_ast/src/visit.rs @@ -97,7 +97,7 @@ pub fn walk_item<'a>(vis: &mut impl Visitor<'a>, item: &'a Item) { ItemKind::Err => {} ItemKind::Callable(decl) => vis.visit_callable_decl(decl), ItemKind::Open(ns, alias) => { - vis.visit_vec_ident(&ns); + vis.visit_vec_ident(ns); alias.iter().for_each(|a| vis.visit_ident(a)); } ItemKind::Ty(ident, def) => { diff --git a/compiler/qsc_data_structures/Cargo.toml b/compiler/qsc_data_structures/Cargo.toml index bff7698695..a5127b5345 100644 --- a/compiler/qsc_data_structures/Cargo.toml +++ b/compiler/qsc_data_structures/Cargo.toml @@ -12,6 +12,7 @@ repository.workspace = true miette = { workspace = true } serde = { workspace = true } bitflags = { workspace = true } +rustc-hash = { workspace = true } [dev-dependencies] expect-test = { workspace = true } diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index 8c37d325ac..3fabd69a74 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -1,7 +1,8 @@ #[cfg(test)] mod tests; -use std::{collections::HashMap, fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; +use rustc_hash::FxHashMap; +use std::{fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; const PRELUDE: [[&str; 3]; 4] = [ ["Microsoft", "Quantum", "Canon"], @@ -83,7 +84,7 @@ impl NamespaceTreeRoot { pub fn new_namespace_node( &mut self, - children: HashMap, NamespaceTreeNode>, + children: FxHashMap, NamespaceTreeNode>, ) -> NamespaceTreeNode { self.assigner += 1; NamespaceTreeNode { @@ -97,7 +98,7 @@ impl NamespaceTreeRoot { } #[must_use] - pub fn find_id(&self, id: &NamespaceId) -> (Vec>, Rc<&NamespaceTreeNode>) { + pub fn find_id(&self, id: &NamespaceId) -> (Vec>, &NamespaceTreeNode) { return self.tree.find_id(*id, vec![]); } @@ -112,7 +113,7 @@ impl Default for NamespaceTreeRoot { let mut tree = Self { assigner: 0, tree: NamespaceTreeNode { - children: HashMap::new(), + children: FxHashMap::default(), id: NamespaceId::new(0), }, }; @@ -127,17 +128,17 @@ impl Default for NamespaceTreeRoot { #[derive(Debug, Clone)] pub struct NamespaceTreeNode { - pub children: HashMap, NamespaceTreeNode>, + pub children: FxHashMap, NamespaceTreeNode>, pub id: NamespaceId, } impl NamespaceTreeNode { #[must_use] - pub fn new(id: NamespaceId, children: HashMap, NamespaceTreeNode>) -> Self { + pub fn new(id: NamespaceId, children: FxHashMap, NamespaceTreeNode>) -> Self { Self { children, id } } #[must_use] - pub fn children(&self) -> &HashMap, NamespaceTreeNode> { + pub fn children(&self) -> &FxHashMap, NamespaceTreeNode> { &self.children } @@ -192,7 +193,8 @@ impl NamespaceTreeNode { _ => {} } *assigner += 1; - let mut new_node = NamespaceTreeNode::new(NamespaceId::new(*assigner), HashMap::new()); + let mut new_node = + NamespaceTreeNode::new(NamespaceId::new(*assigner), FxHashMap::default()); if iter.peek().is_none() { let new_node_id = new_node.id; self.children.insert(next_item, new_node); @@ -208,19 +210,18 @@ impl NamespaceTreeNode { &self, id: NamespaceId, names_buf: Vec>, - ) -> (Vec>, Rc<&NamespaceTreeNode>) { + ) -> (Vec>, &NamespaceTreeNode) { if self.id == id { - return (names_buf, Rc::new(self)); - } else { - for (name, node) in self.children.iter() { - let mut new_buf = names_buf.clone(); - new_buf.push(name.clone()); - let (names, node) = node.find_id(id, new_buf); - if names.len() > 0 { - return (names, node); - } + return (names_buf, &self); + } + for (name, node) in &self.children { + let mut new_buf = names_buf.clone(); + new_buf.push(name.clone()); + let (names, node) = node.find_id(id, new_buf); + if !names.is_empty() { + return (names, node); } - return (vec![], Rc::new(self)); } + (vec![], &self) } } diff --git a/compiler/qsc_data_structures/src/namespaces/tests.rs b/compiler/qsc_data_structures/src/namespaces/tests.rs index dd8b97aac6..76cabfa508 100644 --- a/compiler/qsc_data_structures/src/namespaces/tests.rs +++ b/compiler/qsc_data_structures/src/namespaces/tests.rs @@ -2,289 +2,154 @@ use expect_test::expect; use super::*; +#[allow(clippy::too_many_lines)] #[test] fn test_tree_construction() { let mut root = NamespaceTreeRoot::default(); - for i in 0..10 { + for i in 0..3 { for j in 'a'..'d' { root.insert_or_find_namespace( - vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter(), + vec![Rc::from(format!("ns{i}")), Rc::from(format!("ns{j}"))].into_iter(), ); } } expect![[r#" - NamespaceTreeRoot { - assigner: 40, - tree: NamespaceTreeNode { + NamespaceTreeRoot { + assigner: 18, + tree: NamespaceTreeNode { + children: { + "ns1": NamespaceTreeNode { children: { - "ns6": NamespaceTreeNode { - children: { - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 27, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 28, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 26, - ), - }, - }, + "nsc": NamespaceTreeNode { + children: {}, id: NamespaceId( - 25, + 14, ), }, - "ns3": NamespaceTreeNode { - children: { - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 14, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 16, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 15, - ), - }, - }, + "nsb": NamespaceTreeNode { + children: {}, id: NamespaceId( 13, ), }, - "ns7": NamespaceTreeNode { - children: { - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 32, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 31, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 30, - ), - }, - }, + "nsa": NamespaceTreeNode { + children: {}, id: NamespaceId( - 29, + 12, ), }, - "ns8": NamespaceTreeNode { - children: { - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 35, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 36, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 34, - ), - }, - }, + }, + id: NamespaceId( + 11, + ), + }, + "ns0": NamespaceTreeNode { + children: { + "nsc": NamespaceTreeNode { + children: {}, id: NamespaceId( - 33, + 10, ), }, - "ns0": NamespaceTreeNode { - children: { - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 3, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 2, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 4, - ), - }, - }, + "nsb": NamespaceTreeNode { + children: {}, id: NamespaceId( - 1, + 9, ), }, - "ns4": NamespaceTreeNode { - children: { - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 20, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 18, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 19, - ), - }, - }, + "nsa": NamespaceTreeNode { + children: {}, id: NamespaceId( - 17, + 8, ), }, - "ns2": NamespaceTreeNode { + }, + id: NamespaceId( + 7, + ), + }, + "Microsoft": NamespaceTreeNode { + children: { + "Quantum": NamespaceTreeNode { children: { - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 10, - ), - }, - "nsb": NamespaceTreeNode { + "Canon": NamespaceTreeNode { children: {}, id: NamespaceId( - 11, + 3, ), }, - "nsc": NamespaceTreeNode { + "Measurement": NamespaceTreeNode { children: {}, id: NamespaceId( - 12, - ), - }, - }, - id: NamespaceId( - 9, - ), - }, - "ns5": NamespaceTreeNode { - children: { - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 22, + 6, ), }, - "nsb": NamespaceTreeNode { + "Core": NamespaceTreeNode { children: {}, id: NamespaceId( - 23, + 4, ), }, - "nsc": NamespaceTreeNode { + "Intrinsic": NamespaceTreeNode { children: {}, id: NamespaceId( - 24, + 5, ), }, }, id: NamespaceId( - 21, + 2, ), }, - "ns1": NamespaceTreeNode { - children: { - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 8, - ), - }, - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 6, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 7, - ), - }, - }, + }, + id: NamespaceId( + 1, + ), + }, + "ns2": NamespaceTreeNode { + children: { + "nsc": NamespaceTreeNode { + children: {}, id: NamespaceId( - 5, + 18, ), }, - "ns9": NamespaceTreeNode { - children: { - "nsa": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 38, - ), - }, - "nsb": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 39, - ), - }, - "nsc": NamespaceTreeNode { - children: {}, - id: NamespaceId( - 40, - ), - }, - }, + "nsb": NamespaceTreeNode { + children: {}, id: NamespaceId( - 37, + 17, + ), + }, + "nsa": NamespaceTreeNode { + children: {}, + id: NamespaceId( + 16, ), }, }, id: NamespaceId( - 0, + 15, ), }, - } - "#]] + }, + id: NamespaceId( + 0, + ), + }, + } + "#]] .assert_debug_eq(&root); } +#[allow(clippy::too_many_lines)] #[test] fn test_find_id() { let mut root = NamespaceTreeRoot::default(); let mut id_buf = vec![]; - for i in 0..10 { + for i in 0..3 { for j in 'a'..'d' { id_buf.push(root.insert_or_find_namespace( - vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter(), + vec![Rc::from(format!("ns{i}")), Rc::from(format!("ns{j}"))].into_iter(), )); } } @@ -293,422 +158,149 @@ fn test_find_id() { result_buf.push(root.find_id(&id)); } expect![[r#" + [ + ( [ - ( - [ - "ns0", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 2, - ), - }, + "ns0", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 8, ), - ( - [ - "ns0", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 3, - ), - }, - ), - ( - [ - "ns0", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 4, - ), - }, - ), - ( - [ - "ns1", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 6, - ), - }, - ), - ( - [ - "ns1", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 7, - ), - }, - ), - ( - [ - "ns1", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 8, - ), - }, - ), - ( - [ - "ns2", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 10, - ), - }, - ), - ( - [ - "ns2", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 11, - ), - }, - ), - ( - [ - "ns2", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 12, - ), - }, - ), - ( - [ - "ns3", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 14, - ), - }, - ), - ( - [ - "ns3", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 15, - ), - }, - ), - ( - [ - "ns3", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 16, - ), - }, - ), - ( - [ - "ns4", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 18, - ), - }, - ), - ( - [ - "ns4", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 19, - ), - }, - ), - ( - [ - "ns4", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 20, - ), - }, - ), - ( - [ - "ns5", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 22, - ), - }, - ), - ( - [ - "ns5", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 23, - ), - }, - ), - ( - [ - "ns5", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 24, - ), - }, - ), - ( - [ - "ns6", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 26, - ), - }, - ), - ( - [ - "ns6", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 27, - ), - }, - ), - ( - [ - "ns6", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 28, - ), - }, - ), - ( - [ - "ns7", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 30, - ), - }, - ), - ( - [ - "ns7", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 31, - ), - }, + }, + ), + ( + [ + "ns0", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 9, ), - ( - [ - "ns7", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 32, - ), - }, + }, + ), + ( + [ + "ns0", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 10, ), - ( - [ - "ns8", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 34, - ), - }, + }, + ), + ( + [ + "ns1", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 12, ), - ( - [ - "ns8", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 35, - ), - }, + }, + ), + ( + [ + "ns1", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 13, ), - ( - [ - "ns8", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 36, - ), - }, + }, + ), + ( + [ + "ns1", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 14, ), - ( - [ - "ns9", - "nsa", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 38, - ), - }, + }, + ), + ( + [ + "ns2", + "nsa", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 16, ), - ( - [ - "ns9", - "nsb", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 39, - ), - }, + }, + ), + ( + [ + "ns2", + "nsb", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 17, ), - ( - [ - "ns9", - "nsc", - ], - NamespaceTreeNode { - children: {}, - id: NamespaceId( - 40, - ), - }, + }, + ), + ( + [ + "ns2", + "nsc", + ], + NamespaceTreeNode { + children: {}, + id: NamespaceId( + 18, ), - ] - "#]] - .assert_debug_eq(&result_buf) + }, + ), + ] + "#]] + .assert_debug_eq(&result_buf); } // test that after inserting lots of namespaces, all ids are unique and sequential #[test] fn test_insert_or_find_namespace() { let mut root = NamespaceTreeRoot::default(); let mut ids: Vec = vec![]; - for i in 0..10 { + for i in 0..3 { for j in 'a'..'d' { let id = root.insert_or_find_namespace( - vec![Rc::from(format!("ns{}", i)), Rc::from(format!("ns{}", j))].into_iter(), + vec![Rc::from(format!("ns{i}")), Rc::from(format!("ns{j}"))].into_iter(), ); ids.push(id.into()); } } let mut ids_sorted = ids.clone(); - ids_sorted.sort(); + ids_sorted.sort_unstable(); ids_sorted.dedup(); // there should be no duplicate or out-of-order ids assert_eq!(ids_sorted, ids); - expect![[r#" - [ - 2, - 3, - 4, - 6, - 7, - 8, - 10, - 11, - 12, - 14, - 15, - 16, - 18, - 19, - 20, - 22, - 23, - 24, - 26, - 27, - 28, - 30, - 31, - 32, - 34, - 35, - 36, - 38, - 39, - 40, - ] - "#]] + expect![[r" + [ + 8, + 9, + 10, + 12, + 13, + 14, + 16, + 17, + 18, + ] + "]] .assert_debug_eq(&ids); } diff --git a/compiler/qsc_eval/src/lower.rs b/compiler/qsc_eval/src/lower.rs index 1a4dcaa026..3ea2518397 100644 --- a/compiler/qsc_eval/src/lower.rs +++ b/compiler/qsc_eval/src/lower.rs @@ -150,7 +150,7 @@ impl Lowerer { fn lower_item(&mut self, item: &hir::Item) -> fir::Item { let kind = match &item.kind { hir::ItemKind::Namespace(name, items) => { - let name = self.lower_vec_ident(&name); + let name = self.lower_vec_ident(name); let items = items.iter().map(|i| lower_local_item_id(*i)).collect(); fir::ItemKind::Namespace(name, items) } diff --git a/compiler/qsc_fir/src/fir.rs b/compiler/qsc_fir/src/fir.rs index b69abd302c..dcbe07b38c 100644 --- a/compiler/qsc_fir/src/fir.rs +++ b/compiler/qsc_fir/src/fir.rs @@ -1423,12 +1423,19 @@ impl Display for PatKind { } } -/// A [VecIdent] represents a sequence of idents. It provides a helpful abstraction +/// A [`VecIdent`] represents a sequence of idents. It provides a helpful abstraction /// that is more powerful than a simple `Vec`, and is primarily used to represent /// dot-separated paths. #[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub struct VecIdent(pub Vec); +impl<'a> IntoIterator for &'a VecIdent { + type IntoIter = std::slice::Iter<'a, Ident>; + type Item = &'a Ident; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} impl From for Vec> { fn from(v: VecIdent) -> Self { v.0.iter().map(|i| i.name.clone()).collect() @@ -1462,8 +1469,8 @@ impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut buf = Vec::with_capacity(self.0.len()); - for ident in self.0.iter() { - buf.push(format!("{}", ident)); + for ident in &self.0 { + buf.push(format!("{ident}")); } if buf.len() > 1 { // use square brackets only if there are more than one ident @@ -1475,11 +1482,12 @@ impl Display for VecIdent { } impl VecIdent { /// constructs an iter over the [Ident]s that this contains. - pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { + pub fn iter(&self) -> std::slice::Iter<'_, Ident> { self.0.iter() } - /// the conjoined span of all idents in the VecIdent + /// the conjoined span of all idents in the `VecIdent` + #[must_use] pub fn span(&self) -> Span { Span { lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), diff --git a/compiler/qsc_fir/src/global.rs b/compiler/qsc_fir/src/global.rs index f0406c081b..36b4b3c914 100644 --- a/compiler/qsc_fir/src/global.rs +++ b/compiler/qsc_fir/src/global.rs @@ -80,9 +80,9 @@ impl FromIterator for Table { } Self { - namespaces, tys, terms, + namespaces, } } } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 1ae3493996..00b77b777b 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -267,7 +267,7 @@ impl GlobalScope { } /// Creates a namespace in the namespace mapping. Note that namespaces are tracked separately from their - /// item contents. This returns a [NamespaceId] which you can use to add more tys and terms to the scope. + /// item contents. This returns a [`NamespaceId`] which you can use to add more tys and terms to the scope. fn insert_or_find_namespace(&mut self, name: impl Into>>) -> NamespaceId { self.namespaces.insert_or_find_namespace(name.into()) } @@ -302,9 +302,7 @@ impl PartialEq for Open { impl PartialOrd for Open { fn partial_cmp(&self, other: &Self) -> Option { - let a: usize = self.namespace.into(); - let b: usize = other.namespace.into(); - a.partial_cmp(&b) + Some(self.cmp(other)) } } @@ -466,7 +464,7 @@ impl Resolver { dropped_name .namespace .iter() - .map(|x| x.to_string()) + .map(std::string::ToString::to_string) .collect::>() .join("."), dropped_name.name @@ -521,18 +519,15 @@ impl Resolver { } fn bind_open(&mut self, name: &VecIdent, alias: &Option>) { - let id = match self.globals.find_namespace(name) { - Some(id) => id, - None => { - self.errors.push(Error::NotFound( - name.iter() - .map(|x| x.name.to_string()) - .collect::>() - .join("."), - name.span(), - )); - return; - } + let Some(id) = self.globals.find_namespace(name) else { + self.errors.push(Error::NotFound( + name.iter() + .map(|x| x.name.to_string()) + .collect::>() + .join("."), + name.span(), + )); + return; }; let alias = alias .as_ref() @@ -857,7 +852,7 @@ impl GlobalTable { scope: GlobalScope { tys, terms: FxHashMap::default(), - namespaces: Default::default(), + namespaces: NamespaceTreeRoot::default(), intrinsics: FxHashSet::default(), }, } @@ -1023,7 +1018,7 @@ fn bind_global_item( decl.name.name.to_string(), namespace_name.to_string(), decl.name.span, - )) + )); } Entry::Vacant(entry) => { entry.insert(res); @@ -1115,12 +1110,16 @@ fn resolve<'a>( return Ok(res); } } - let aliases = FxHashMap::from_iter(scope.opens.iter().map(|(alias, opens)| { - ( - alias.clone(), - opens.iter().cloned().map(|x| (x.namespace, x)).collect(), - ) - })); + let aliases = scope + .opens + .iter() + .map(|(alias, opens)| { + ( + alias.clone(), + opens.iter().cloned().map(|x| (x.namespace, x)).collect(), + ) + }) + .collect::>(); let scope_opens = scope .opens @@ -1135,19 +1134,23 @@ fn resolve<'a>( scope_opens .into_iter() .map(|open @ Open { namespace, .. }| (namespace, open.clone())), - aliases, + &aliases, ); - if explicit_open_candidates.len() == 1 { - return Ok(single(explicit_open_candidates.into_keys()).unwrap()); - } else if explicit_open_candidates.len() > 1 { - return ambiguous_symbol_error( - globals, - provided_symbol_name, - provided_symbol_str, - explicit_open_candidates, - ); + match explicit_open_candidates.len() { + 1 => { + return Ok(single(explicit_open_candidates.into_keys()) + .expect("we asserted on the length, so this is infallible")) + } + len if len > 1 => { + return ambiguous_symbol_error( + globals, + provided_symbol_name, + provided_symbol_str, + explicit_open_candidates, + ) + } + _ => (), } - if scope.kind == ScopeKind::Callable { // Since local callables are not closures, hide local variables in parent scopes. vars = false; @@ -1164,7 +1167,7 @@ fn resolve<'a>( provided_symbol_str, prelude.into_iter().map(|(a, b)| (b, a)), // there are no aliases in the prelude - Default::default(), + &FxHashMap::default(), ); if prelude_candidates.len() > 1 { @@ -1204,12 +1207,13 @@ fn resolve<'a>( provided_symbol_str, vec![(globals.namespaces.root_id(), ())].into_iter(), // there are no aliases in globals - Default::default(), + &FxHashMap::default(), ); // we don't have to worry about having extra candidates here, as we are only looking at the root, // and that's only one namespace. individual namespaces cannot have duplicate declarations. if global_candidates.len() == 1 { - return Ok(single(global_candidates.into_keys()).unwrap()); + return Ok(single(global_candidates.into_keys()) + .expect("we asserted on the length, so this is infallible")); } Err(Error::NotFound( @@ -1244,7 +1248,7 @@ fn find_symbol_in_namespaces( provided_namespace_name: &Option, provided_symbol_str: &str, namespaces_to_search: T, - aliases: FxHashMap>, Vec<(NamespaceId, O)>>, + aliases: &FxHashMap>, Vec<(NamespaceId, O)>>, ) -> FxHashMap where T: Iterator, @@ -1256,7 +1260,7 @@ where let mut candidates = vec![]; for (ns_id, open) in opens { if let Some(res) = globals.get(kind, *ns_id, provided_symbol_str) { - candidates.push((res.clone(), open.clone())); + candidates.push((*res, open.clone())); } } if !candidates.is_empty() { @@ -1272,13 +1276,13 @@ where if let Some(ref provided_namespace_name) = provided_namespace_name { if let Some(namespace) = candidate_namespace.find_namespace(provided_namespace_name) { if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { - candidates.insert(res.clone(), open.clone()); + candidates.insert(*res, open.clone()); } } } if let Some(res) = globals.get(kind, candidate_namespace_id, provided_symbol_str) { - candidates.insert(res.clone(), open); + candidates.insert(*res, open); } } if candidates.len() > 1 { @@ -1304,7 +1308,7 @@ fn prelude_namespaces(globals: &GlobalScope) -> Vec<(String, NamespaceId)> { // add prelude to the list of candidate namespaces last, as they are the final fallback for a symbol for prelude_namespace in PRELUDE { let prelude_name = prelude_namespace - .into_iter() + .iter() .map(|x| -> Rc { Rc::from(*x) }) .collect::>(); prelude.push(( diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index 6f96f3dec7..c6952fc51e 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -55,7 +55,7 @@ impl<'a> Renamer<'a> { names, changes: Vec::new(), namespaces, - aliases: Default::default(), + aliases: FxHashMap::default(), } } @@ -95,12 +95,11 @@ impl Visitor<'_> for Renamer<'_> { } fn visit_item(&mut self, item: &'_ Item) { - match &*item.kind { - ItemKind::Open(namespace, Some(alias)) => { - let ns_id = self.namespaces.find_namespace(namespace).unwrap(); - self.aliases.insert(vec![alias.name.clone()], ns_id); - } - _ => (), + if let ItemKind::Open(namespace, Some(alias)) = &*item.kind { + let Some(ns_id) = self.namespaces.find_namespace(namespace) else { + return; + }; + self.aliases.insert(vec![alias.name.clone()], ns_id); } visit::walk_item(self, item); } diff --git a/compiler/qsc_hir/src/global.rs b/compiler/qsc_hir/src/global.rs index 630a0237a6..3ddf8b5232 100644 --- a/compiler/qsc_hir/src/global.rs +++ b/compiler/qsc_hir/src/global.rs @@ -94,9 +94,9 @@ impl FromIterator for Table { } Self { - namespaces, tys, terms, + namespaces, } } } diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs index 4c7e174748..28613f99df 100644 --- a/compiler/qsc_hir/src/hir.rs +++ b/compiler/qsc_hir/src/hir.rs @@ -1134,12 +1134,19 @@ impl Display for QubitInitKind { } } -/// A [VecIdent] represents a sequence of idents. It provides a helpful abstraction +/// A [`VecIdent`] represents a sequence of idents. It provides a helpful abstraction /// that is more powerful than a simple `Vec`, and is primarily used to represent /// dot-separated paths. #[derive(Clone, Debug, Eq, Hash, PartialEq, Default)] pub struct VecIdent(pub Vec); +impl<'a> IntoIterator for &'a VecIdent { + type IntoIter = std::slice::Iter<'a, Ident>; + type Item = &'a Ident; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} impl From for Vec> { fn from(v: VecIdent) -> Self { v.0.iter().map(|i| i.name.clone()).collect() @@ -1174,8 +1181,8 @@ impl Display for VecIdent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut buf = Vec::with_capacity(self.0.len()); - for ident in self.0.iter() { - buf.push(format!("{}", ident)); + for ident in &self.0 { + buf.push(format!("{ident}")); } if buf.len() > 1 { // use square brackets only if there are more than one ident @@ -1187,11 +1194,12 @@ impl Display for VecIdent { } impl VecIdent { /// constructs an iter over the [Ident]s that this contains. - pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Ident> { + pub fn iter(&self) -> std::slice::Iter<'_, Ident> { self.0.iter() } - /// the conjoined span of all idents in the VecIdent + /// the conjoined span of all idents in the `VecIdent` + #[must_use] pub fn span(&self) -> Span { Span { lo: self.0.first().map(|i| i.span.lo).unwrap_or_default(), @@ -1199,12 +1207,14 @@ impl VecIdent { } } - /// Whether or not the first ident in this [VecIdent] matches `arg` + /// Whether or not the first ident in this [`VecIdent`] matches `arg` + #[must_use] pub fn starts_with(&self, arg: &str) -> bool { - self.0.first().map(|i| &*i.name == arg).unwrap_or_default() + self.0.first().is_some_and(|i| &*i.name == arg) } - /// Whether or not the first `n` idents in this [VecIdent] match `arg` + /// Whether or not the first `n` idents in this [`VecIdent`] match `arg` + #[must_use] pub fn starts_with_sequence(&self, arg: &[&str]) -> bool { if arg.len() > self.0.len() { return false; @@ -1214,11 +1224,12 @@ impl VecIdent { return false; } } - return true; + true } - /// The stringified dot-separated path of the idents in this [VecIdent] + /// The stringified dot-separated path of the idents in this [`VecIdent`] /// E.g. `a.b.c` + #[must_use] pub fn name(&self) -> String { self.0 .iter() diff --git a/compiler/qsc_hir/src/mut_visit.rs b/compiler/qsc_hir/src/mut_visit.rs index ea246dab0c..bf77b7ed8d 100644 --- a/compiler/qsc_hir/src/mut_visit.rs +++ b/compiler/qsc_hir/src/mut_visit.rs @@ -54,8 +54,8 @@ pub trait MutVisitor: Sized { } } pub fn walk_vec_ident(vis: &mut impl MutVisitor, ident: &mut crate::hir::VecIdent) { - for ref mut ident in ident.0.iter_mut() { - vis.visit_ident(ident) + for ref mut ident in &mut ident.0 { + vis.visit_ident(ident); } } pub fn walk_package(vis: &mut impl MutVisitor, package: &mut Package) { diff --git a/language_service/src/compilation.rs b/language_service/src/compilation.rs index 14f696cdfa..1406cc4255 100644 --- a/language_service/src/compilation.rs +++ b/language_service/src/compilation.rs @@ -226,7 +226,7 @@ impl Compilation { .expect("user package should exist") .ast .namespaces - .find_namespace(ns.into_iter().map(|s| Rc::from(s)).collect::>()) + .find_namespace(ns.into_iter().map(Rc::from).collect::>()) .expect("namespace should exist") } } diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index bc1e736291..cf3eb6d010 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -462,8 +462,7 @@ impl CompletionListBuilder { Some(start) => { additional_edits.push(TextEdit { new_text: format!( - "open {};{}", - namespace, indent, + "open {namespace};{indent}", ), range: start, }); From eb33cd42f23c45705b8af42196f01c3e98ad78a7 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 19:31:41 -0700 Subject: [PATCH 62/76] wip on clippy --- compiler/qsc_frontend/src/resolve.rs | 184 +++++++++++++++++---------- 1 file changed, 120 insertions(+), 64 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 00b77b777b..7ce2f57507 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1099,62 +1099,15 @@ fn resolve<'a>( provided_namespace_name: &Option, ) -> Result { let scopes = scopes.collect::>(); - let mut vars = true; - let provided_symbol_str = &(*provided_symbol_name.name); - for scope in &scopes { - if provided_namespace_name.is_none() { - if let Some(res) = resolve_scope_locals(kind, globals, scope, vars, provided_symbol_str) - { - // Local declarations shadow everything. - return Ok(res); - } - } - let aliases = scope - .opens - .iter() - .map(|(alias, opens)| { - ( - alias.clone(), - opens.iter().cloned().map(|x| (x.namespace, x)).collect(), - ) - }) - .collect::>(); - - let scope_opens = scope - .opens - .iter() - .flat_map(|(_, open)| open.clone()) - .collect::>(); - let explicit_open_candidates = find_symbol_in_namespaces( - kind, - globals, - provided_namespace_name, - provided_symbol_str, - scope_opens - .into_iter() - .map(|open @ Open { namespace, .. }| (namespace, open.clone())), - &aliases, - ); - match explicit_open_candidates.len() { - 1 => { - return Ok(single(explicit_open_candidates.into_keys()) - .expect("we asserted on the length, so this is infallible")) - } - len if len > 1 => { - return ambiguous_symbol_error( - globals, - provided_symbol_name, - provided_symbol_str, - explicit_open_candidates, - ) - } - _ => (), - } - if scope.kind == ScopeKind::Callable { - // Since local callables are not closures, hide local variables in parent scopes. - vars = false; - } + if let Some(value) = check_all_scopes( + kind, + globals, + provided_symbol_name, + provided_namespace_name, + &scopes, + ) { + return value; } let prelude = prelude_namespaces(globals); @@ -1164,7 +1117,7 @@ fn resolve<'a>( kind, globals, provided_namespace_name, - provided_symbol_str, + provided_symbol_name, prelude.into_iter().map(|(a, b)| (b, a)), // there are no aliases in the prelude &FxHashMap::default(), @@ -1204,7 +1157,7 @@ fn resolve<'a>( kind, globals, provided_namespace_name, - provided_symbol_str, + provided_symbol_name, vec![(globals.namespaces.root_id(), ())].into_iter(), // there are no aliases in globals &FxHashMap::default(), @@ -1217,15 +1170,112 @@ fn resolve<'a>( } Err(Error::NotFound( - provided_symbol_str.to_string(), + provided_symbol_name.name.to_string(), provided_symbol_name.span, )) } +/// Checks all given scopes, in the correct order, for a resolution. +/// Calls `check_scoped_resolutions` on each scope, and tracks if we should allow local variables in closures in parent scopes +/// using the `vars` parameter. +fn check_all_scopes( + kind: NameKind, + globals: &GlobalScope, + provided_symbol_name: &Ident, + provided_namespace_name: &Option, + scopes: &Vec<&Scope>, +) -> Option> { + let mut vars = true; + + for scope in scopes { + if let Some(value) = check_scoped_resolutions( + kind, + globals, + provided_symbol_name, + provided_namespace_name, + &mut vars, + scope, + ) { + return Some(value); + } + } + None +} + +/// In a given [Scope], check: +/// 1. if any locally declared symbols match `provided_symbol_name` +/// 2. if any aliases in this scope match the provided namespace, and if they contain `provided_symbol_name` +/// 3. if any opens in this scope contain the `provided_symbol_name` +fn check_scoped_resolutions( + kind: NameKind, + globals: &GlobalScope, + provided_symbol_name: &Ident, + provided_namespace_name: &Option, + vars: &mut bool, + scope: &Scope, +) -> Option> { + if provided_namespace_name.is_none() { + if let Some(res) = resolve_scope_locals( + kind, + globals, + scope, + *vars, + (&*provided_symbol_name.name).into(), + ) { + // Local declarations shadow everything. + return Some(Ok(res)); + } + } + let aliases = scope + .opens + .iter() + .map(|(alias, opens)| { + ( + alias.clone(), + opens.iter().cloned().map(|x| (x.namespace, x)).collect(), + ) + }) + .collect::>(); + + let scope_opens = scope + .opens + .iter() + .flat_map(|(_, open)| open.clone()) + .collect::>(); + let explicit_open_candidates = find_symbol_in_namespaces( + kind, + globals, + provided_namespace_name, + provided_symbol_name, + scope_opens + .into_iter() + .map(|open @ Open { namespace, .. }| (namespace, open.clone())), + &aliases, + ); + match explicit_open_candidates.len() { + 1 => { + return Some(Ok(single(explicit_open_candidates.into_keys()) + .expect("we asserted on the length, so this is infallible"))) + } + len if len > 1 => { + return Some(ambiguous_symbol_error( + globals, + provided_symbol_name, + explicit_open_candidates, + )) + } + _ => (), + } + if scope.kind == ScopeKind::Callable { + // Since local callables are not closures, hide local variables in parent scopes. + *vars = false; + } + None +} + fn ambiguous_symbol_error( globals: &GlobalScope, provided_symbol_name: &Ident, - provided_symbol_str: &str, candidates: FxHashMap, ) -> Result { let mut opens: Vec<_> = candidates.into_values().collect(); @@ -1233,7 +1283,7 @@ fn ambiguous_symbol_error( let (first_open_ns, _) = globals.namespaces.find_id(&opens[0].namespace); let (second_open_ns, _) = globals.namespaces.find_id(&opens[1].namespace); Err(Error::Ambiguous { - name: provided_symbol_str.to_string(), + name: provided_symbol_name.name.to_string(), first_open: first_open_ns.join("."), second_open: second_open_ns.join("."), name_span: provided_symbol_name.span, @@ -1246,7 +1296,7 @@ fn find_symbol_in_namespaces( kind: NameKind, globals: &GlobalScope, provided_namespace_name: &Option, - provided_symbol_str: &str, + provided_symbol_name: &Ident, namespaces_to_search: T, aliases: &FxHashMap>, Vec<(NamespaceId, O)>>, ) -> FxHashMap @@ -1259,7 +1309,7 @@ where if let Some(opens) = aliases.get(&(Into::>>::into(provided_namespace_name))) { let mut candidates = vec![]; for (ns_id, open) in opens { - if let Some(res) = globals.get(kind, *ns_id, provided_symbol_str) { + if let Some(res) = globals.get(kind, *ns_id, (&*provided_symbol_name.name).into()) { candidates.push((*res, open.clone())); } } @@ -1275,13 +1325,19 @@ where if let Some(ref provided_namespace_name) = provided_namespace_name { if let Some(namespace) = candidate_namespace.find_namespace(provided_namespace_name) { - if let Some(res) = globals.get(kind, namespace, provided_symbol_str) { + if let Some(res) = + globals.get(kind, namespace, (&*provided_symbol_name.name).into()) + { candidates.insert(*res, open.clone()); } } } - if let Some(res) = globals.get(kind, candidate_namespace_id, provided_symbol_str) { + if let Some(res) = globals.get( + kind, + candidate_namespace_id, + (&*provided_symbol_name.name).into(), + ) { candidates.insert(*res, open); } } From 24a8a2234871fade9d19d43e63fa17f956b2aea3 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 19:39:57 -0700 Subject: [PATCH 63/76] fix all clippy warnings in compiler subdir --- .gitignore | 1 + compiler/qsc_frontend/src/resolve.rs | 22 +++++-------------- compiler/qsc_passes/src/invert_block.rs | 2 +- compiler/qsc_passes/src/loop_unification.rs | 4 ++-- .../src/replace_qubit_allocation.rs | 10 ++++----- language_service/src/completion.rs | 8 ++++--- 6 files changed, 20 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 8cbb2b6615..70b962534d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ __pycache__/ /fuzz/Cargo.lock .mypy_cache/ .pytest_cache/ +.idea/ diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 7ce2f57507..fda0b491ff 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1215,13 +1215,9 @@ fn check_scoped_resolutions( scope: &Scope, ) -> Option> { if provided_namespace_name.is_none() { - if let Some(res) = resolve_scope_locals( - kind, - globals, - scope, - *vars, - (&*provided_symbol_name.name).into(), - ) { + if let Some(res) = + resolve_scope_locals(kind, globals, scope, *vars, &provided_symbol_name.name) + { // Local declarations shadow everything. return Some(Ok(res)); } @@ -1309,7 +1305,7 @@ where if let Some(opens) = aliases.get(&(Into::>>::into(provided_namespace_name))) { let mut candidates = vec![]; for (ns_id, open) in opens { - if let Some(res) = globals.get(kind, *ns_id, (&*provided_symbol_name.name).into()) { + if let Some(res) = globals.get(kind, *ns_id, &provided_symbol_name.name) { candidates.push((*res, open.clone())); } } @@ -1325,19 +1321,13 @@ where if let Some(ref provided_namespace_name) = provided_namespace_name { if let Some(namespace) = candidate_namespace.find_namespace(provided_namespace_name) { - if let Some(res) = - globals.get(kind, namespace, (&*provided_symbol_name.name).into()) - { + if let Some(res) = globals.get(kind, namespace, &provided_symbol_name.name) { candidates.insert(*res, open.clone()); } } } - if let Some(res) = globals.get( - kind, - candidate_namespace_id, - (&*provided_symbol_name.name).into(), - ) { + if let Some(res) = globals.get(kind, candidate_namespace_id, &provided_symbol_name.name) { candidates.insert(*res, open); } } diff --git a/compiler/qsc_passes/src/invert_block.rs b/compiler/qsc_passes/src/invert_block.rs index 87cc4ad958..e11d5a0410 100644 --- a/compiler/qsc_passes/src/invert_block.rs +++ b/compiler/qsc_passes/src/invert_block.rs @@ -331,7 +331,7 @@ fn make_range_field(range_id: NodeId, field: PrimField) -> Expr { fn make_array_index_range_reverse(core: &Table, arr_id: NodeId, arr_ty: &Ty) -> Expr { let ns = core .find_namespace(vec!["Microsoft".into(), "Quantum".into(), "Core".into()]) - .unwrap(); + .expect("prelude namespaces should exist"); let len = Box::new(Expr { id: NodeId::default(), span: Span::default(), diff --git a/compiler/qsc_passes/src/loop_unification.rs b/compiler/qsc_passes/src/loop_unification.rs index 4cdaeadcda..49f03f2478 100644 --- a/compiler/qsc_passes/src/loop_unification.rs +++ b/compiler/qsc_passes/src/loop_unification.rs @@ -32,7 +32,6 @@ impl LoopUni<'_> { span: Span, ) -> Expr { let cond_span = cond.span; - let continue_cond_id = self.gen_ident("continue_cond", Ty::Prim(Prim::Bool), cond_span); let continue_cond_init = continue_cond_id.gen_id_init( Mutability::Mutable, @@ -118,6 +117,7 @@ impl LoopUni<'_> { } } + #[allow(clippy::too_many_lines)] fn visit_for_array( &mut self, iter: Pat, @@ -136,7 +136,7 @@ impl LoopUni<'_> { let ns = self .core .find_namespace(vec!["Microsoft".into(), "Quantum".into(), "Core".into()]) - .unwrap(); + .expect("prelude namespaces should exist"); let mut len_callee = create_gen_core_ref( self.core, ns, diff --git a/compiler/qsc_passes/src/replace_qubit_allocation.rs b/compiler/qsc_passes/src/replace_qubit_allocation.rs index 3589ea35e9..7c65a5d331 100644 --- a/compiler/qsc_passes/src/replace_qubit_allocation.rs +++ b/compiler/qsc_passes/src/replace_qubit_allocation.rs @@ -241,7 +241,7 @@ impl<'a> ReplaceQubitAllocation<'a> { let ns = self .core .find_namespace(vec!["QIR".into(), "Runtime".into()]) - .unwrap(); + .expect("prelude namespaces should exist"); let mut call_expr = create_gen_core_ref( self.core, ns, @@ -257,7 +257,7 @@ impl<'a> ReplaceQubitAllocation<'a> { let ns = self .core .find_namespace(vec!["QIR".into(), "Runtime".into()]) - .unwrap(); + .expect("prelude namespaces should exist"); let mut call_expr = create_gen_core_ref(self.core, ns, "AllocateQubitArray", Vec::new(), ident.span); call_expr.id = self.assigner.next_node(); @@ -268,7 +268,7 @@ impl<'a> ReplaceQubitAllocation<'a> { let ns = self .core .find_namespace(vec!["QIR".into(), "Runtime".into()]) - .unwrap(); + .expect("prelude namespaces should exist"); let mut call_expr = create_gen_core_ref( self.core, ns, @@ -284,7 +284,7 @@ impl<'a> ReplaceQubitAllocation<'a> { let ns = self .core .find_namespace(vec!["QIR".into(), "Runtime".into()]) - .unwrap(); + .expect("prelude namespaces should exist"); let mut call_expr = create_gen_core_ref(self.core, ns, "ReleaseQubitArray", Vec::new(), ident.span); call_expr.id = self.assigner.next_node(); @@ -420,7 +420,7 @@ fn create_qubit_global_alloc( fn qubit_alloc_expr(assigner: &mut Assigner, core: &Table, qubit_init: QubitInit) -> Expr { let ns = core .find_namespace(vec!["QIR".into(), "Runtime".into()]) - .unwrap(); + .expect("prelude namespaces should exist"); match qubit_init.kind { QubitInitKind::Array(mut expr) => { let mut call_expr = create_gen_core_ref( diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index cf3eb6d010..8fe09014df 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -23,6 +23,8 @@ const PRELUDE: [[&str; 3]; 4] = [ ["Microsoft", "Quantum", "Intrinsic"], ["Microsoft", "Quantum", "Measurement"], ]; +type NamespaceName = Vec>; +type NamespaceAlias = Rc; pub(crate) fn get_completions( compilation: &Compilation, @@ -268,7 +270,7 @@ impl CompletionListBuilder { fn push_globals( &mut self, compilation: &Compilation, - opens: &[(Vec>, Option>)], + opens: &[(NamespaceName, Option)], insert_open_range: Option, current_namespace_name: &Option>>, indent: &String, @@ -392,7 +394,7 @@ impl CompletionListBuilder { compilation: &'a Compilation, package_id: PackageId, // name and alias - opens: &'a [(Vec>, Option>)], + opens: &'a [(NamespaceName, Option)], // The range at which to insert an open statement if one is needed insert_open_at: Option, // The name of the current namespace, if any -- @@ -611,7 +613,7 @@ fn local_completion( struct ContextFinder { offset: u32, context: Context, - opens: Vec<(Vec>, Option>)>, + opens: Vec<(NamespaceName, Option)>, start_of_namespace: Option, current_namespace_name: Option>>, } From ef1d739f556212b7541e4b7a0c712ed9fa8b1fa1 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 19:42:57 -0700 Subject: [PATCH 64/76] fix language service open namespace text edit issue --- language_service/src/completion.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index 8fe09014df..ff6ba316f3 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -464,7 +464,7 @@ impl CompletionListBuilder { Some(start) => { additional_edits.push(TextEdit { new_text: format!( - "open {namespace};{indent}", + "open {};{indent}", namespace.name() ), range: start, }); From f235671bbdd0e3aa137700b29871184a5fc486b1 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 19:51:51 -0700 Subject: [PATCH 65/76] deduplicate prelude definition --- .../qsc_data_structures/src/namespaces.rs | 2 +- compiler/qsc_frontend/src/resolve.rs | 19 ++++++++----------- language_service/src/completion.rs | 14 ++++++-------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/compiler/qsc_data_structures/src/namespaces.rs b/compiler/qsc_data_structures/src/namespaces.rs index 3fabd69a74..0a6dc3be21 100644 --- a/compiler/qsc_data_structures/src/namespaces.rs +++ b/compiler/qsc_data_structures/src/namespaces.rs @@ -4,7 +4,7 @@ mod tests; use rustc_hash::FxHashMap; use std::{fmt::Display, iter::Peekable, ops::Deref, rc::Rc}; -const PRELUDE: [[&str; 3]; 4] = [ +pub const PRELUDE: [[&str; 3]; 4] = [ ["Microsoft", "Quantum", "Canon"], ["Microsoft", "Quantum", "Core"], ["Microsoft", "Quantum", "Intrinsic"], diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index fda0b491ff..299124858c 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -14,7 +14,7 @@ use qsc_ast::{ use qsc_data_structures::{ index_map::IndexMap, - namespaces::{NamespaceId, NamespaceTreeRoot}, + namespaces::{NamespaceId, NamespaceTreeRoot, PRELUDE}, span::Span, }; use qsc_hir::{ @@ -30,14 +30,6 @@ use thiserror::Error; use crate::compile::preprocess::TrackedName; -// TODO dedup this? -const PRELUDE: [&[&str; 3]; 4] = [ - &["Microsoft", "Quantum", "Canon"], - &["Microsoft", "Quantum", "Core"], - &["Microsoft", "Quantum", "Intrinsic"], - &["Microsoft", "Quantum", "Measurement"], -]; - // All AST Path nodes get mapped // All AST Ident nodes get mapped, except those under AST Path nodes pub(super) type Names = IndexMap; @@ -1084,13 +1076,18 @@ fn decl_is_intrinsic(decl: &CallableDecl) -> bool { } } -/// TODO(alex): rename namespaces to show what are candidates and what are being passed in. Document this, especially detailed shadowing -/// rules. +/// Resolves a given symbol and namespace name, according to the Q# shadowing rules. /// Shadowing rules are as follows: /// - Local variables shadow everything. They are the first priority. /// - Next, we check open statements for an explicit open. /// - Then, we check the prelude. /// - Lastly, we check the global namespace. +/// In the example `Foo.Bar.Baz()` -- the `provided_namespace_name` would be +///`Foo.Bar` and the `provided_symbol_name` would be `Baz`. +/// +/// In the example `Foo()` -- the `provided_namespace_name` would be `None` and the +/// `provided_symbol_name` would be `Foo`. +/// returns the resolution if successful, or an error if not. fn resolve<'a>( kind: NameKind, globals: &GlobalScope, diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index ff6ba316f3..6d5d2c7154 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -13,16 +13,13 @@ use qsc::display::{CodeDisplay, Lookup}; use qsc::hir::{ItemKind, Package, PackageId, Visibility}; use qsc::line_column::{Encoding, Position, Range}; -use qsc::resolve::{Local, LocalKind}; +use qsc::{ + resolve::{Local, LocalKind}, + PRELUDE, +}; use rustc_hash::FxHashSet; use std::rc::Rc; -const PRELUDE: [[&str; 3]; 4] = [ - ["Microsoft", "Quantum", "Canon"], - ["Microsoft", "Quantum", "Core"], - ["Microsoft", "Quantum", "Intrinsic"], - ["Microsoft", "Quantum", "Measurement"], -]; type NamespaceName = Vec>; type NamespaceAlias = Rc; @@ -464,7 +461,8 @@ impl CompletionListBuilder { Some(start) => { additional_edits.push(TextEdit { new_text: format!( - "open {};{indent}", namespace.name() + "open {};{indent}", + namespace.name() ), range: start, }); From 9a6924600f56407e275f5da438a12fc43c086bce Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 3 Apr 2024 20:27:38 -0700 Subject: [PATCH 66/76] simpify resolve fn --- compiler/qsc_frontend/src/resolve.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 299124858c..5c158bfdc9 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1107,15 +1107,14 @@ fn resolve<'a>( return value; } - let prelude = prelude_namespaces(globals); - // then, check prelude + // check the prelude if provided_namespace_name.is_none() { let prelude_candidates = find_symbol_in_namespaces( kind, globals, provided_namespace_name, provided_symbol_name, - prelude.into_iter().map(|(a, b)| (b, a)), + prelude_namespaces(globals).into_iter().map(|(a, b)| (b, a)), // there are no aliases in the prelude &FxHashMap::default(), ); @@ -1159,11 +1158,11 @@ fn resolve<'a>( // there are no aliases in globals &FxHashMap::default(), ); - // we don't have to worry about having extra candidates here, as we are only looking at the root, + + // we don't have to throw an error if there are extra candidates here, as we are only looking at the root, // and that's only one namespace. individual namespaces cannot have duplicate declarations. - if global_candidates.len() == 1 { - return Ok(single(global_candidates.into_keys()) - .expect("we asserted on the length, so this is infallible")); + if let Some(res) = single(global_candidates.into_keys()) { + return Ok(res); } Err(Error::NotFound( @@ -1171,7 +1170,6 @@ fn resolve<'a>( provided_symbol_name.span, )) } - /// Checks all given scopes, in the correct order, for a resolution. /// Calls `check_scoped_resolutions` on each scope, and tracks if we should allow local variables in closures in parent scopes /// using the `vars` parameter. From ad12a6260f8629512a595d1fad69884fc741ba55 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 4 Apr 2024 08:28:28 -0700 Subject: [PATCH 67/76] simplify resolution --- compiler/qsc_frontend/src/resolve.rs | 55 +++++++++++++++++++--------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index 5c158bfdc9..b14c9827da 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1197,10 +1197,25 @@ fn check_all_scopes( None } +/// This function checks scopes for a given symbol and namespace name. /// In a given [Scope], check: /// 1. if any locally declared symbols match `provided_symbol_name` /// 2. if any aliases in this scope match the provided namespace, and if they contain `provided_symbol_name` /// 3. if any opens in this scope contain the `provided_symbol_name` +/// It follows the Q# shadowing rules: +/// - Local variables shadow everything. They are the first priority. +/// - Next, we check open statements for an explicit open. +/// - Then, we check the prelude. +/// - Lastly, we check the global namespace. +/// +/// # Parameters +/// +/// * `kind` - The [`NameKind`] of the name +/// * `globals` - The global scope to resolve the name against. +/// * `provided_symbol_name` - The symbol name to resolve. +/// * `provided_namespace_name` - The namespace of the symbol, if any. +/// * `vars` - A mutable reference to a boolean indicating whether to allow local variables in closures in parent scopes. +/// * `scope` - The scope to check for resolutions. fn check_scoped_resolutions( kind: NameKind, globals: &GlobalScope, @@ -1264,6 +1279,15 @@ fn check_scoped_resolutions( None } +/// This function always returns an `Err` variant of `Result`. The error is of type `Error::Ambiguous` and contains +/// the name of the ambiguous symbol and the namespaces that contain the conflicting entities. +/// # Arguments +/// +/// * `globals` - The global scope to resolve the name against. +/// * `provided_symbol_name` - The symbol name that is ambiguous. +/// * `candidates` - A map of possible resolutions for the symbol, each associated with the `Open` +/// statement that brought it into scope. Note that only the first two opens in +/// the candidates are actually used in the error message. fn ambiguous_symbol_error( globals: &GlobalScope, provided_symbol_name: &Ident, @@ -1310,19 +1334,22 @@ where } } let mut candidates = FxHashMap::default(); - // search through each candidate namespace to find the items for (candidate_namespace_id, open) in namespaces_to_search { + // Retrieve the namespace associated with the candidate_namespace_id from the global namespaces let candidate_namespace = globals.namespaces.find_id(&candidate_namespace_id).1; - if let Some(ref provided_namespace_name) = provided_namespace_name { - if let Some(namespace) = candidate_namespace.find_namespace(provided_namespace_name) { - if let Some(res) = globals.get(kind, namespace, &provided_symbol_name.name) { - candidates.insert(*res, open.clone()); - } - } - } + // Attempt to find a namespace within the candidate_namespace that matches the provided_namespace_name + let namespace = provided_namespace_name + .as_ref() + .and_then(|name| candidate_namespace.find_namespace(name)); - if let Some(res) = globals.get(kind, candidate_namespace_id, &provided_symbol_name.name) { + // Attempt to get the symbol from the global scope. If the namespace is None, use the candidate_namespace_id as a fallback + let res = namespace + .or(Some(candidate_namespace_id)) + .and_then(|ns_id| globals.get(kind, ns_id, &provided_symbol_name.name)); + + // If a symbol was found, insert it into the candidates map + if let Some(res) = res { candidates.insert(*res, open); } } @@ -1330,14 +1357,8 @@ where // If there are multiple candidates, remove unimplemented items. This allows resolution to // succeed in cases where both an older, unimplemented API and newer, implemented API with the // same name are both in scope without forcing the user to fully qualify the name. - let mut removals = Vec::new(); - for res in candidates.keys() { - if let Res::Item(_, ItemStatus::Unimplemented) = res { - removals.push(*res); - } - } - for res in removals { - candidates.remove(&res); + if candidates.len() > 1 { + candidates.retain(|&res, _| !matches!(res, Res::Item(_, ItemStatus::Unimplemented))); } } candidates From 36d7a31fc3a9cb92ab44337fbc5378eff8180e00 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 4 Apr 2024 08:51:26 -0700 Subject: [PATCH 68/76] further simplify resolution algorithms --- compiler/qsc_frontend/src/resolve.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index b14c9827da..a62332c2a6 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1319,20 +1319,23 @@ where T: Iterator, O: Clone + std::fmt::Debug, { - // replace aliases with real namespaces + // check aliases to see if the provided namespace is actually an alias if let Some(provided_namespace_name) = provided_namespace_name { if let Some(opens) = aliases.get(&(Into::>>::into(provided_namespace_name))) { - let mut candidates = vec![]; - for (ns_id, open) in opens { - if let Some(res) = globals.get(kind, *ns_id, &provided_symbol_name.name) { - candidates.push((*res, open.clone())); - } - } + let candidates: Vec<_> = opens + .iter() + .filter_map(|(ns_id, open)| { + globals + .get(kind, *ns_id, &provided_symbol_name.name) + .map(|res| (*res, open.clone())) + }) + .collect(); if !candidates.is_empty() { return candidates.into_iter().collect(); } } } + let mut candidates = FxHashMap::default(); for (candidate_namespace_id, open) in namespaces_to_search { // Retrieve the namespace associated with the candidate_namespace_id from the global namespaces @@ -1353,6 +1356,7 @@ where candidates.insert(*res, open); } } + if candidates.len() > 1 { // If there are multiple candidates, remove unimplemented items. This allows resolution to // succeed in cases where both an older, unimplemented API and newer, implemented API with the @@ -1364,6 +1368,7 @@ where candidates } +/// Fetch the name and namespace ID of all prelude namespaces. fn prelude_namespaces(globals: &GlobalScope) -> Vec<(String, NamespaceId)> { let mut prelude = vec![]; @@ -1371,19 +1376,18 @@ fn prelude_namespaces(globals: &GlobalScope) -> Vec<(String, NamespaceId)> { for prelude_namespace in PRELUDE { let prelude_name = prelude_namespace .iter() - .map(|x| -> Rc { Rc::from(*x) }) + .map(|s| Rc::from(*s)) .collect::>(); prelude.push(( prelude_name.join("."), globals .namespaces .find_namespace(prelude_name) - .expect("prelude namespaces should exist"), + .expect("prelude should always exist in the namespace map"), )); } prelude } - /// Implements shadowing rules within a single scope. /// A local variable always wins out against an item with the same name, even if they're declared in /// the same scope. It is implemented in a way that resembles Rust: From 804f4e7a2947d52bb3df17060f8793017c5a16f5 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 4 Apr 2024 10:34:47 -0700 Subject: [PATCH 69/76] Fix interpreter test case for printing stack traces --- compiler/qsc/src/interpret/debug.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/qsc/src/interpret/debug.rs b/compiler/qsc/src/interpret/debug.rs index 04975cf333..c1539c8740 100644 --- a/compiler/qsc/src/interpret/debug.rs +++ b/compiler/qsc/src/interpret/debug.rs @@ -83,7 +83,7 @@ fn get_item_file_name(store: &PackageStore, id: StoreItemId) -> Option { #[must_use] fn get_ns_name(item: &Item) -> Option { if let ItemKind::Namespace(ns, _) = &item.kind { - Some(format!("{ns}")) + Some(ns.name().to_string()) } else { None } From 8016603f4af8d6f88058f0d9939e19dea9909263 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 4 Apr 2024 15:16:01 -0700 Subject: [PATCH 70/76] fix katas test --- compiler/qsc_ast/src/ast.rs | 11 +++++ compiler/qsc_circuit/src/operations/tests.rs | 2 +- compiler/qsc_frontend/src/resolve.rs | 10 +++- compiler/qsc_frontend/src/resolve/tests.rs | 36 ++++++++++++++ samples/language/String.qs | 49 ++++++++++++++------ 5 files changed, 92 insertions(+), 16 deletions(-) diff --git a/compiler/qsc_ast/src/ast.rs b/compiler/qsc_ast/src/ast.rs index 15f1b0e74b..aeb82ed248 100644 --- a/compiler/qsc_ast/src/ast.rs +++ b/compiler/qsc_ast/src/ast.rs @@ -1373,6 +1373,17 @@ impl VecIdent { hi: self.0.last().map(|i| i.span.hi).unwrap_or_default(), } } + + /// The stringified dot-separated path of the idents in this [`VecIdent`] + /// E.g. `a.b.c` + #[must_use] + pub fn name(&self) -> String { + self.0 + .iter() + .map(|i| i.name.to_string()) + .collect::>() + .join(".") + } } impl Default for Ident { diff --git a/compiler/qsc_circuit/src/operations/tests.rs b/compiler/qsc_circuit/src/operations/tests.rs index 7ea31291cf..d350cd5709 100644 --- a/compiler/qsc_circuit/src/operations/tests.rs +++ b/compiler/qsc_circuit/src/operations/tests.rs @@ -44,7 +44,7 @@ fn compile_one_operation(code: &str) -> (Item, String) { ); ( only_callable.clone(), - format!("{only_namespace}.{callable_name}"), + format!("{}.{callable_name}", only_namespace.name()), ) } diff --git a/compiler/qsc_frontend/src/resolve.rs b/compiler/qsc_frontend/src/resolve.rs index a62332c2a6..652d9c38e4 100644 --- a/compiler/qsc_frontend/src/resolve.rs +++ b/compiler/qsc_frontend/src/resolve.rs @@ -1346,9 +1346,17 @@ where .as_ref() .and_then(|name| candidate_namespace.find_namespace(name)); + // if a namespace was provided, but not found, then this is not the correct namespace. + if provided_namespace_name.is_some() && namespace.is_none() { + continue; + } // Attempt to get the symbol from the global scope. If the namespace is None, use the candidate_namespace_id as a fallback let res = namespace - .or(Some(candidate_namespace_id)) + .or(if namespace.is_none() { + Some(candidate_namespace_id) + } else { + None + }) .and_then(|ns_id| globals.get(kind, ns_id, &provided_symbol_name.name)); // If a symbol was found, insert it into the candidates map diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index c6952fc51e..dfa734d440 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2782,3 +2782,39 @@ fn basic_hierarchical_namespace() { }"#]], ); } + +#[test] +fn test_katas_shadowing_use_case() { + check( + indoc! { + "namespace Kata { + operation ApplyX() : Unit { + // Do nothing. + } +} + +namespace Kata.Verification { + operation CheckSolution() : Bool { + let _ = Kata.ApplyX(); + let _ = ApplyX(); + } + + operation ApplyX() : Unit {} +} +" }, + &expect![["namespace namespace7 { + operation item1() : Unit { + // Do nothing. + } +} + +namespace namespace8 { + operation item3() : Bool { + let _ = namespace7.item1(); + let _ = item4(); + } + + operation item4() : Unit {} +}"]], + ); +} diff --git a/samples/language/String.qs b/samples/language/String.qs index 30cda14b4e..33b000d0d1 100644 --- a/samples/language/String.qs +++ b/samples/language/String.qs @@ -1,21 +1,42 @@ -/// # Sample -/// String -/// -/// # Description -/// Text as values that consist of a sequence of UTF-16 code units. -namespace MyQuantumApp { +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Kata { + open Microsoft.Quantum.Intrinsic; + + operation ApplyX(q : Qubit) : Unit is Adj + Ctl { + // Do nothing. + } +} + +namespace Kata.Verification { + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Intrinsic; @EntryPoint() - operation Main() : String { - // Strings literals are declared with double quotes: - let myString = "Foo"; + operation CheckSolution() : Bool { + VerifySingleQubitOperation(Kata.ApplyX, Kata.Verification.ApplyX) + } - // Strings can be concatenated with `+` - let myString = myString + "Bar"; + operation ApplyX(q : Qubit) : Unit is Adj + Ctl { + X(q); + } - // Q# supports string interpolation with the `$` prefix. - let myString = $"interpolated: {myString}"; + operation VerifySingleQubitOperation( + op : (Qubit => Unit is Adj + Ctl), + reference : (Qubit => Unit is Adj + Ctl)) + : Bool { + use (control, target) = (Qubit(), Qubit()); + within { + H(control); + } + apply { + Controlled op([control], target); + Adjoint Controlled reference([control], target); + } + let isCorrect = CheckAllZero([control, target]); + ResetAll([control, target]); - return myString; + isCorrect } } \ No newline at end of file From 5d60bfac355179a2e13a5538a724fad587c5a62d Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 4 Apr 2024 15:26:31 -0700 Subject: [PATCH 71/76] fix katas shadowing rules and doc gen tests --- compiler/qsc_doc_gen/src/generate_docs.rs | 2 +- compiler/qsc_frontend/src/resolve/tests.rs | 28 ++++++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 2c9167d29a..920fc245b4 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -166,7 +166,7 @@ fn get_namespace(package: &Package, item: &Item) -> Option> { if name.starts_with("QIR") { None // We ignore "QIR" namespaces } else { - Some(format!("{name}").into()) + Some(format!("{}", name.name()).into()) } } _ => None, diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index dfa734d440..09fb1779b6 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2657,7 +2657,7 @@ fn nested_namespaces_with_same_function_name() { namespace namespace8 { function item3() : Unit {} function item4() : Unit { - item3(); + item1(); item3(); // Should call Bar.A without needing to qualify } } @@ -2802,19 +2802,21 @@ namespace Kata.Verification { operation ApplyX() : Unit {} } " }, - &expect![["namespace namespace7 { - operation item1() : Unit { - // Do nothing. - } -} + &expect![[r#" + namespace namespace7 { + operation item1() : Unit { + // Do nothing. + } + } -namespace namespace8 { - operation item3() : Bool { - let _ = namespace7.item1(); - let _ = item4(); - } + namespace namespace8 { + operation item3() : Bool { + let _ = item1(); + let _ = item4(); + } - operation item4() : Unit {} -}"]], + operation item4() : Unit {} + } + "#]], ); } From b2cf9c6277f8afd6897f88c14d4ca015ab498b0d Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 4 Apr 2024 22:59:31 -0700 Subject: [PATCH 72/76] Fix resolve and RCA tests --- compiler/qsc_fir/src/fir.rs | 10 ++++++++++ compiler/qsc_frontend/src/compile/preprocess.rs | 8 ++------ compiler/qsc_frontend/src/resolve/tests.rs | 2 +- compiler/qsc_rca/src/overrider.rs | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/compiler/qsc_fir/src/fir.rs b/compiler/qsc_fir/src/fir.rs index dcbe07b38c..7e56020876 100644 --- a/compiler/qsc_fir/src/fir.rs +++ b/compiler/qsc_fir/src/fir.rs @@ -1481,6 +1481,16 @@ impl Display for VecIdent { } } impl VecIdent { + /// The stringified dot-separated path of the idents in this [`VecIdent`] + /// E.g. `a.b.c` + #[must_use] + pub fn name(&self) -> String { + self.0 + .iter() + .map(|i| i.name.to_string()) + .collect::>() + .join(".") + } /// constructs an iter over the [Ident]s that this contains. pub fn iter(&self) -> std::slice::Iter<'_, Ident> { self.0.iter() diff --git a/compiler/qsc_frontend/src/compile/preprocess.rs b/compiler/qsc_frontend/src/compile/preprocess.rs index aaaf4bb4d4..a78da8f6e7 100644 --- a/compiler/qsc_frontend/src/compile/preprocess.rs +++ b/compiler/qsc_frontend/src/compile/preprocess.rs @@ -76,12 +76,8 @@ impl MutVisitor for Conditional { ItemKind::Callable(callable) => { self.dropped_names.push(TrackedName { name: callable.name.name.clone(), - namespace: namespace - .name - .clone() - .iter() - .map(|x| Rc::from(x.to_string())) - .collect(), + namespace: (&namespace.name).into() + , }); } ItemKind::Ty(ident, _) => self.dropped_names.push(TrackedName { diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index 09fb1779b6..399ae6df74 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2023,7 +2023,7 @@ fn dropped_callable() { } } - // NotAvailable("Dropped", "Ident 2 [10-11] \"A\".Dropped", Span { lo: 100, hi: 107 }) + // NotAvailable("Dropped", "A.Dropped", Span { lo: 100, hi: 107 }) "#]], ); } diff --git a/compiler/qsc_rca/src/overrider.rs b/compiler/qsc_rca/src/overrider.rs index 2e1b9b1a14..a645ba3174 100644 --- a/compiler/qsc_rca/src/overrider.rs +++ b/compiler/qsc_rca/src/overrider.rs @@ -206,7 +206,7 @@ impl<'a> Visitor<'a> for Overrider<'a> { .items .iter() .filter_map(|(_, item)| match &item.kind { - ItemKind::Namespace(ident, items) => Some((format!("{ident}"), items)), + ItemKind::Namespace(ident, items) => Some((format!("{}", ident.name()), items)), _ => None, }); for (namespace_ident, namespace_items) in namespaces { From 42e6ec9ce81f4dae54f3ef2668ac9e4e1d64d0f9 Mon Sep 17 00:00:00 2001 From: sezna Date: Fri, 5 Apr 2024 07:31:39 -0700 Subject: [PATCH 73/76] update not available test --- compiler/qsc_ast/src/visit.rs | 1 - compiler/qsc_frontend/src/resolve/tests.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/qsc_ast/src/visit.rs b/compiler/qsc_ast/src/visit.rs index 900694d8f2..c96b0a84a8 100644 --- a/compiler/qsc_ast/src/visit.rs +++ b/compiler/qsc_ast/src/visit.rs @@ -85,7 +85,6 @@ pub fn walk_package<'a>(vis: &mut impl Visitor<'a>, package: &'a Package) { } pub fn walk_namespace<'a>(vis: &mut impl Visitor<'a>, namespace: &'a Namespace) { - // possibly this visit is incorrect? vis.visit_vec_ident(&namespace.name); namespace.items.iter().for_each(|i| vis.visit_item(i)); } diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index 399ae6df74..e62ba49e1b 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2077,8 +2077,8 @@ fn multiple_definition_dropped_is_not_found() { } } - // NotFound("B", Span { lo: 265, hi: 266 }) - // NotFound("C", Span { lo: 278, hi: 279 }) + // NotAvailable("B", "A.B", Span { lo: 265, hi: 266 }) + // NotAvailable("C", "A.C", Span { lo: 278, hi: 279 }) "#]], ); } From fe267f835a4bdf3461b6326441e90b344f16229b Mon Sep 17 00:00:00 2001 From: sezna Date: Fri, 5 Apr 2024 07:37:11 -0700 Subject: [PATCH 74/76] cargo fmt --- compiler/qsc_frontend/src/compile/preprocess.rs | 3 +-- compiler/qsc_frontend/src/resolve/tests.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/qsc_frontend/src/compile/preprocess.rs b/compiler/qsc_frontend/src/compile/preprocess.rs index a78da8f6e7..39bbb26ee7 100644 --- a/compiler/qsc_frontend/src/compile/preprocess.rs +++ b/compiler/qsc_frontend/src/compile/preprocess.rs @@ -76,8 +76,7 @@ impl MutVisitor for Conditional { ItemKind::Callable(callable) => { self.dropped_names.push(TrackedName { name: callable.name.name.clone(), - namespace: (&namespace.name).into() - , + namespace: (&namespace.name).into(), }); } ItemKind::Ty(ident, _) => self.dropped_names.push(TrackedName { diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index e62ba49e1b..e61d48b756 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -2786,8 +2786,7 @@ fn basic_hierarchical_namespace() { #[test] fn test_katas_shadowing_use_case() { check( - indoc! { - "namespace Kata { + indoc! {"namespace Kata { operation ApplyX() : Unit { // Do nothing. } From 03027ce2c554839a06a44a099a2914e64cd1c723 Mon Sep 17 00:00:00 2001 From: sezna Date: Fri, 5 Apr 2024 22:05:05 -0700 Subject: [PATCH 75/76] format string sample --- compiler/qsc_doc_gen/src/generate_docs.rs | 2 +- compiler/qsc_rca/src/overrider.rs | 2 +- samples/language/String.qs | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 920fc245b4..44b1075501 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -166,7 +166,7 @@ fn get_namespace(package: &Package, item: &Item) -> Option> { if name.starts_with("QIR") { None // We ignore "QIR" namespaces } else { - Some(format!("{}", name.name()).into()) + Some(Rc::from(name.name())) } } _ => None, diff --git a/compiler/qsc_rca/src/overrider.rs b/compiler/qsc_rca/src/overrider.rs index a645ba3174..9a4cbc59e5 100644 --- a/compiler/qsc_rca/src/overrider.rs +++ b/compiler/qsc_rca/src/overrider.rs @@ -206,7 +206,7 @@ impl<'a> Visitor<'a> for Overrider<'a> { .items .iter() .filter_map(|(_, item)| match &item.kind { - ItemKind::Namespace(ident, items) => Some((format!("{}", ident.name()), items)), + ItemKind::Namespace(ident, items) => Some((ident.name().to_string(), items)), _ => None, }); for (namespace_ident, namespace_items) in namespaces { diff --git a/samples/language/String.qs b/samples/language/String.qs index 33b000d0d1..34f8884a9b 100644 --- a/samples/language/String.qs +++ b/samples/language/String.qs @@ -24,13 +24,12 @@ namespace Kata.Verification { operation VerifySingleQubitOperation( op : (Qubit => Unit is Adj + Ctl), - reference : (Qubit => Unit is Adj + Ctl)) - : Bool { + reference : (Qubit => Unit is Adj + Ctl) + ) : Bool { use (control, target) = (Qubit(), Qubit()); within { H(control); - } - apply { + } apply { Controlled op([control], target); Adjoint Controlled reference([control], target); } From 499283a0e7e8e78690ecd0816a9aaa3885429f6a Mon Sep 17 00:00:00 2001 From: sezna Date: Fri, 5 Apr 2024 22:41:50 -0700 Subject: [PATCH 76/76] revert sample changes --- samples/language/String.qs | 48 +++++++++++--------------------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/samples/language/String.qs b/samples/language/String.qs index 34f8884a9b..30cda14b4e 100644 --- a/samples/language/String.qs +++ b/samples/language/String.qs @@ -1,41 +1,21 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Kata { - open Microsoft.Quantum.Intrinsic; - - operation ApplyX(q : Qubit) : Unit is Adj + Ctl { - // Do nothing. - } -} - -namespace Kata.Verification { - open Microsoft.Quantum.Diagnostics; - open Microsoft.Quantum.Intrinsic; +/// # Sample +/// String +/// +/// # Description +/// Text as values that consist of a sequence of UTF-16 code units. +namespace MyQuantumApp { @EntryPoint() - operation CheckSolution() : Bool { - VerifySingleQubitOperation(Kata.ApplyX, Kata.Verification.ApplyX) - } + operation Main() : String { + // Strings literals are declared with double quotes: + let myString = "Foo"; - operation ApplyX(q : Qubit) : Unit is Adj + Ctl { - X(q); - } + // Strings can be concatenated with `+` + let myString = myString + "Bar"; - operation VerifySingleQubitOperation( - op : (Qubit => Unit is Adj + Ctl), - reference : (Qubit => Unit is Adj + Ctl) - ) : Bool { - use (control, target) = (Qubit(), Qubit()); - within { - H(control); - } apply { - Controlled op([control], target); - Adjoint Controlled reference([control], target); - } - let isCorrect = CheckAllZero([control, target]); - ResetAll([control, target]); + // Q# supports string interpolation with the `$` prefix. + let myString = $"interpolated: {myString}"; - isCorrect + return myString; } } \ No newline at end of file