use crate::{
decl_engine::DeclRef,
engine_threading::Engines,
language::{
parsed::*,
ty::{self, TyTraitItem},
CallPath, Visibility,
},
semantic_analysis::*,
transform::to_parsed_lang,
Ident, Namespace, TypeId, TypeInfo,
};
use super::{
lexical_scope::{Items, LexicalScope, SymbolMap},
root::Root,
LexicalScopeId, ModuleName, ModulePath, ModulePathBuf,
};
use sway_ast::ItemConst;
use sway_error::handler::Handler;
use sway_error::{error::CompileError, handler::ErrorEmitted};
use sway_parse::{lex, Parser};
use sway_types::{span::Span, Spanned};
#[derive(Clone, Debug)]
pub struct Module {
pub(crate) submodules: im::OrdMap<ModuleName, Module>,
pub lexical_scopes: Vec<LexicalScope>,
pub current_lexical_scope_id: LexicalScopeId,
pub name: Option<Ident>,
pub visibility: Visibility,
pub span: Option<Span>,
pub is_external: bool,
pub(crate) mod_path: ModulePathBuf,
}
impl Default for Module {
fn default() -> Self {
Self {
visibility: Visibility::Private,
submodules: Default::default(),
lexical_scopes: vec![LexicalScope::default()],
current_lexical_scope_id: 0,
name: Default::default(),
span: Default::default(),
is_external: Default::default(),
mod_path: Default::default(),
}
}
}
impl Module {
pub fn mod_path(&self) -> &ModulePath {
self.mod_path.as_slice()
}
pub fn mod_path_buf(&self) -> ModulePathBuf {
self.mod_path.clone()
}
pub fn default_with_contract_id(
engines: &Engines,
name: Option<Ident>,
contract_id_value: String,
experimental: crate::ExperimentalFlags,
) -> Result<Self, vec1::Vec1<CompileError>> {
let handler = <_>::default();
Module::default_with_contract_id_inner(
&handler,
engines,
name,
contract_id_value,
experimental,
)
.map_err(|_| {
let (errors, warnings) = handler.consume();
assert!(warnings.is_empty());
vec1::Vec1::try_from_vec(errors).unwrap()
})
}
fn default_with_contract_id_inner(
handler: &Handler,
engines: &Engines,
ns_name: Option<Ident>,
contract_id_value: String,
experimental: crate::ExperimentalFlags,
) -> Result<Self, ErrorEmitted> {
let mut compiled_constants: SymbolMap = Default::default();
let const_item = format!("pub const CONTRACT_ID: b256 = {contract_id_value};");
let const_item_len = const_item.len();
let input_arc = std::sync::Arc::from(const_item);
let token_stream = lex(handler, &input_arc, 0, const_item_len, None).unwrap();
let mut parser = Parser::new(handler, &token_stream);
let const_item: ItemConst = parser.parse()?;
let const_item_span = const_item.span();
let name = const_item.name.clone();
let attributes = Default::default();
let const_decl_id = to_parsed_lang::item_const_to_constant_declaration(
&mut to_parsed_lang::Context::new(crate::BuildTarget::EVM, experimental),
handler,
engines,
const_item,
attributes,
true,
)?;
let const_decl = engines.pe().get_constant(&const_decl_id);
let has_literal = match &const_decl.value {
Some(value) => {
matches!(value.kind, ExpressionKind::Literal(_))
}
None => false,
};
if !has_literal {
return Err(handler.emit_err(CompileError::ContractIdValueNotALiteral {
span: const_item_span,
}));
}
let ast_node = AstNode {
content: AstNodeContent::Declaration(Declaration::ConstantDeclaration(const_decl_id)),
span: const_item_span.clone(),
};
let root = Root::from(Module::default());
let mut ns = Namespace::init_root(root);
ns.root.module.name = ns_name;
ns.root.module.is_external = true;
ns.root.module.visibility = Visibility::Public;
let type_check_ctx = TypeCheckContext::from_namespace(&mut ns, engines, experimental);
let typed_node = ty::TyAstNode::type_check(handler, type_check_ctx, ast_node).unwrap();
let typed_decl = match typed_node.content {
ty::TyAstNodeContent::Declaration(decl) => decl,
_ => {
return Err(
handler.emit_err(CompileError::ContractIdConstantNotAConstDecl {
span: const_item_span,
}),
);
}
};
compiled_constants.insert(name, typed_decl);
let mut ret = Self::default();
ret.current_lexical_scope_mut().items.symbols = compiled_constants;
Ok(ret)
}
pub fn submodules(&self) -> &im::OrdMap<ModuleName, Module> {
&self.submodules
}
pub fn insert_submodule(&mut self, name: String, submodule: Module) {
self.submodules.insert(name, submodule);
}
pub fn submodule(&self, path: &ModulePath) -> Option<&Module> {
let mut module = self;
for ident in path.iter() {
match module.submodules.get(ident.as_str()) {
Some(ns) => module = ns,
None => return None,
}
}
Some(module)
}
pub fn submodule_mut(&mut self, path: &ModulePath) -> Option<&mut Module> {
let mut module = self;
for ident in path.iter() {
match module.submodules.get_mut(ident.as_str()) {
Some(ns) => module = ns,
None => return None,
}
}
Some(module)
}
pub(crate) fn lookup_submodule(
&self,
handler: &Handler,
path: &[Ident],
) -> Result<&Module, ErrorEmitted> {
match self.submodule(path) {
None => Err(handler.emit_err(module_not_found(path))),
Some(module) => Ok(module),
}
}
fn current_lexical_scope(&self) -> &LexicalScope {
self.lexical_scopes
.get(self.current_lexical_scope_id)
.unwrap()
}
fn current_lexical_scope_mut(&mut self) -> &mut LexicalScope {
self.lexical_scopes
.get_mut(self.current_lexical_scope_id)
.unwrap()
}
pub fn current_items(&self) -> &Items {
&self.current_lexical_scope().items
}
pub fn current_items_mut(&mut self) -> &mut Items {
&mut self.current_lexical_scope_mut().items
}
pub fn current_lexical_scope_id(&self) -> LexicalScopeId {
self.current_lexical_scope_id
}
pub fn push_new_lexical_scope(&mut self) -> LexicalScopeId {
let previous_scope_id = self.current_lexical_scope_id();
let new_scoped_id = {
self.lexical_scopes.push(LexicalScope {
parent: Some(previous_scope_id),
..Default::default()
});
self.current_lexical_scope_id()
};
let previous_scope = self.lexical_scopes.get_mut(previous_scope_id).unwrap();
previous_scope.children.push(new_scoped_id);
self.current_lexical_scope_id = new_scoped_id;
new_scoped_id
}
pub fn pop_lexical_scope(&mut self) {
let parent_scope_id = self.current_lexical_scope().parent;
self.current_lexical_scope_id = parent_scope_id.unwrap_or(0);
}
pub(crate) fn resolve_call_path(
&self,
handler: &Handler,
engines: &Engines,
mod_path: &ModulePath,
call_path: &CallPath,
self_type: Option<TypeId>,
) -> Result<ty::TyDecl, ErrorEmitted> {
let (decl, _) =
self.resolve_call_path_and_mod_path(handler, engines, mod_path, call_path, self_type)?;
Ok(decl)
}
pub(crate) fn resolve_call_path_and_mod_path(
&self,
handler: &Handler,
engines: &Engines,
mod_path: &ModulePath,
call_path: &CallPath,
self_type: Option<TypeId>,
) -> Result<(ty::TyDecl, Vec<Ident>), ErrorEmitted> {
let symbol_path: Vec<_> = mod_path
.iter()
.chain(&call_path.prefixes)
.cloned()
.collect();
self.resolve_symbol_and_mod_path(
handler,
engines,
&symbol_path,
&call_path.suffix,
self_type,
)
}
pub(crate) fn resolve_call_path_and_root_type_id(
&self,
handler: &Handler,
engines: &Engines,
root_type_id: TypeId,
mut as_trait: Option<CallPath>,
call_path: &CallPath,
self_type: Option<TypeId>,
) -> Result<ty::TyDecl, ErrorEmitted> {
let mut decl_opt = None;
let mut type_id_opt = Some(root_type_id);
for ident in call_path.prefixes.iter() {
if let Some(type_id) = type_id_opt {
type_id_opt = None;
decl_opt = Some(self.resolve_associated_type_from_type_id(
handler,
engines,
ident,
type_id,
as_trait.clone(),
self_type,
)?);
as_trait = None;
} else if let Some(decl) = decl_opt {
decl_opt = Some(self.resolve_associated_type(
handler,
engines,
ident,
decl,
as_trait.clone(),
self_type,
)?);
as_trait = None;
}
}
if let Some(type_id) = type_id_opt {
let decl = self.resolve_associated_type_from_type_id(
handler,
engines,
&call_path.suffix,
type_id,
as_trait,
self_type,
)?;
return Ok(decl);
}
if let Some(decl) = decl_opt {
let decl = self.resolve_associated_item(
handler,
engines,
&call_path.suffix,
decl,
as_trait,
self_type,
)?;
Ok(decl)
} else {
Err(handler.emit_err(CompileError::Internal("Unexpected error", call_path.span())))
}
}
pub(crate) fn resolve_symbol(
&self,
handler: &Handler,
engines: &Engines,
mod_path: &ModulePath,
symbol: &Ident,
self_type: Option<TypeId>,
) -> Result<ty::TyDecl, ErrorEmitted> {
let (decl, _) =
self.resolve_symbol_and_mod_path(handler, engines, mod_path, symbol, self_type)?;
Ok(decl)
}
fn resolve_symbol_and_mod_path(
&self,
handler: &Handler,
engines: &Engines,
mod_path: &ModulePath,
symbol: &Ident,
self_type: Option<TypeId>,
) -> Result<(ty::TyDecl, Vec<Ident>), ErrorEmitted> {
let mut module = self;
let mut current_mod_path = vec![];
let mut decl_opt = None;
for ident in mod_path.iter() {
if let Some(decl) = decl_opt {
decl_opt = Some(
self.resolve_associated_type(handler, engines, ident, decl, None, self_type)?,
);
} else {
match module.submodules.get(ident.as_str()) {
Some(ns) => {
module = ns;
current_mod_path.push(ident.clone());
}
None => {
decl_opt = Some(self.resolve_symbol_helper(
handler,
engines,
¤t_mod_path,
ident,
module,
self_type,
)?);
}
}
}
}
if let Some(decl) = decl_opt {
let decl =
self.resolve_associated_item(handler, engines, symbol, decl, None, self_type)?;
return Ok((decl, current_mod_path));
}
self.lookup_submodule(handler, mod_path).and_then(|module| {
let decl =
self.resolve_symbol_helper(handler, engines, mod_path, symbol, module, self_type)?;
Ok((decl, mod_path.to_vec()))
})
}
fn resolve_associated_type(
&self,
handler: &Handler,
engines: &Engines,
symbol: &Ident,
decl: ty::TyDecl,
as_trait: Option<CallPath>,
self_type: Option<TypeId>,
) -> Result<ty::TyDecl, ErrorEmitted> {
let type_info = self.decl_to_type_info(handler, engines, symbol, decl)?;
self.resolve_associated_type_from_type_id(
handler,
engines,
symbol,
engines
.te()
.insert(engines, type_info, symbol.span().source_id()),
as_trait,
self_type,
)
}
fn resolve_associated_item(
&self,
handler: &Handler,
engines: &Engines,
symbol: &Ident,
decl: ty::TyDecl,
as_trait: Option<CallPath>,
self_type: Option<TypeId>,
) -> Result<ty::TyDecl, ErrorEmitted> {
let type_info = self.decl_to_type_info(handler, engines, symbol, decl)?;
self.resolve_associated_item_from_type_id(
handler,
engines,
symbol,
engines
.te()
.insert(engines, type_info, symbol.span().source_id()),
as_trait,
self_type,
)
}
fn decl_to_type_info(
&self,
handler: &Handler,
engines: &Engines,
symbol: &Ident,
decl: ty::TyDecl,
) -> Result<TypeInfo, ErrorEmitted> {
Ok(match decl.clone() {
ty::TyDecl::StructDecl(struct_decl) => TypeInfo::Struct(DeclRef::new(
struct_decl.name.clone(),
struct_decl.decl_id,
struct_decl.name.span(),
)),
ty::TyDecl::EnumDecl(enum_decl) => TypeInfo::Enum(DeclRef::new(
enum_decl.name.clone(),
enum_decl.decl_id,
enum_decl.name.span(),
)),
ty::TyDecl::TraitTypeDecl(type_decl) => {
let type_decl = engines.de().get_type(&type_decl.decl_id);
(*engines.te().get(type_decl.ty.clone().unwrap().type_id)).clone()
}
_ => {
return Err(handler.emit_err(CompileError::SymbolNotFound {
name: symbol.clone(),
span: symbol.span(),
}))
}
})
}
fn resolve_associated_type_from_type_id(
&self,
handler: &Handler,
engines: &Engines,
symbol: &Ident,
type_id: TypeId,
as_trait: Option<CallPath>,
self_type: Option<TypeId>,
) -> Result<ty::TyDecl, ErrorEmitted> {
let item_decl = self.resolve_associated_item_from_type_id(
handler, engines, symbol, type_id, as_trait, self_type,
)?;
if !matches!(item_decl, ty::TyDecl::TraitTypeDecl(_)) {
return Err(handler.emit_err(CompileError::Internal(
"Expecting associated type",
item_decl.span(),
)));
}
Ok(item_decl)
}
fn resolve_associated_item_from_type_id(
&self,
handler: &Handler,
engines: &Engines,
symbol: &Ident,
type_id: TypeId,
as_trait: Option<CallPath>,
self_type: Option<TypeId>,
) -> Result<ty::TyDecl, ErrorEmitted> {
let type_id = if engines.te().get(type_id).is_self_type() {
if let Some(self_type) = self_type {
self_type
} else {
return Err(handler.emit_err(CompileError::Internal(
"Self type not provided.",
symbol.span(),
)));
}
} else {
type_id
};
let item_ref = self
.current_items()
.implemented_traits
.get_trait_item_for_type(handler, engines, symbol, type_id, as_trait)?;
match item_ref {
TyTraitItem::Fn(fn_ref) => Ok(fn_ref.into()),
TyTraitItem::Constant(const_ref) => Ok(const_ref.into()),
TyTraitItem::Type(type_ref) => Ok(type_ref.into()),
}
}
fn resolve_symbol_helper(
&self,
handler: &Handler,
engines: &Engines,
mod_path: &ModulePath,
symbol: &Ident,
module: &Module,
self_type: Option<TypeId>,
) -> Result<ty::TyDecl, ErrorEmitted> {
let true_symbol = self[mod_path]
.current_items()
.use_aliases
.get(symbol.as_str())
.unwrap_or(symbol);
match module.current_items().use_synonyms.get(symbol) {
Some((_, _, decl @ ty::TyDecl::EnumVariantDecl { .. })) => Ok(decl.clone()),
Some((src_path, _, _)) if mod_path != src_path => {
match module.current_items().symbols.get(true_symbol) {
Some(decl) => Ok(decl.clone()),
None => self.resolve_symbol(handler, engines, src_path, true_symbol, self_type),
}
}
_ => module
.current_items()
.check_symbol(true_symbol)
.map_err(|e| handler.emit_err(e))
.cloned(),
}
}
}
impl<'a> std::ops::Index<&'a ModulePath> for Module {
type Output = Module;
fn index(&self, path: &'a ModulePath) -> &Self::Output {
self.submodule(path)
.unwrap_or_else(|| panic!("no module for the given path {path:?}"))
}
}
impl<'a> std::ops::IndexMut<&'a ModulePath> for Module {
fn index_mut(&mut self, path: &'a ModulePath) -> &mut Self::Output {
self.submodule_mut(path)
.unwrap_or_else(|| panic!("no module for the given path {path:?}"))
}
}
impl From<Root> for Module {
fn from(root: Root) -> Self {
root.module
}
}
fn module_not_found(path: &[Ident]) -> CompileError {
CompileError::ModuleNotFound {
span: path.iter().fold(path[0].span(), |acc, this_one| {
if acc.source_id() == this_one.span().source_id() {
Span::join(acc, this_one.span())
} else {
acc
}
}),
name: path
.iter()
.map(|x| x.as_str())
.collect::<Vec<_>>()
.join("::"),
}
}