use super::analyze::*;
use super::region::*;
use crate::ast::*;
use crate::data::*;
use std::sync::Arc;
macro_rules! try_unknown {
($expr:expr) => {
if let Some(value) = $expr? {
value
} else {
return Ok(None);
}
};
}
#[derive(Debug)]
pub enum ObjectBase {
Object(ObjectEnt),
DeferredConstant,
ExternalName(ExternalObjectClass),
}
impl ObjectBase {
pub fn mode(&self) -> Option<Mode> {
match self {
ObjectBase::Object(object) => object.mode(),
ObjectBase::DeferredConstant => None,
ObjectBase::ExternalName(_) => None,
}
}
pub fn class(&self) -> ObjectClass {
match self {
ObjectBase::Object(object) => object.class(),
ObjectBase::DeferredConstant => ObjectClass::Constant,
ObjectBase::ExternalName(class) => (*class).into(),
}
}
pub fn describe_class(&self) -> String {
if let Some(mode) = self.mode() {
if self.class() == ObjectClass::Constant {
format!("interface {}", self.class())
} else {
format!("interface {} of mode {}", self.class(), mode)
}
} else {
format!("{}", self.class())
}
}
}
#[derive(Debug)]
pub enum ResolvedName {
Library(Symbol),
Design(DesignEnt),
Type(TypeEnt),
Overloaded(OverloadedName),
ObjectSelection {
base: ObjectBase,
type_mark: TypeEnt,
},
Final(Arc<AnyEnt>),
}
impl ResolvedName {
fn from_design(ent: Arc<AnyEnt>) -> Result<Self, String> {
let name = match ent.kind() {
AnyEntKind::Object(object) => {
let type_mark = object.subtype.type_mark().to_owned();
ResolvedName::ObjectSelection {
base: ObjectBase::Object(ObjectEnt::new(ent)),
type_mark,
}
}
AnyEntKind::ObjectAlias {
base_object,
type_mark,
} => ResolvedName::ObjectSelection {
base: ObjectBase::Object(base_object.clone()),
type_mark: type_mark.to_owned(),
},
AnyEntKind::ExternalAlias { class, type_mark } => ResolvedName::ObjectSelection {
base: ObjectBase::ExternalName(*class),
type_mark: type_mark.clone(),
},
AnyEntKind::DeferredConstant(subtype) => ResolvedName::ObjectSelection {
base: ObjectBase::DeferredConstant,
type_mark: subtype.type_mark().clone(),
},
AnyEntKind::Type(_) => ResolvedName::Type(TypeEnt::from_any(ent).unwrap()),
AnyEntKind::Overloaded(_) => ResolvedName::Overloaded(OverloadedName::single(
OverloadedEnt::from_any(ent).unwrap(),
)),
AnyEntKind::File(_)
| AnyEntKind::InterfaceFile(_)
| AnyEntKind::Component(_)
| AnyEntKind::PhysicalLiteral(_) => ResolvedName::Final(ent.clone()),
AnyEntKind::Design(_)
| AnyEntKind::Library
| AnyEntKind::Attribute
| AnyEntKind::ElementDeclaration(_)
| AnyEntKind::Label
| AnyEntKind::LoopParameter => {
return Err(format!(
"{} cannot be selected from design unit",
ent.kind().describe()
))
}
};
Ok(name)
}
fn from_scope(ent: Arc<AnyEnt>) -> Result<Self, String> {
let name = match ent.kind() {
AnyEntKind::Object(object) => {
let type_mark = object.subtype.type_mark().to_owned();
ResolvedName::ObjectSelection {
base: ObjectBase::Object(ObjectEnt::new(ent)),
type_mark,
}
}
AnyEntKind::ObjectAlias {
base_object,
type_mark,
} => ResolvedName::ObjectSelection {
base: ObjectBase::Object(base_object.clone()),
type_mark: type_mark.to_owned(),
},
AnyEntKind::ExternalAlias { class, type_mark } => ResolvedName::ObjectSelection {
base: ObjectBase::ExternalName(*class),
type_mark: type_mark.clone(),
},
AnyEntKind::DeferredConstant(subtype) => ResolvedName::ObjectSelection {
base: ObjectBase::DeferredConstant,
type_mark: subtype.type_mark().clone(),
},
AnyEntKind::Type(_) => ResolvedName::Type(TypeEnt::from_any(ent).unwrap()),
AnyEntKind::Design(_) => ResolvedName::Design(DesignEnt::from_any(ent).unwrap()),
AnyEntKind::Library => {
ResolvedName::Library(ent.designator().as_identifier().cloned().unwrap())
}
AnyEntKind::Overloaded(_) => ResolvedName::Overloaded(OverloadedName::single(
OverloadedEnt::from_any(ent).unwrap(),
)),
AnyEntKind::File(_)
| AnyEntKind::InterfaceFile(_)
| AnyEntKind::Component(_)
| AnyEntKind::Label
| AnyEntKind::LoopParameter
| AnyEntKind::PhysicalLiteral(_) => ResolvedName::Final(ent.clone()),
AnyEntKind::Attribute | AnyEntKind::ElementDeclaration(_) => {
return Err(format!(
"{} should never be looked up from the current scope",
ent.kind().describe()
))
}
};
Ok(name)
}
fn describe(&self) -> String {
match self {
ResolvedName::Library(sym) => format!("library {}", sym),
ResolvedName::Design(ent) => ent.describe(),
ResolvedName::Type(ent) => ent.describe(),
ResolvedName::Overloaded(name) => name.describe(),
ResolvedName::ObjectSelection { type_mark, .. } => type_mark.describe(),
ResolvedName::Final(ent) => ent.describe(),
}
}
}
impl<'a> AnalyzeContext<'a> {
pub fn resolve_object_prefix(
&self,
scope: &Scope<'_>,
name_pos: &SrcPos,
name: &mut Name,
err_msg: &'static str,
diagnostics: &mut dyn DiagnosticHandler,
) -> AnalysisResult<Option<ResolvedName>> {
match name {
Name::Selected(prefix, suffix) => {
suffix.clear_reference();
let resolved = try_unknown!(self.resolve_object_prefix(
scope,
&prefix.pos,
&mut prefix.item,
err_msg,
diagnostics,
));
match resolved {
ResolvedName::Library(ref library_name) => {
Ok(Some(ResolvedName::Design(self.lookup_in_library(
library_name,
&suffix.pos,
&suffix.item.item,
&mut suffix.item.reference,
)?)))
}
ResolvedName::Design(ref ent) => {
let name = ent.selected(&prefix.pos, suffix)?;
suffix.set_reference(&name);
match name {
NamedEntities::Single(named_entity) => Ok(Some(
ResolvedName::from_design(named_entity)
.map_err(|e| Diagnostic::error(&suffix.pos, e))?,
)),
NamedEntities::Overloaded(overloaded) => {
Ok(Some(ResolvedName::Overloaded(overloaded)))
}
}
}
ResolvedName::Type(..) => Err(Diagnostic::error(name_pos, err_msg).into()),
ResolvedName::ObjectSelection { base, type_mark } => {
match type_mark.selected(&prefix.pos, suffix)? {
TypedSelection::RecordElement(elem) => {
suffix.set_unique_reference(elem.as_ref());
Ok(Some(ResolvedName::ObjectSelection {
base,
type_mark: elem.type_mark().to_owned(),
}))
}
TypedSelection::ProtectedMethod(overloaded) => {
suffix.set_reference(&overloaded);
Err(Diagnostic::error(name_pos, err_msg).into())
}
}
}
ResolvedName::Overloaded(..) => {
Err(Diagnostic::error(name_pos, err_msg).into())
}
ResolvedName::Final(..) => Err(Diagnostic::error(name_pos, err_msg).into()),
}
}
Name::SelectedAll(prefix) => {
let resolved = try_unknown!(self.resolve_object_prefix(
scope,
&prefix.pos,
&mut prefix.item,
err_msg,
diagnostics,
));
if let ResolvedName::ObjectSelection { base, type_mark } = resolved {
if let Some(type_mark) = type_mark.accessed_type() {
return Ok(Some(ResolvedName::ObjectSelection {
base,
type_mark: type_mark.clone(),
}));
}
}
Err(Diagnostic::error(&prefix.pos, "Cannot be the prefix of .all").into())
}
Name::Designator(designator) => {
designator.clear_reference();
let name = scope.lookup(name_pos, designator.designator())?;
designator.set_reference(&name);
match name {
NamedEntities::Single(named_entity) => Ok(Some(
ResolvedName::from_scope(named_entity)
.map_err(|e| Diagnostic::error(name_pos, e))?,
)),
NamedEntities::Overloaded(overloaded) => {
Ok(Some(ResolvedName::Overloaded(overloaded)))
}
}
}
Name::Indexed(ref mut prefix, ref mut indexes) => {
let resolved = try_unknown!(self.resolve_object_prefix(
scope,
&prefix.pos,
&mut prefix.item,
err_msg,
diagnostics,
));
if let ResolvedName::ObjectSelection { base, type_mark } = resolved {
let elem_type = self.analyze_indexed_name(
scope,
name_pos,
prefix.suffix_pos(),
&type_mark,
indexes,
diagnostics,
)?;
Ok(Some(ResolvedName::ObjectSelection {
base,
type_mark: elem_type,
}))
} else {
for expr in indexes.iter_mut() {
self.analyze_expression(scope, expr, diagnostics)?;
}
Err(Diagnostic::error(&prefix.pos, err_msg).into())
}
}
Name::Slice(ref mut prefix, ref mut drange) => {
let resolved = try_unknown!(self.resolve_object_prefix(
scope,
&prefix.pos,
&mut prefix.item,
err_msg,
diagnostics,
));
self.analyze_discrete_range(scope, drange.as_mut(), diagnostics)?;
if let ResolvedName::ObjectSelection { ref type_mark, .. } = resolved {
if !type_mark.can_be_sliced() {
return Err(Diagnostic::error(
prefix.suffix_pos(),
format!("{} cannot be sliced", type_mark.describe()),
)
.into());
} else {
Ok(Some(resolved))
}
} else {
Err(Diagnostic::error(
prefix.suffix_pos(),
format!("{} cannot be sliced", resolved.describe()),
)
.into())
}
}
Name::Attribute(..) => Err(Diagnostic::error(name_pos, err_msg).into()),
Name::FunctionCall(ref mut fcall) => {
if let Some((prefix, indexes)) = fcall.to_indexed() {
*name = Name::Indexed(prefix, indexes);
self.resolve_object_prefix(scope, name_pos, name, err_msg, diagnostics)
} else {
Err(Diagnostic::error(name_pos, err_msg).into())
}
}
Name::External(ref mut ename) => {
let ExternalName { subtype, class, .. } = ename.as_mut();
let subtype = self.resolve_subtype_indication(scope, subtype, diagnostics)?;
Ok(Some(ResolvedName::ObjectSelection {
base: ObjectBase::ExternalName(*class),
type_mark: subtype.type_mark().to_owned(),
}))
}
}
}
}