use crate::crate_prelude::*;
use crate::{
ast::{self, Visitor},
ast_map::{AstMap, AstNode},
common::{arenas::Alloc, arenas::TypedArena, Session},
func_args::FuncArgList,
hir::{self, HirNode},
port_list::PortList,
resolver::Scope,
value::{Value, ValueData, ValueKind},
ParamEnv, ParamEnvData, QueryDatabase, QueryStorage,
};
use std::{
cell::RefCell,
collections::{BTreeSet, HashMap, HashSet},
};
pub struct GlobalContext<'gcx> {
pub sess: &'gcx Session,
pub arena: &'gcx GlobalArenas<'gcx>,
storage: QueryStorage<'gcx>,
ast_map: AstMap<'gcx>,
ast_map2: RefCell<HashMap<NodeId, &'gcx dyn ast::AnyNode<'gcx>>>,
roots: RefCell<Vec<&'gcx ast::Root<'gcx>>>,
modules: RefCell<HashMap<Name, NodeId>>,
packages: RefCell<HashMap<Name, NodeId>>,
interfaces: RefCell<HashMap<Name, NodeId>>,
imports: RefCell<Vec<NodeId>>,
node_id_to_span: RefCell<HashMap<NodeId, Span>>,
tables: GlobalTables<'gcx>,
}
impl<'gcx> GlobalContext<'gcx> {
pub fn new(sess: &'gcx Session, arena: &'gcx GlobalArenas<'gcx>) -> Self {
GlobalContext {
sess,
arena,
storage: Default::default(),
ast_map: Default::default(),
ast_map2: Default::default(),
roots: Default::default(),
modules: Default::default(),
packages: Default::default(),
interfaces: Default::default(),
imports: Default::default(),
node_id_to_span: Default::default(),
tables: Default::default(),
}
}
pub fn add_root(&self, root: &'gcx ast::Root<'gcx>) {
self.roots.borrow_mut().push(root);
debug!("Linking nodes");
let mut index = 0;
root.link(None, &mut index);
debug!("Linked {} nodes", index);
debug!("Materializing scopes");
crate::resolver::materialize_scope(self, root);
self.register_ast(root);
debug!("Checking names");
self.nameck(root);
for file in &root.files {
for item in &file.items {
match &item.data {
ast::ItemData::ModuleDecl(ref n) => {
let id = self.map_ast(AstNode::Module(n));
self.modules.borrow_mut().insert(n.name.value, id);
}
ast::ItemData::PackageDecl(ref n) => {
let id = self.map_ast(AstNode::Package(n));
self.packages.borrow_mut().insert(n.name.value, id);
}
ast::ItemData::InterfaceDecl(ref n) => {
let id = self.map_ast(AstNode::Interface(n));
self.interfaces.borrow_mut().insert(n.name.value, id);
}
ast::ItemData::ImportDecl(ref n) => {
for item in &n.items {
let id = self.map_ast(AstNode::Import(item));
self.imports.borrow_mut().push(id);
}
}
_ => (),
}
}
}
debug!("{:?} added", root);
}
pub fn add_files(&self, files: impl Iterator<Item = &'gcx ast::SourceFile<'gcx>>) {
let root = ast::Root::new(
moore_common::source::INVALID_SPAN,
ast::RootData {
files: files.collect(),
},
);
let root = self.arena.alloc_ast_root(root);
self.add_root(root);
}
pub fn roots(&self) -> impl Iterator<Item = &'gcx ast::Root<'gcx>> {
self.roots.borrow().clone().into_iter()
}
pub fn find_module(&self, name: Name) -> Option<NodeId> {
self.modules.borrow().get(&name).cloned()
}
pub fn modules(&self) -> impl Iterator<Item = (Name, NodeId)> {
self.modules.borrow().clone().into_iter()
}
pub fn find_package(&self, name: Name) -> Option<NodeId> {
self.packages.borrow().get(&name).cloned()
}
pub fn imports(&self) -> impl Iterator<Item = NodeId> {
self.imports.borrow().clone().into_iter()
}
}
impl DiagEmitter for GlobalContext<'_> {
fn emit(&self, mut diag: DiagBuilder2) {
let sev = diag.get_severity();
if sev >= Severity::Error {
for query in self.storage().stack.borrow().iter().rev() {
match query {
QueryTag::TypeOfExpr(query) => {
diag = diag
.add_note("Needed to compute the type of the expression:")
.span(query.0.span());
}
QueryTag::TypeOfIntPort(query) => {
diag = diag
.add_note(format!(
"Needed to compute the type of port `{}`:",
query.0.name
))
.span(query.0.span);
}
QueryTag::TypeOfVarDecl(query) => {
diag = diag
.add_note(format!(
"Needed to compute the type of variable `{}`:",
query.0.name
))
.span(query.0.span);
}
_ => (),
}
}
}
self.sess.emit(diag);
if sev >= Severity::Warning {
trace!("Diagnostic query trace:");
for x in self.storage().stack.borrow().iter().rev() {
trace!(" - {:?}", x);
}
trace!(
"Diagnostic triggered here:\n{:?}",
backtrace::Backtrace::new()
);
}
}
}
impl<'gcx> Context<'gcx> for GlobalContext<'gcx> {
fn gcx(&self) -> &GlobalContext<'gcx> {
self
}
}
impl<'gcx> QueryDatabase<'gcx> for GlobalContext<'gcx> {
type Context = Self;
fn context(&self) -> &Self {
self
}
fn storage(&self) -> &QueryStorage<'gcx> {
&self.storage
}
}
impl<'gcx> ty::HasTypeStorage<'gcx> for GlobalContext<'gcx> {
fn type_storage(&self) -> &'gcx ty::TypeStorage<'gcx> {
&self.arena.type_storage
}
}
#[derive(Default)]
pub struct GlobalArenas<'t> {
ids: TypedArena<NodeId>,
pub ast: ast::Arena<'t>,
hir: hir::Arena<'t>,
param_envs: TypedArena<ParamEnvData<'t>>,
ribs: TypedArena<Rib>,
port_lists: TypedArena<PortList<'t>>,
scopes: TypedArena<Scope<'t>>,
values: TypedArena<ValueData<'t>>,
mir_lvalue: TypedArena<mir::Lvalue<'t>>,
mir_rvalue: TypedArena<mir::Rvalue<'t>>,
mir_assignment: TypedArena<mir::Assignment<'t>>,
ast_roots: TypedArena<ast::Root<'t>>,
ast_types: TypedArena<ast::Type<'t>>,
ast_exprs: TypedArena<ast::Expr<'t>>,
type_storage: ty::TypeStorage<'t>,
func_arg_lists: TypedArena<FuncArgList<'t>>,
}
impl<'t> GlobalArenas<'t> {
pub fn alloc_ids(&'t self, ids: impl IntoIterator<Item = NodeId>) -> &'t [NodeId] {
self.ids.alloc_extend(ids)
}
pub fn alloc_hir<T>(&'t self, hir: T) -> &'t T
where
hir::Arena<'t>: Alloc<'t, 't, T>,
T: 't,
{
self.hir.alloc(hir)
}
pub fn alloc_rib(&'t self, rib: Rib) -> &'t Rib {
self.ribs.alloc(rib)
}
pub fn alloc_port_list(&'t self, port_list: PortList<'t>) -> &'t PortList<'t> {
self.port_lists.alloc(port_list)
}
pub fn alloc_scope(&'t self, scope: Scope<'t>) -> &'t Scope<'t> {
self.scopes.alloc(scope)
}
pub fn alloc_mir_lvalue(&'t self, mir: mir::Lvalue<'t>) -> &'t mir::Lvalue<'t> {
self.mir_lvalue.alloc(mir)
}
pub fn alloc_mir_rvalue(&'t self, mir: mir::Rvalue<'t>) -> &'t mir::Rvalue<'t> {
self.mir_rvalue.alloc(mir)
}
pub fn alloc_mir_assignment(&'t self, mir: mir::Assignment<'t>) -> &'t mir::Assignment<'t> {
self.mir_assignment.alloc(mir)
}
pub fn alloc_ast_root(&'t self, ast: ast::Root<'t>) -> &'t ast::Root {
self.ast_roots.alloc(ast)
}
pub fn alloc_ast_type(&'t self, ast: ast::Type<'t>) -> &'t ast::Type {
self.ast_types.alloc(ast)
}
pub fn alloc_ast_expr(&'t self, ast: ast::Expr<'t>) -> &'t ast::Expr {
self.ast_exprs.alloc(ast)
}
pub fn alloc_func_arg_list(&'t self, func_arg_list: FuncArgList<'t>) -> &'t FuncArgList<'t> {
self.func_arg_lists.alloc(func_arg_list)
}
}
impl<'t, T: 't> Alloc<'t, 't, T> for GlobalArenas<'t>
where
ast::Arena<'t>: Alloc<'t, 't, T>,
{
fn alloc(&'t self, value: T) -> &'t mut T {
self.ast.alloc(value)
}
}
#[derive(Default)]
pub struct GlobalTables<'t> {
interned_param_envs: RefCell<HashMap<&'t ParamEnvData<'t>, ParamEnv>>,
param_envs: RefCell<Vec<&'t ParamEnvData<'t>>>,
param_env_contexts: RefCell<HashMap<ParamEnv, BTreeSet<NodeId>>>,
node_id_to_parent_node_id: RefCell<HashMap<NodeId, NodeId>>,
interned_values: RefCell<HashSet<Value<'t>>>,
lowering_hints: RefCell<HashMap<NodeId, hir::Hint>>,
interned_hir: RefCell<HashMap<NodeId, HirNode<'t>>>,
}
pub trait Context<'gcx>: DiagEmitter + QueryDatabase<'gcx> + ty::HasTypeStorage<'gcx> {
fn gcx(&self) -> &GlobalContext<'gcx>;
fn sess(&self) -> &'gcx Session {
self.gcx().sess
}
fn arena(&self) -> &'gcx GlobalArenas<'gcx> {
self.gcx().arena
}
fn tables(&self) -> &GlobalTables<'gcx> {
&self.gcx().tables
}
fn unimp<T: HasSpan + HasDesc, R>(&self, node: &T) -> Result<R> {
self.emit(
DiagBuilder2::bug(format!("{} not implemented", node.desc_full()))
.span(node.human_span()),
);
Err(())
}
fn unimp_msg<T: HasSpan + HasDesc, R>(&self, msg: impl Into<String>, node: &T) -> Result<R> {
self.emit(
DiagBuilder2::bug(format!(
"{} {} not implemented",
msg.into(),
node.desc_full()
))
.span(node.human_span()),
);
Err(())
}
fn alloc_id(&self, span: Span) -> NodeId {
let id = NodeId::alloc();
self.set_span(id, span);
id
}
fn span(&self, node_id: NodeId) -> Span {
self.gcx()
.node_id_to_span
.borrow()
.get(&node_id)
.cloned()
.unwrap_or(crate::common::source::INVALID_SPAN)
}
fn set_span(&self, node_id: NodeId, span: Span) {
self.gcx()
.node_id_to_span
.borrow_mut()
.insert(node_id, span);
}
fn set_ast(&self, node_id: NodeId, ast: AstNode<'gcx>) {
self.gcx().ast_map.set(node_id, ast);
if let Some(any) = ast.get_any() {
self.gcx().ast_map2.borrow_mut().insert(node_id, any);
}
}
fn map_ast(&self, ast: AstNode<'gcx>) -> NodeId {
let id = match ast.get_any() {
Some(node) => {
self.set_span(node.id(), node.human_span());
node.id()
}
None => self.alloc_id(ast.human_span()),
};
self.set_ast(id, ast);
id
}
fn map_ast_with_parent(&self, ast: AstNode<'gcx>, parent: NodeId) -> NodeId {
let id = self.map_ast(ast);
self.set_parent(id, parent);
id
}
fn ast_of(&self, node_id: NodeId) -> Result<AstNode<'gcx>> {
match self.gcx().ast_map.get(node_id) {
Some(node) => return Ok(node),
None => (),
}
let other = self.ast_for_id(node_id);
match AstNode::from_all(other.as_all()).next() {
Some(node) => return Ok(node),
None => (),
}
self.emit(
DiagBuilder2::bug(format!(
"no ast node for `{}` in the map",
other.span().extract()
))
.span(other.span())
.add_note(format!("Offending node: {:#1?}", other))
.add_note(format!("Required here:\n{:?}", {
let v: Vec<_> = backtrace::Backtrace::new()
.frames()
.iter()
.cloned()
.skip(1)
.take(8)
.collect();
backtrace::Backtrace::from(v)
})),
);
panic!("no ast node");
}
fn register_ast(&self, node: &'gcx dyn ast::AnyNode<'gcx>) {
let mut visitor = AstMapRegistrator { cx: self.gcx() };
visitor.pre_visit_node(node);
node.accept(&mut visitor);
visitor.post_visit_node(node);
}
fn ast_for_id(&self, node_id: NodeId) -> &'gcx dyn ast::AnyNode<'gcx> {
match self.gcx().ast_map2.borrow().get(&node_id) {
Some(&node) => node,
None => panic!("no AST node for {:?} registered", node_id),
}
}
fn intern_hir(&self, id: NodeId, hir: HirNode<'gcx>) {
self.tables().interned_hir.borrow_mut().insert(id, hir);
}
fn intern_hir_with_parent(&self, id: NodeId, hir: HirNode<'gcx>, parent: NodeId) {
self.intern_hir(id, hir);
self.set_parent(id, parent);
}
fn interned_hir(&self, id: NodeId) -> HirNode<'gcx> {
self.tables().interned_hir.borrow()[&id]
}
fn get_interned_hir(&self, id: NodeId) -> Option<HirNode<'gcx>> {
self.tables().interned_hir.borrow().get(&id).cloned()
}
fn intern_value(&self, value: ValueData<'gcx>) -> Value<'gcx> {
if let Some(&x) = self.tables().interned_values.borrow().get(&value) {
return x;
}
let value = self.arena().values.alloc(value);
self.tables().interned_values.borrow_mut().insert(value);
value
}
fn intern_param_env(&self, env: ParamEnvData<'gcx>) -> ParamEnv {
if let Some(&x) = self.tables().interned_param_envs.borrow().get(&env) {
return x;
}
let data = self.arena().param_envs.alloc(env);
let id = {
let mut vec = self.tables().param_envs.borrow_mut();
let id = ParamEnv(vec.len() as u32);
vec.push(data);
id
};
self.tables()
.interned_param_envs
.borrow_mut()
.insert(data, id);
id
}
fn param_env_data(&self, env: ParamEnv) -> &'gcx ParamEnvData<'gcx> {
self.tables().param_envs.borrow()[env.0 as usize]
}
fn default_param_env(&self) -> ParamEnv {
self.intern_param_env(ParamEnvData::default())
}
fn add_param_env_context(&self, env: ParamEnv, context: NodeId) {
self.tables()
.param_env_contexts
.borrow_mut()
.entry(env)
.or_insert_with(Default::default)
.insert(context);
}
fn param_env_contexts(&self, env: ParamEnv) -> Vec<NodeId> {
self.tables()
.param_env_contexts
.borrow()
.get(&env)
.map(|s| s.iter().cloned().collect())
.unwrap_or_else(Default::default)
}
fn set_parent(&self, node_id: NodeId, parent_id: NodeId) {
self.tables()
.node_id_to_parent_node_id
.borrow_mut()
.insert(node_id, parent_id);
}
fn parent_node_id(&self, node_id: NodeId) -> Option<NodeId> {
self.tables()
.node_id_to_parent_node_id
.borrow()
.get(&node_id)
.cloned()
}
fn is_parent_of(&self, parent_id: NodeId, child_id: NodeId) -> bool {
if parent_id == child_id {
true
} else {
match self.parent_node_id(child_id) {
Some(id) => self.is_parent_of(parent_id, id),
None => false,
}
}
}
fn resolve_upwards_or_error(&self, name: Spanned<Name>, start_at: NodeId) -> Result<NodeId> {
match self.gcx().resolve_upwards(name.value, start_at)? {
Some(id) => Ok(id),
None => {
self.emit(
DiagBuilder2::error(format!("`{}` not found", name.value)).span(name.span),
);
Err(())
}
}
}
fn resolve_downwards_or_error(&self, name: Spanned<Name>, start_at: NodeId) -> Result<NodeId> {
match self.gcx().resolve_downwards(name.value, start_at)? {
Some(id) => Ok(id),
None => {
self.emit(
DiagBuilder2::error(format!(
"`{}` not found in {}",
name.value,
self.ast_of(start_at)?.desc_full()
))
.span(name.span),
);
Err(())
}
}
}
fn set_lowering_hint(&self, node_id: NodeId, hint: hir::Hint) {
self.tables()
.lowering_hints
.borrow_mut()
.insert(node_id, hint);
}
fn lowering_hint(&self, node_id: NodeId) -> Option<hir::Hint> {
self.tables().lowering_hints.borrow().get(&node_id).cloned()
}
fn constant_int_value_of(&self, node_id: NodeId, env: ParamEnv) -> Result<&'gcx num::BigInt> {
match self.gcx().constant_value_of(node_id, env).kind {
ValueKind::Int(ref x, ..) => Ok(x),
ValueKind::Error => Err(()),
_ => {
let hir = self.gcx().hir_of(node_id)?;
self.emit(
DiagBuilder2::error(format!("{} is not a constant integer", hir.desc_full()))
.span(hir.human_span()),
);
Err(())
}
}
}
}
struct AstMapRegistrator<'a, 'b> {
cx: &'b GlobalContext<'a>,
}
impl<'a, 'b> ast::Visitor<'a> for AstMapRegistrator<'a, 'b> {
fn post_visit_node(&mut self, node: &'a dyn ast::AnyNode<'a>) {
self.cx.gcx().ast_map2.borrow_mut().insert(node.id(), node);
for n in AstNode::from_all(node.as_all()) {
if let AstNode::Any(_) = n {
continue;
}
let parent = n.get_any().unwrap().get_parent().unwrap();
let parent = if let Some(kind) = parent.as_all().get_type_kind() {
kind.get_parent().unwrap()
} else {
parent
};
self.cx.map_ast_with_parent(n, parent.id());
}
}
fn post_visit_module(&mut self, node: &'a ast::Module<'a>) {
self.cx.canonicalize_ports(node);
}
fn post_visit_interface(&mut self, node: &'a ast::Interface<'a>) {
self.cx.canonicalize_ports(node);
}
}