use crate::salsa;
use crate::{
ast,
ast_map::{AstMap, AstNode},
common::{arenas::Alloc, arenas::TypedArena, Session},
crate_prelude::*,
hir::{self, AccessTable, HirNode},
resolver::StructDef,
ty::{Type, TypeKind},
typeck::TypeContext,
value::{Value, ValueData, ValueKind},
ParamEnv, ParamEnvData, ParamEnvSource, PortMapping, PortMappingSource,
};
use std::{
cell::RefCell,
collections::{BTreeSet, HashMap, HashSet},
sync::Arc,
};
pub struct GlobalContext<'gcx> {
pub sess: &'gcx Session,
pub arena: &'gcx GlobalArenas<'gcx>,
runtime: salsa::Runtime<GlobalContext<'gcx>>,
ast_map: AstMap<'gcx>,
modules: RefCell<HashMap<Name, NodeId>>,
packages: RefCell<HashMap<Name, 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,
runtime: Default::default(),
ast_map: Default::default(),
modules: Default::default(),
packages: Default::default(),
node_id_to_span: Default::default(),
tables: Default::default(),
}
}
pub fn add_root_nodes(&self, ast: impl Iterator<Item = &'gcx ast::Root>) {
for root in ast {
for item in &root.items {
match *item {
ast::Item::Module(ref m) => {
let id = self.map_ast(AstNode::Module(m));
self.modules.borrow_mut().insert(m.name, id);
}
ast::Item::Package(ref p) => {
let id = self.map_ast(AstNode::Package(p));
self.packages.borrow_mut().insert(p.name, id);
}
_ => {
let _: Result<()> = self.unimp(item);
}
}
}
}
}
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()
}
}
impl DiagEmitter for GlobalContext<'_> {
fn emit(&self, diag: DiagBuilder2) {
self.sess.emit(diag)
}
}
impl<'gcx> salsa::Database for GlobalContext<'gcx> {
fn salsa_runtime(&self) -> &salsa::Runtime<Self> {
&self.runtime
}
}
impl<'gcx> BaseContext<'gcx> for GlobalContext<'gcx> {
fn gcx(&self) -> &GlobalContext<'gcx> {
self
}
}
pub struct GlobalArenas<'t> {
ids: TypedArena<NodeId>,
hir: hir::Arena<'t>,
param_envs: TypedArena<ParamEnvData<'t>>,
ribs: TypedArena<Rib>,
types: TypedArena<TypeKind<'t>>,
values: TypedArena<ValueData<'t>>,
mir_lvalue: TypedArena<mir::Lvalue<'t>>,
mir_rvalue: TypedArena<mir::Rvalue<'t>>,
}
impl Default for GlobalArenas<'_> {
fn default() -> Self {
GlobalArenas {
ids: TypedArena::new(),
hir: Default::default(),
param_envs: TypedArena::new(),
ribs: TypedArena::new(),
types: TypedArena::new(),
values: TypedArena::new(),
mir_lvalue: TypedArena::new(),
mir_rvalue: TypedArena::new(),
}
}
}
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_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)
}
}
#[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_types: RefCell<HashSet<Type<'t>>>,
interned_values: RefCell<HashSet<Value<'t>>>,
lowering_hints: RefCell<HashMap<NodeId, hir::Hint>>,
}
pub trait BaseContext<'gcx>: salsa::Database + DiagEmitter {
#[inline(always)]
fn gcx(&self) -> &GlobalContext<'gcx>;
#[inline(always)]
fn sess(&self) -> &'gcx Session {
self.gcx().sess
}
#[inline(always)]
fn arena(&self) -> &'gcx GlobalArenas<'gcx> {
self.gcx().arena
}
#[inline(always)]
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.gcx().node_id_to_span.borrow_mut().insert(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_ast(&self, node_id: NodeId, ast: AstNode<'gcx>) {
self.gcx().ast_map.set(node_id, ast)
}
fn map_ast(&self, ast: AstNode<'gcx>) -> NodeId {
let id = 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) => Ok(node),
None => {
if let Some(&span) = self.gcx().node_id_to_span.borrow().get(&node_id) {
self.emit(
DiagBuilder2::bug(format!(
"no ast node for `{}` in the map",
span.extract()
))
.span(span),
);
} else {
self.emit(DiagBuilder2::bug(format!(
"no ast node for {:?} in the map",
node_id
)));
}
Err(())
}
}
}
fn intern_type(&self, ty: TypeKind<'gcx>) -> Type<'gcx> {
if let Some(&x) = self.tables().interned_types.borrow().get(&ty) {
return x;
}
let ty = self.arena().types.alloc(ty);
self.tables().interned_types.borrow_mut().insert(ty);
ty
}
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 mkty_void(&self) -> Type<'gcx> {
&ty::VOID_TYPE
}
fn mkty_time(&self) -> Type<'gcx> {
&ty::TIME_TYPE
}
fn mkty_bit(&self) -> Type<'gcx> {
&ty::BIT_TYPE
}
fn mkty_logic(&self) -> Type<'gcx> {
&ty::LOGIC_TYPE
}
fn mkty_named(&self, name: Spanned<Name>, binding: NodeEnvId) -> Type<'gcx> {
self.intern_type(TypeKind::Named(
name,
binding.0,
self.gcx()
.map_to_type(binding.0, binding.1)
.unwrap_or(self.mkty_void()),
))
}
fn mkty_int(&self, width: usize) -> Type<'gcx> {
self.intern_type(TypeKind::Int(width, ty::Domain::TwoValued))
}
fn mkty_integer(&self, width: usize) -> Type<'gcx> {
self.intern_type(TypeKind::Int(width, ty::Domain::FourValued))
}
fn mkty_struct(&self, def_id: NodeId) -> Type<'gcx> {
self.intern_type(TypeKind::Struct(def_id))
}
fn mkty_packed_array(&self, size: usize, elem_ty: Type<'gcx>) -> Type<'gcx> {
self.intern_type(TypeKind::PackedArray(size, elem_ty))
}
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) {
if let Some(old_id) = self
.tables()
.node_id_to_parent_node_id
.borrow_mut()
.insert(node_id, parent_id)
{
panic!(
"node {:?} already had parent {:?} (overwritten with {:?} now)",
node_id, old_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),
_ => {
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(())
}
}
}
}
pub(super) mod queries {
use super::*;
query_group! {
pub trait Context<'a>: BaseContext<'a> {
fn hir_of(node_id: NodeId) -> Result<HirNode<'a>> {
type HirOfQuery;
use fn hir::hir_of;
}
fn param_env(src: ParamEnvSource<'a>) -> Result<ParamEnv> {
type ParamEnvQuery;
use fn param_env::compute;
}
fn type_of(node_id: NodeId, env: ParamEnv) -> Result<Type<'a>> {
type TypeOfQuery;
use fn typeck::type_of;
}
fn map_to_type(node_id: NodeId, env: ParamEnv) -> Result<Type<'a>> {
type MapToTypeQuery;
use fn typeck::map_to_type;
}
fn self_determined_type(node_id: NodeId, env: ParamEnv) -> Option<Type<'a>> {
type SelfDeterminedTypeQuery;
use fn typeck::self_determined_type;
}
fn need_self_determined_type(node_id: NodeId, env: ParamEnv) -> Type<'a> {
type NeedSelfDeterminedTypeQuery;
use fn typeck::need_self_determined_type;
}
fn operation_type(node_id: NodeId, env: ParamEnv) -> Option<Type<'a>> {
type OperationTypeQuery;
use fn typeck::operation_type;
}
fn need_operation_type(node_id: NodeId, env: ParamEnv) -> Type<'a> {
type NeedOperationTypeQuery;
use fn typeck::need_operation_type;
}
fn type_context(node_id: NodeId, env: ParamEnv) -> Option<TypeContext<'a>> {
type TypeContextQuery;
use fn typeck::type_context;
}
fn need_type_context(node_id: NodeId, env: ParamEnv) -> TypeContext<'a> {
type NeedTypeContextQuery;
use fn typeck::need_type_context;
}
fn local_rib(node_id: NodeId) -> Result<&'a Rib> {
type LocalRibQuery;
use fn resolver::local_rib;
}
fn hierarchical_rib(node_id: NodeId) -> Result<&'a Rib> {
type HierarchicalRibQuery;
use fn resolver::hierarchical_rib;
}
fn resolve_upwards(name: Name, start_at: NodeId) -> Result<Option<NodeId>> {
type ResolveUpwardsQuery;
use fn resolver::resolve_upwards;
}
fn resolve_downwards(name: Name, start_at: NodeId) -> Result<Option<NodeId>> {
type ResolveDownwardsQuery;
use fn resolver::resolve_downwards;
}
fn resolve_node(node_id: NodeId, env: ParamEnv) -> Result<NodeId> {
type ResolveNodeQuery;
use fn resolver::resolve_node;
}
fn constant_value_of(node_id: NodeId, env: ParamEnv) -> Result<Value<'a>> {
type ConstantValueOfQuery;
use fn value::constant_value_of;
}
fn is_constant(node_id: NodeId) -> Result<bool> {
type IsConstantQuery;
use fn value::is_constant;
}
fn type_default_value(ty: Type<'a>) -> Value<'a> {
type TypeDefaultValueQuery;
use fn value::type_default_value;
}
fn accessed_nodes(node_id: NodeId) -> Result<Arc<AccessTable>> {
type AccessedNodesQuery;
use fn hir::accessed_nodes;
}
fn port_mapping(src: PortMappingSource<'a>) -> Result<Arc<PortMapping>> {
type PortMappingQuery;
use fn port_mapping::compute;
}
fn struct_def(node_id: NodeId) -> Result<Arc<StructDef>> {
type StructDefQuery;
use fn resolver::struct_def;
}
fn resolve_field_access(node_id: NodeId, env: ParamEnv) -> Result<(NodeId, usize, NodeId)> {
type ResolveFieldAccessQuery;
use fn resolver::resolve_field_access;
}
fn mir_lvalue(
expr_id: NodeId,
env: ParamEnv,
) -> &'a mir::Lvalue<'a> {
type MirLvalueQuery;
use fn mir::lower::lvalue::lower_expr;
}
fn mir_rvalue(
expr_id: NodeId,
env: ParamEnv,
) -> &'a mir::Rvalue<'a> {
type MirRvalueQuery;
use fn mir::lower::rvalue::lower_expr;
}
}
}
database_storage! {
pub struct GlobalStorage<'gcx> for GlobalContext<'gcx> {
impl Context<'gcx> {
fn hir_of() for HirOfQuery<'gcx>;
fn param_env() for ParamEnvQuery<'gcx>;
fn type_of() for TypeOfQuery<'gcx>;
fn map_to_type() for MapToTypeQuery<'gcx>;
fn self_determined_type() for SelfDeterminedTypeQuery<'gcx>;
fn need_self_determined_type() for NeedSelfDeterminedTypeQuery<'gcx>;
fn operation_type() for OperationTypeQuery<'gcx>;
fn need_operation_type() for NeedOperationTypeQuery<'gcx>;
fn type_context() for TypeContextQuery<'gcx>;
fn need_type_context() for NeedTypeContextQuery<'gcx>;
fn local_rib() for LocalRibQuery<'gcx>;
fn hierarchical_rib() for HierarchicalRibQuery<'gcx>;
fn resolve_upwards() for ResolveUpwardsQuery<'gcx>;
fn resolve_downwards() for ResolveDownwardsQuery<'gcx>;
fn constant_value_of() for ConstantValueOfQuery<'gcx>;
fn is_constant() for IsConstantQuery<'gcx>;
fn type_default_value() for TypeDefaultValueQuery<'gcx>;
fn resolve_node() for ResolveNodeQuery<'gcx>;
fn accessed_nodes() for AccessedNodesQuery<'gcx>;
fn port_mapping() for PortMappingQuery<'gcx>;
fn struct_def() for StructDefQuery<'gcx>;
fn resolve_field_access() for ResolveFieldAccessQuery<'gcx>;
fn mir_lvalue() for MirLvalueQuery<'gcx>;
fn mir_rvalue() for MirRvalueQuery<'gcx>;
}
}
}
}
pub use self::queries::Context;