use crate::ast;
use crate::collections::{HashMap, HashSet};
use crate::ir;
use crate::ir::{IrBudget, IrCompile, IrCompiler, IrInterpreter, IrQuery};
use crate::parsing::Opaque;
use crate::shared::{Consts, Gen, Items};
use crate::{
CompileError, CompileErrorKind, CompileVisitor, Id, ImportEntryStep, NoopCompileVisitor,
Resolve as _, Spanned, Storage, UnitBuilder,
};
use runestick::format;
use runestick::{
Call, CompileItem, CompileMeta, CompileMetaCapture, CompileMetaEmpty, CompileMetaKind,
CompileMetaStruct, CompileMetaTuple, CompileMod, CompileSource, Component, ComponentRef,
Context, Hash, IntoComponent, Item, Location, Names, Source, SourceId, Span, Visibility,
};
use std::cell::{RefCell, RefMut};
use std::collections::VecDeque;
use std::fmt;
use std::num::NonZeroUsize;
use std::rc::Rc;
use std::sync::Arc;
pub use self::query_error::{QueryError, QueryErrorKind};
mod query_error;
pub(crate) enum BuiltInMacro {
Template(BuiltInTemplate),
Format(BuiltInFormat),
File(BuiltInFile),
Line(BuiltInLine),
}
pub(crate) struct BuiltInTemplate {
pub(crate) span: Span,
pub(crate) from_literal: bool,
pub(crate) exprs: Vec<ast::Expr>,
}
pub(crate) struct BuiltInFormat {
pub(crate) span: Span,
pub(crate) fill: Option<(ast::LitChar, char)>,
pub(crate) align: Option<(ast::Ident, format::Alignment)>,
pub(crate) width: Option<(ast::LitNumber, Option<NonZeroUsize>)>,
pub(crate) precision: Option<(ast::LitNumber, Option<NonZeroUsize>)>,
pub(crate) flags: Option<(ast::LitNumber, format::Flags)>,
pub(crate) format_type: Option<(ast::Ident, format::Type)>,
pub(crate) value: ast::Expr,
}
pub struct BuiltInFile {
pub(crate) span: Span,
pub(crate) value: ast::LitStr,
}
pub struct BuiltInLine {
pub(crate) span: Span,
pub(crate) value: ast::LitNumber,
}
impl IrQuery for QueryInner {
fn query_meta(
&mut self,
span: Span,
item: &Item,
used: Used,
) -> Result<Option<CompileMeta>, QueryError> {
QueryInner::query_meta(self, span, item, used)
}
fn builtin_macro_for(
&self,
span: Span,
id: Option<Id>,
) -> Result<Arc<BuiltInMacro>, QueryError> {
QueryInner::builtin_macro_for(self, span, id)
}
fn const_fn_for(&self, span: Span, id: Option<Id>) -> Result<Arc<QueryConstFn>, QueryError> {
QueryInner::const_fn_for(self, span, id)
}
}
#[derive(Clone, Default)]
pub(crate) struct Query {
inner: Rc<RefCell<QueryInner>>,
}
impl Query {
pub fn new(
visitor: Rc<dyn CompileVisitor>,
storage: Storage,
unit: UnitBuilder,
consts: Consts,
gen: Gen,
) -> Self {
Self {
inner: Rc::new(RefCell::new(QueryInner {
visitor,
meta: HashMap::new(),
storage,
prelude: unit.prelude(),
unit,
consts,
gen,
queue: VecDeque::new(),
indexed: HashMap::new(),
const_fns: HashMap::new(),
query_paths: HashMap::new(),
internal_macros: HashMap::new(),
items: HashMap::new(),
names: Names::default(),
modules: HashMap::new(),
})),
}
}
pub(crate) fn as_ir_query(&self) -> RefMut<'_, dyn IrQuery> {
let inner = self.inner.borrow_mut();
RefMut::map(inner, |inner| inner)
}
pub(crate) fn insert_meta(&self, spanned: Span, meta: CompileMeta) -> Result<(), QueryError> {
let mut inner = self.inner.borrow_mut();
inner
.unit
.insert_meta(&meta)
.map_err(|error| QueryError::new(spanned, error))?;
inner.insert_meta(spanned, meta)?;
Ok(())
}
pub(crate) fn next_build_entry(&self) -> Option<BuildEntry> {
self.inner.borrow_mut().queue.pop_front()
}
pub(crate) fn push_build_entry(&self, entry: BuildEntry) {
self.inner.borrow_mut().queue.push_back(entry)
}
pub(crate) fn storage(&self) -> Storage {
self.inner.borrow().storage.clone()
}
pub(crate) fn insert_path(
&self,
module: &Arc<CompileMod>,
impl_item: Option<&Arc<Item>>,
item: &Item,
) -> Id {
let mut inner = self.inner.borrow_mut();
let query_path = Arc::new(QueryPath {
module: module.clone(),
impl_item: impl_item.cloned(),
item: item.clone(),
});
let id = inner.gen.next();
inner.query_paths.insert(id, query_path);
id
}
pub(crate) fn remove_path_by_id(&self, id: Option<Id>) {
let mut inner = self.inner.borrow_mut();
if let Some(id) = id {
inner.query_paths.remove(&id);
}
}
pub(crate) fn insert_mod(
&self,
items: &Items,
source_id: SourceId,
span: Span,
parent: &Arc<CompileMod>,
visibility: Visibility,
) -> Result<Arc<CompileMod>, QueryError> {
let mut inner = self.inner.borrow_mut();
let item = inner.insert_new_item(items, source_id, span, parent, visibility)?;
let query_mod = Arc::new(CompileMod {
location: Location::new(source_id, span),
item: item.item.clone(),
visibility,
parent: Some(parent.clone()),
});
inner.modules.insert(item.item.clone(), query_mod.clone());
inner.insert_name(&item.item);
Ok(query_mod)
}
pub(crate) fn insert_root_mod(
&self,
source_id: SourceId,
spanned: Span,
) -> Result<Arc<CompileMod>, QueryError> {
let mut inner = self.inner.borrow_mut();
let query_mod = Arc::new(CompileMod {
location: Location::new(source_id, spanned),
item: Item::new(),
visibility: Visibility::Public,
parent: None,
});
inner.modules.insert(Item::new(), query_mod.clone());
inner.insert_name(&Item::new());
Ok(query_mod)
}
pub(crate) fn get_item(&self, span: Span, id: Id) -> Result<Arc<CompileItem>, QueryError> {
let inner = self.inner.borrow();
if let Some(item) = inner.items.get(&id) {
return Ok(item.clone());
}
Err(QueryError::new(span, QueryErrorKind::MissingRevId { id }))
}
pub(crate) fn insert_new_item(
&self,
items: &Items,
source_id: SourceId,
spanned: Span,
module: &Arc<CompileMod>,
visibility: Visibility,
) -> Result<Arc<CompileItem>, QueryError> {
self.inner
.borrow_mut()
.insert_new_item(items, source_id, spanned, module, visibility)
}
pub(crate) fn insert_new_builtin_macro(
&mut self,
internal_macro: BuiltInMacro,
) -> Result<Id, QueryError> {
self.inner
.borrow_mut()
.insert_new_builtin_macro(internal_macro)
}
pub(crate) fn item_for<T>(&self, ast: T) -> Result<Arc<CompileItem>, QueryError>
where
T: Spanned + Opaque,
{
self.inner.borrow().item_for(ast.span(), ast.id())
}
pub(crate) fn builtin_macro_for<T>(&self, ast: T) -> Result<Arc<BuiltInMacro>, QueryError>
where
T: Spanned + Opaque,
{
self.inner.borrow().builtin_macro_for(ast.span(), ast.id())
}
pub(crate) fn const_fn_for<T>(&self, ast: T) -> Result<Arc<QueryConstFn>, QueryError>
where
T: Spanned + Opaque,
{
self.inner.borrow().const_fn_for(ast.span(), ast.id())
}
pub fn index(&self, entry: IndexedEntry) {
self.inner.borrow_mut().index(entry);
}
pub fn index_const<T>(
&self,
item: &Arc<CompileItem>,
source: &Arc<Source>,
expr: &T,
) -> Result<(), QueryError>
where
T: IrCompile<Output = ir::Ir>,
{
log::trace!("new const: {:?}", item.item);
let mut inner = self.inner.borrow_mut();
let mut ir_compiler = IrCompiler {
storage: inner.storage.clone(),
source: source.clone(),
query: &mut *inner,
};
let ir = ir_compiler.compile(expr)?;
inner.index(IndexedEntry {
item: item.clone(),
source: source.clone(),
indexed: Indexed::Const(Const {
module: item.module.clone(),
ir,
}),
});
Ok(())
}
pub fn index_const_fn(
&self,
item: &Arc<CompileItem>,
source: &Arc<Source>,
item_fn: Box<ast::ItemFn>,
) -> Result<(), QueryError> {
log::trace!("new const fn: {:?}", item.item);
self.inner.borrow_mut().index(IndexedEntry {
item: item.clone(),
source: source.clone(),
indexed: Indexed::ConstFn(ConstFn { item_fn }),
});
Ok(())
}
pub fn index_enum(
&self,
item: &Arc<CompileItem>,
source: &Arc<Source>,
) -> Result<(), QueryError> {
log::trace!("new enum: {:?}", item.item);
self.inner.borrow_mut().index(IndexedEntry {
item: item.clone(),
source: source.clone(),
indexed: Indexed::Enum,
});
Ok(())
}
pub fn index_struct(
&self,
item: &Arc<CompileItem>,
source: &Arc<Source>,
ast: Box<ast::ItemStruct>,
) -> Result<(), QueryError> {
log::trace!("new struct: {:?}", item.item);
self.inner.borrow_mut().index(IndexedEntry {
item: item.clone(),
source: source.clone(),
indexed: Indexed::Struct(Struct::new(ast)),
});
Ok(())
}
pub fn index_variant(
&self,
item: &Arc<CompileItem>,
source: &Arc<Source>,
enum_id: Id,
ast: ast::ItemVariant,
) -> Result<(), QueryError> {
log::trace!("new variant: {:?}", item.item);
self.inner.borrow_mut().index(IndexedEntry {
item: item.clone(),
source: source.clone(),
indexed: Indexed::Variant(Variant::new(enum_id, ast)),
});
Ok(())
}
pub fn index_closure(
&self,
item: &Arc<CompileItem>,
source: &Arc<Source>,
ast: Box<ast::ExprClosure>,
captures: Arc<[CompileMetaCapture]>,
call: Call,
do_move: bool,
) -> Result<(), QueryError> {
log::trace!("new closure: {:?}", item.item);
self.inner.borrow_mut().index(IndexedEntry {
item: item.clone(),
source: source.clone(),
indexed: Indexed::Closure(Closure {
ast,
captures,
call,
do_move,
}),
});
Ok(())
}
pub fn index_async_block(
&self,
item: &Arc<CompileItem>,
source: &Arc<Source>,
ast: ast::Block,
captures: Arc<[CompileMetaCapture]>,
call: Call,
do_move: bool,
) -> Result<(), QueryError> {
log::trace!("new closure: {:?}", item.item);
self.inner.borrow_mut().index(IndexedEntry {
item: item.clone(),
source: source.clone(),
indexed: Indexed::AsyncBlock(AsyncBlock {
ast,
captures,
call,
do_move,
}),
});
Ok(())
}
pub(crate) fn queue_unused_entries(&self) -> Result<bool, (SourceId, QueryError)> {
let mut inner = self.inner.borrow_mut();
let unused = inner
.indexed
.values()
.flat_map(|entries| entries.iter())
.map(|e| e.item.clone())
.collect::<Vec<_>>();
if unused.is_empty() {
return Ok(false);
}
for query_item in unused {
if let Some(meta) = inner
.query_meta(query_item.location.span, &query_item.item, Used::Unused)
.map_err(|e| (query_item.location.source_id, e))?
{
inner.visitor.visit_meta(
query_item.location.source_id,
&meta,
query_item.location.span,
);
}
}
Ok(true)
}
pub(crate) fn query_meta(
&self,
span: Span,
item: &Item,
used: Used,
) -> Result<Option<CompileMeta>, QueryError> {
self.inner.borrow_mut().query_meta(span, item, used)
}
pub(crate) fn convert_path(
&self,
context: &Context,
storage: &Storage,
source: &Source,
path: &ast::Path,
) -> Result<Named, CompileError> {
self.inner
.borrow_mut()
.convert_path(context, storage, source, path)
}
pub(crate) fn insert_import(
&self,
source_id: SourceId,
span: Span,
source: &Arc<Source>,
module: &Arc<CompileMod>,
visibility: Visibility,
at: Item,
target: Item,
alias: Option<&str>,
wildcard: bool,
) -> Result<(), QueryError> {
let mut inner = self.inner.borrow_mut();
let last = alias
.as_ref()
.map(IntoComponent::as_component_ref)
.or_else(|| target.last())
.ok_or_else(|| QueryError::new(span, QueryErrorKind::LastUseComponent))?;
let item = at.extended(last);
let location = Location::new(source_id, span);
let entry = ImportEntry {
location,
visibility,
target: target.clone(),
module: module.clone(),
};
let id = inner.gen.next();
let item = inner.insert_new_item_with(id, &item, source_id, span, module, visibility)?;
if item.is_public() {
inner.queue.push_back(BuildEntry {
location,
item: item.clone(),
build: Build::ReExport,
source: source.clone(),
used: Used::Used,
});
}
inner.index(IndexedEntry {
item,
source: source.clone(),
indexed: Indexed::Import(Import { wildcard, entry }),
});
return Ok(());
}
pub(crate) fn contains_prefix(&self, item: &Item) -> bool {
self.inner.borrow().names.contains_prefix(item)
}
pub(crate) fn iter_components<I>(&self, iter: I) -> Vec<Component>
where
I: IntoIterator,
I::Item: IntoComponent,
{
let inner = self.inner.borrow();
inner
.names
.iter_components(iter)
.map(ComponentRef::into_component)
.collect::<Vec<_>>()
}
pub(crate) fn import(
&self,
span: Span,
module: &Arc<CompileMod>,
item: &Item,
used: Used,
) -> Result<Option<Item>, QueryError> {
let mut inner = self.inner.borrow_mut();
inner.import(span, module, item, used)
}
}
#[derive(Clone)]
struct QueryInner {
visitor: Rc<dyn CompileVisitor>,
meta: HashMap<Item, CompileMeta>,
storage: Storage,
prelude: HashMap<Box<str>, Item>,
unit: UnitBuilder,
consts: Consts,
gen: Gen,
queue: VecDeque<BuildEntry>,
indexed: HashMap<Item, Vec<IndexedEntry>>,
const_fns: HashMap<Id, Arc<QueryConstFn>>,
query_paths: HashMap<Id, Arc<QueryPath>>,
internal_macros: HashMap<Id, Arc<BuiltInMacro>>,
items: HashMap<Id, Arc<CompileItem>>,
names: Names,
modules: HashMap<Item, Arc<CompileMod>>,
}
impl Default for QueryInner {
fn default() -> Self {
Self {
visitor: Rc::new(NoopCompileVisitor::new()),
meta: Default::default(),
storage: Default::default(),
prelude: Default::default(),
unit: Default::default(),
consts: Default::default(),
gen: Default::default(),
queue: Default::default(),
indexed: Default::default(),
const_fns: Default::default(),
query_paths: Default::default(),
internal_macros: Default::default(),
items: Default::default(),
names: Default::default(),
modules: Default::default(),
}
}
}
impl QueryInner {
fn item_for(&self, span: Span, id: Option<Id>) -> Result<Arc<CompileItem>, QueryError> {
let item = id
.and_then(|n| self.items.get(&n))
.ok_or_else(|| QueryError::new(span, QueryErrorKind::MissingId { what: "item", id }))?;
Ok(item.clone())
}
fn builtin_macro_for(
&self,
span: Span,
id: Option<Id>,
) -> Result<Arc<BuiltInMacro>, QueryError> {
let internal_macro = id
.and_then(|n| self.internal_macros.get(&n))
.ok_or_else(|| {
QueryError::new(
span,
QueryErrorKind::MissingId {
what: "builtin macro",
id,
},
)
})?;
Ok(internal_macro.clone())
}
fn const_fn_for(&self, spanned: Span, id: Option<Id>) -> Result<Arc<QueryConstFn>, QueryError> {
let const_fn = id.and_then(|n| self.const_fns.get(&n)).ok_or_else(|| {
QueryError::new(
spanned,
QueryErrorKind::MissingId {
what: "constant function",
id,
},
)
})?;
Ok(const_fn.clone())
}
fn insert_name(&mut self, item: &Item) {
self.names.insert(item);
}
fn insert_new_item(
&mut self,
items: &Items,
source_id: SourceId,
spanned: Span,
module: &Arc<CompileMod>,
visibility: Visibility,
) -> Result<Arc<CompileItem>, QueryError> {
let id = items.id();
let item = &*items.item();
self.insert_new_item_with(id, item, source_id, spanned, module, visibility)
}
fn insert_new_item_with(
&mut self,
id: Id,
item: &Item,
source_id: SourceId,
spanned: Span,
module: &Arc<CompileMod>,
visibility: Visibility,
) -> Result<Arc<CompileItem>, QueryError> {
let query_item = Arc::new(CompileItem {
location: Location::new(source_id, spanned),
id,
item: item.clone(),
module: module.clone(),
visibility,
});
self.items.insert(id, query_item.clone());
Ok(query_item)
}
pub(crate) fn insert_new_builtin_macro(
&mut self,
internal_macro: BuiltInMacro,
) -> Result<Id, QueryError> {
let id = self.gen.next();
self.internal_macros.insert(id, Arc::new(internal_macro));
Ok(id)
}
fn index(&mut self, entry: IndexedEntry) {
log::trace!("indexed: {}", entry.item.item);
self.insert_name(&entry.item.item);
self.indexed
.entry(entry.item.item.clone())
.or_default()
.push(entry);
}
fn import_indexed(
&mut self,
span: Span,
item: Arc<CompileItem>,
source: Arc<Source>,
indexed: Indexed,
used: Used,
) -> Result<(), QueryError> {
let entry = IndexedEntry {
item,
source,
indexed,
};
let meta = self.build_indexed_entry(span, entry, used)?;
self.unit
.insert_meta(&meta)
.map_err(|error| QueryError::new(span, error))?;
self.insert_meta(span, meta)?;
Ok(())
}
fn import(
&mut self,
span: Span,
module: &Arc<CompileMod>,
item: &Item,
used: Used,
) -> Result<Option<Item>, QueryError> {
let mut visited = HashSet::<Item>::new();
let mut path = Vec::new();
let mut module = module.clone();
let mut item = item.clone();
let mut any_matched = false;
'outer: loop {
let mut cur = Item::new();
let mut it = item.iter();
while let Some(c) = it.next() {
cur.push(c);
let update = self.import_step(span, &module, &cur, used, &mut path)?;
let update = match update {
Some(update) => update,
None => continue,
};
path.push(ImportEntryStep {
location: update.location,
item: update.target.clone(),
});
if !visited.insert(item.clone()) {
return Err(QueryError::new(span, QueryErrorKind::ImportCycle { path }));
}
module = update.module;
item = update.target.join(it);
any_matched = true;
continue 'outer;
}
break;
}
if any_matched {
return Ok(Some(item));
}
Ok(None)
}
fn import_step(
&mut self,
span: Span,
module: &Arc<CompileMod>,
item: &Item,
used: Used,
path: &mut Vec<ImportEntryStep>,
) -> Result<Option<ImportStep>, QueryError> {
if let Some(meta) = self.meta.get(item) {
return Ok(match &meta.kind {
CompileMetaKind::Import {
module,
location,
target,
} => Some(ImportStep {
module: module.clone(),
location: *location,
target: target.clone(),
}),
_ => None,
});
}
let entry = match self.remove_indexed(span, item)? {
Some(entry) => entry,
_ => return Ok(None),
};
self.check_access_to(
span,
&*module,
item,
&entry.item.module,
entry.item.location,
entry.item.visibility,
path,
)?;
let import = match entry.indexed {
Indexed::Import(import) => import.entry,
indexed => {
self.import_indexed(span, entry.item, entry.source, indexed, used)?;
return Ok(None);
}
};
let meta = CompileMeta {
item: entry.item.clone(),
kind: CompileMetaKind::Import {
module: import.module.clone(),
location: import.location,
target: import.target.clone(),
},
source: None,
};
self.insert_meta(span, meta)?;
Ok(Some(ImportStep {
module: import.module,
location: import.location,
target: import.target,
}))
}
fn query_meta(
&mut self,
span: Span,
item: &Item,
used: Used,
) -> Result<Option<CompileMeta>, QueryError> {
if let Some(meta) = self.meta.get(item) {
return Ok(Some(meta.clone()));
}
let entry = match self.remove_indexed(span, item)? {
Some(entry) => entry,
None => return Ok(None),
};
let meta = self.build_indexed_entry(span, entry, used)?;
self.unit
.insert_meta(&meta)
.map_err(|error| QueryError::new(span, error))?;
self.insert_meta(span, meta.clone())?;
Ok(Some(meta))
}
fn remove_indexed(
&mut self,
span: Span,
item: &Item,
) -> Result<Option<IndexedEntry>, QueryError> {
let entries = match self.indexed.remove(item) {
Some(entries) => entries,
None => return Ok(None),
};
let mut it = entries.into_iter().peekable();
let mut cur = match it.next() {
Some(first) => first,
None => return Ok(None),
};
if it.peek().is_none() {
return Ok(Some(cur));
}
let mut locations = Vec::new();
locations.push((cur.item.location, cur.item().clone()));
while let Some(oth) = it.next() {
locations.push((oth.item.location, oth.item().clone()));
if let (Indexed::Import(a), Indexed::Import(b)) = (&cur.indexed, &oth.indexed) {
if a.wildcard {
cur = oth;
continue;
}
if b.wildcard {
continue;
}
}
for oth in it {
locations.push((oth.item.location, oth.item().clone()));
}
return Err(QueryError::new(
span,
QueryErrorKind::AmbiguousItem {
item: cur.item.item.clone(),
locations,
},
));
}
if let Indexed::Import(Import { wildcard: true, .. }) = &cur.indexed {
return Err(QueryError::new(
span,
QueryErrorKind::AmbiguousItem {
item: cur.item.item.clone(),
locations,
},
));
}
Ok(Some(cur))
}
fn insert_meta(&mut self, span: Span, meta: CompileMeta) -> Result<(), QueryError> {
let item = meta.item.item.clone();
self.visitor.register_meta(&meta);
if let Some(existing) = self.meta.insert(item, meta.clone()) {
return Err(QueryError::new(
span,
QueryErrorKind::MetaConflict {
current: meta,
existing,
},
));
}
Ok(())
}
fn lookup_initial(
&mut self,
context: &Context,
module: &Arc<CompileMod>,
base: &Item,
local: &str,
) -> Result<Item, CompileError> {
debug_assert!(base.starts_with(&module.item));
let mut base = base.clone();
while base.starts_with(&module.item) {
let item = base.extended(local);
if self.names.contains(&item) {
return Ok(item);
}
if base.pop().is_none() {
break;
}
}
if let Some(item) = self.prelude.get(local) {
return Ok(item.clone());
}
if context.contains_crate(local) {
return Ok(Item::with_crate(local));
}
Ok(module.item.extended(local))
}
fn convert_path(
&mut self,
context: &Context,
storage: &Storage,
source: &Source,
path: &ast::Path,
) -> Result<Named, CompileError> {
let id = path.id();
let qp = id
.and_then(|id| self.query_paths.get(&id))
.ok_or_else(|| QueryError::new(path, QueryErrorKind::MissingId { what: "path", id }))?
.clone();
let mut in_self_type = false;
let mut local = None;
let mut item = match (&path.global, &path.first) {
(Some(..), ast::PathSegment::Ident(ident)) => {
let ident = ident.resolve(storage, source)?;
Item::with_crate(ident.as_ref())
}
(Some(global), _) => {
return Err(CompileError::new(
global.span(),
CompileErrorKind::UnsupportedGlobal,
));
}
(None, segment) => match segment {
ast::PathSegment::Ident(ident) => {
let ident = ident.resolve(storage, source)?;
if path.rest.is_empty() {
local = Some(<Box<str>>::from(ident.as_ref()));
}
self.lookup_initial(context, &qp.module, &qp.item, &*ident)?
}
ast::PathSegment::Super(super_value) => {
let mut item = qp.module.item.clone();
item.pop()
.ok_or_else(CompileError::unsupported_super(super_value))?;
item
}
ast::PathSegment::SelfType(self_type) => {
let impl_item = qp.impl_item.as_deref().ok_or_else(|| {
CompileError::new(self_type, CompileErrorKind::UnsupportedSelfType)
})?;
in_self_type = true;
impl_item.clone()
}
ast::PathSegment::SelfValue(..) => qp.module.item.clone(),
ast::PathSegment::Crate(..) => Item::new(),
ast::PathSegment::Generics(arguments) => {
return Err(CompileError::new(
arguments,
CompileErrorKind::UnsupportedGenerics,
));
}
},
};
for (_, segment) in &path.rest {
log::trace!("item = {}", item);
match segment {
ast::PathSegment::Ident(ident) => {
let ident = ident.resolve(storage, source)?;
item.push(ident.as_ref());
}
ast::PathSegment::Super(super_token) => {
if in_self_type {
return Err(CompileError::new(
super_token,
CompileErrorKind::UnsupportedSuperInSelfType,
));
}
item.pop()
.ok_or_else(CompileError::unsupported_super(super_token))?;
}
ast::PathSegment::Generics(arguments) => {
return Err(CompileError::new(
arguments,
CompileErrorKind::UnsupportedGenerics,
));
}
other => {
return Err(CompileError::new(
other,
CompileErrorKind::ExpectedLeadingPathSegment,
));
}
}
}
let span = path.span();
if let Some(new) = self.import(span, &qp.module, &item, Used::Used)? {
return Ok(Named { local, item: new });
}
Ok(Named { local, item })
}
fn build_indexed_entry(
&mut self,
span: Span,
entry: IndexedEntry,
used: Used,
) -> Result<CompileMeta, QueryError> {
let IndexedEntry {
item: query_item,
indexed,
source,
} = entry;
let path = source.path().map(ToOwned::to_owned);
let kind = match indexed {
Indexed::Enum => CompileMetaKind::Enum {
type_hash: Hash::type_hash(&query_item.item),
},
Indexed::Variant(variant) => {
let enum_item = self.item_for(query_item.location.span, Some(variant.enum_id))?;
self.query_meta(span, &enum_item.item, Default::default())?;
variant_into_item_decl(
&query_item.item,
variant.ast.body,
Some(&enum_item.item),
&self.storage,
&*source,
)?
}
Indexed::Struct(st) => {
struct_into_item_decl(&query_item.item, st.ast.body, None, &self.storage, &*source)?
}
Indexed::Function(f) => {
self.queue.push_back(BuildEntry {
location: query_item.location,
item: query_item.clone(),
build: Build::Function(f),
source,
used,
});
CompileMetaKind::Function {
type_hash: Hash::type_hash(&query_item.item),
is_test: false,
}
}
Indexed::Closure(c) => {
let captures = c.captures.clone();
let do_move = c.do_move;
self.queue.push_back(BuildEntry {
location: query_item.location,
item: query_item.clone(),
build: Build::Closure(c),
source,
used,
});
CompileMetaKind::Closure {
type_hash: Hash::type_hash(&query_item.item),
captures,
do_move,
}
}
Indexed::AsyncBlock(b) => {
let captures = b.captures.clone();
let do_move = b.do_move;
self.queue.push_back(BuildEntry {
location: query_item.location,
item: query_item.clone(),
build: Build::AsyncBlock(b),
source,
used,
});
CompileMetaKind::AsyncBlock {
type_hash: Hash::type_hash(&query_item.item),
captures,
do_move,
}
}
Indexed::Const(c) => {
let mut const_compiler = IrInterpreter {
budget: IrBudget::new(1_000_000),
scopes: Default::default(),
module: c.module.clone(),
item: query_item.item.clone(),
consts: self.consts.clone(),
query: self,
};
let const_value = const_compiler.eval_const(&c.ir, used)?;
if used.is_unused() {
self.queue.push_back(BuildEntry {
location: query_item.location,
item: query_item.clone(),
build: Build::Unused,
source,
used,
});
}
CompileMetaKind::Const { const_value }
}
Indexed::ConstFn(c) => {
let mut ir_compiler = IrCompiler {
storage: self.storage.clone(),
source: source.clone(),
query: self,
};
let ir_fn = ir_compiler.compile(&*c.item_fn)?;
let id = self.insert_const_fn(&query_item, ir_fn);
if used.is_unused() {
self.queue.push_back(BuildEntry {
location: query_item.location,
item: query_item.clone(),
build: Build::Unused,
source,
used,
});
}
CompileMetaKind::ConstFn { id, is_test: false }
}
Indexed::Import(import) => {
let module = import.entry.module.clone();
let location = import.entry.location;
let target = import.entry.target.clone();
if !import.wildcard {
self.queue.push_back(BuildEntry {
location: query_item.location,
item: query_item.clone(),
build: Build::Import(import),
source,
used,
});
}
CompileMetaKind::Import {
module,
location,
target,
}
}
};
let source = CompileSource {
source_id: query_item.location.source_id,
span: query_item.location.span,
path,
};
Ok(CompileMeta {
item: query_item,
kind,
source: Some(source),
})
}
fn insert_const_fn(&mut self, item: &Arc<CompileItem>, ir_fn: ir::IrFn) -> Id {
let id = self.gen.next();
self.const_fns.insert(
id,
Arc::new(QueryConstFn {
item: item.clone(),
ir_fn,
}),
);
id
}
fn check_access_to(
&self,
span: Span,
from: &CompileMod,
item: &Item,
module: &CompileMod,
location: Location,
visibility: Visibility,
chain: &mut Vec<ImportEntryStep>,
) -> Result<(), QueryError> {
let (common, tree) = from.item.ancestry(&module.item);
let mut current_module = common.clone();
for c in &tree {
current_module.push(c);
let m = self.modules.get(¤t_module).ok_or_else(|| {
QueryError::new(
span,
QueryErrorKind::MissingMod {
item: current_module.clone(),
},
)
})?;
if !m.visibility.is_visible(&common, ¤t_module) {
return Err(QueryError::new(
span,
QueryErrorKind::NotVisibleMod {
chain: into_chain(std::mem::take(chain)),
location: m.location,
visibility: m.visibility,
item: current_module,
from: from.item.clone(),
},
));
}
}
if !visibility.is_visible_inside(&common, &module.item) {
return Err(QueryError::new(
span,
QueryErrorKind::NotVisible {
chain: into_chain(std::mem::take(chain)),
location,
visibility,
item: item.clone(),
from: from.item.clone(),
},
));
}
Ok(())
}
}
#[derive(Debug, Clone, Copy)]
pub enum Used {
Unused,
Used,
}
impl Used {
pub(crate) fn is_unused(self) -> bool {
matches!(self, Self::Unused)
}
}
impl Default for Used {
fn default() -> Self {
Self::Used
}
}
#[derive(Debug, Clone)]
pub(crate) enum Indexed {
Enum,
Struct(Struct),
Variant(Variant),
Function(Function),
Closure(Closure),
AsyncBlock(AsyncBlock),
Const(Const),
ConstFn(ConstFn),
Import(Import),
}
#[derive(Debug, Clone)]
pub struct Import {
pub(crate) entry: ImportEntry,
pub(crate) wildcard: bool,
}
#[derive(Debug, Clone)]
pub struct Struct {
ast: Box<ast::ItemStruct>,
}
impl Struct {
pub fn new(ast: Box<ast::ItemStruct>) -> Self {
Self { ast }
}
}
#[derive(Debug, Clone)]
pub struct Variant {
enum_id: Id,
ast: ast::ItemVariant,
}
impl Variant {
pub fn new(enum_id: Id, ast: ast::ItemVariant) -> Self {
Self { enum_id, ast }
}
}
#[derive(Debug, Clone)]
pub(crate) struct Function {
pub(crate) ast: Box<ast::ItemFn>,
pub(crate) call: Call,
}
#[derive(Debug, Clone)]
pub(crate) struct InstanceFunction {
pub(crate) ast: Box<ast::ItemFn>,
pub(crate) impl_item: Arc<Item>,
pub(crate) instance_span: Span,
pub(crate) call: Call,
}
#[derive(Debug, Clone)]
pub(crate) struct Closure {
pub(crate) ast: Box<ast::ExprClosure>,
pub(crate) captures: Arc<[CompileMetaCapture]>,
pub(crate) call: Call,
pub(crate) do_move: bool,
}
#[derive(Debug, Clone)]
pub(crate) struct AsyncBlock {
pub(crate) ast: ast::Block,
pub(crate) captures: Arc<[CompileMetaCapture]>,
pub(crate) call: Call,
pub(crate) do_move: bool,
}
#[derive(Debug, Clone)]
pub(crate) struct Const {
pub(crate) module: Arc<CompileMod>,
pub(crate) ir: ir::Ir,
}
#[derive(Debug, Clone)]
pub(crate) struct ConstFn {
pub(crate) item_fn: Box<ast::ItemFn>,
}
#[derive(Debug, Clone)]
pub(crate) enum Build {
Function(Function),
InstanceFunction(InstanceFunction),
Closure(Closure),
AsyncBlock(AsyncBlock),
Unused,
Import(Import),
ReExport,
}
#[derive(Debug, Clone)]
pub(crate) struct BuildEntry {
pub(crate) location: Location,
pub(crate) item: Arc<CompileItem>,
pub(crate) build: Build,
pub(crate) source: Arc<Source>,
pub(crate) used: Used,
}
#[derive(Debug, Clone)]
pub(crate) struct IndexedEntry {
pub(crate) item: Arc<CompileItem>,
pub(crate) source: Arc<Source>,
pub(crate) indexed: Indexed,
}
impl IndexedEntry {
pub fn item(&self) -> &Item {
match &self.indexed {
Indexed::Import(Import { entry, .. }) => &entry.target,
_ => &self.item.item,
}
}
}
#[derive(Debug)]
pub(crate) struct QueryPath {
pub(crate) module: Arc<CompileMod>,
pub(crate) impl_item: Option<Arc<Item>>,
pub(crate) item: Item,
}
#[derive(Debug)]
pub(crate) struct QueryConstFn {
pub(crate) item: Arc<CompileItem>,
pub(crate) ir_fn: ir::IrFn,
}
#[derive(Debug)]
pub struct Named {
pub local: Option<Box<str>>,
pub item: Item,
}
impl Named {
pub fn as_local(&self) -> Option<&str> {
self.local.as_deref()
}
}
impl fmt::Display for Named {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.item, f)
}
}
fn unit_body_meta(item: &Item, enum_item: Option<&Item>) -> CompileMetaKind {
let type_hash = Hash::type_hash(item);
let empty = CompileMetaEmpty {
hash: Hash::type_hash(item),
};
match enum_item {
Some(enum_item) => CompileMetaKind::UnitVariant {
type_hash,
enum_item: enum_item.clone(),
empty,
},
None => CompileMetaKind::UnitStruct { type_hash, empty },
}
}
fn tuple_body_meta(
item: &Item,
enum_item: Option<&Item>,
tuple: ast::Parenthesized<ast::Field, T![,]>,
) -> CompileMetaKind {
let type_hash = Hash::type_hash(item);
let tuple = CompileMetaTuple {
args: tuple.len(),
hash: Hash::type_hash(item),
};
match enum_item {
Some(enum_item) => CompileMetaKind::TupleVariant {
type_hash,
enum_item: enum_item.clone(),
tuple,
},
None => CompileMetaKind::TupleStruct { type_hash, tuple },
}
}
fn struct_body_meta(
item: &Item,
enum_item: Option<&Item>,
storage: &Storage,
source: &Source,
st: ast::Braced<ast::Field, T![,]>,
) -> Result<CompileMetaKind, QueryError> {
let type_hash = Hash::type_hash(item);
let mut fields = HashSet::new();
for (ast::Field { name, .. }, _) in st {
let name = name.resolve(&storage, &*source)?;
fields.insert(name.into());
}
let object = CompileMetaStruct { fields };
Ok(match enum_item {
Some(enum_item) => CompileMetaKind::StructVariant {
type_hash,
enum_item: enum_item.clone(),
object,
},
None => CompileMetaKind::Struct { type_hash, object },
})
}
fn variant_into_item_decl(
item: &Item,
body: ast::ItemVariantBody,
enum_item: Option<&Item>,
storage: &Storage,
source: &Source,
) -> Result<CompileMetaKind, QueryError> {
Ok(match body {
ast::ItemVariantBody::UnitBody => unit_body_meta(item, enum_item),
ast::ItemVariantBody::TupleBody(tuple) => tuple_body_meta(item, enum_item, tuple),
ast::ItemVariantBody::StructBody(st) => {
struct_body_meta(item, enum_item, storage, source, st)?
}
})
}
fn struct_into_item_decl(
item: &Item,
body: ast::ItemStructBody,
enum_item: Option<&Item>,
storage: &Storage,
source: &Source,
) -> Result<CompileMetaKind, QueryError> {
Ok(match body {
ast::ItemStructBody::UnitBody => unit_body_meta(item, enum_item),
ast::ItemStructBody::TupleBody(tuple) => tuple_body_meta(item, enum_item, tuple),
ast::ItemStructBody::StructBody(st) => {
struct_body_meta(item, enum_item, storage, source, st)?
}
})
}
#[derive(Debug, Clone)]
pub struct ImportEntry {
pub location: Location,
pub visibility: Visibility,
pub target: Item,
pub(crate) module: Arc<CompileMod>,
}
struct ImportStep {
module: Arc<CompileMod>,
location: Location,
target: Item,
}
fn into_chain(chain: Vec<ImportEntryStep>) -> Vec<Location> {
chain.into_iter().map(|c| c.location).collect()
}