use super::named_entity::*;
use super::standard::StandardRegion;
use super::*;
use crate::ast::*;
use crate::data::*;
use analyze::*;
use region::*;
impl<'a> AnalyzeContext<'a> {
pub fn analyze_primary_unit(
&self,
id: EntityId,
unit: &mut AnyPrimaryUnit,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
match unit {
AnyPrimaryUnit::Entity(unit) => self.analyze_entity(id, unit, diagnostics),
AnyPrimaryUnit::Configuration(unit) => self.analyze_configuration(unit, diagnostics),
AnyPrimaryUnit::Package(unit) => self.analyze_package(id, unit, diagnostics),
AnyPrimaryUnit::PackageInstance(unit) => {
self.analyze_package_instance(id, unit, diagnostics)
}
AnyPrimaryUnit::Context(unit) => self.analyze_context(unit, diagnostics),
}
}
pub fn analyze_secondary_unit(
&self,
unit: &mut AnySecondaryUnit,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
match unit {
AnySecondaryUnit::Architecture(unit) => self.analyze_architecture(unit, diagnostics),
AnySecondaryUnit::PackageBody(unit) => self.analyze_package_body(unit, diagnostics),
}
}
fn analyze_entity(
&self,
id: EntityId,
unit: &mut EntityDeclaration,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
let root_scope = Scope::default();
self.add_implicit_context_clause(&root_scope)?;
self.analyze_context_clause(&root_scope, &mut unit.context_clause, diagnostics)?;
let primary_scope = root_scope.nested();
primary_scope.make_potentially_visible(Some(unit.pos()), self.arena.get(id));
if let Some(ref mut list) = unit.generic_clause {
self.analyze_interface_list(&primary_scope, list, diagnostics)?;
}
if let Some(ref mut list) = unit.port_clause {
self.analyze_interface_list(&primary_scope, list, diagnostics)?;
}
self.analyze_declarative_part(&primary_scope, &mut unit.decl, diagnostics)?;
self.analyze_concurrent_part(&primary_scope, &mut unit.statements, diagnostics)?;
let region = primary_scope.into_region();
let visibility = root_scope.into_visibility();
self.redefine(
id,
&mut unit.ident,
AnyEntKind::Design(Design::Entity(visibility, region)),
);
Ok(())
}
fn analyze_configuration(
&self,
unit: &mut ConfigurationDeclaration,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
let root_region = Scope::default();
self.add_implicit_context_clause(&root_region)?;
self.analyze_context_clause(&root_region, &mut unit.context_clause, diagnostics)?;
match self.lookup_entity_for_configuration(&root_region, unit) {
Ok(named_entity) => {
if let Some(primary_pos) = named_entity.decl_pos() {
let secondary_pos = unit.pos();
if primary_pos.source == secondary_pos.source
&& primary_pos.start() > secondary_pos.start()
{
diagnostics.push(Diagnostic::error(
secondary_pos,
capitalize(&format!(
"{} declared before {}",
self.current_unit_id().describe(),
named_entity.describe()
)),
));
}
}
}
Err(err) => {
err.add_to(diagnostics)?;
}
};
self.arena
.define(&mut unit.ident, AnyEntKind::Design(Design::Configuration));
Ok(())
}
fn analyze_package(
&self,
id: EntityId,
unit: &mut PackageDeclaration,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
let root_scope = Scope::default();
self.add_implicit_context_clause(&root_scope)?;
self.analyze_context_clause(&root_scope, &mut unit.context_clause, diagnostics)?;
let scope = root_scope.nested().in_package_declaration();
scope.make_potentially_visible(Some(unit.pos()), self.arena.get(id));
if let Some(ref mut list) = unit.generic_clause {
self.analyze_interface_list(&scope, list, diagnostics)?;
}
self.analyze_declarative_part(&scope, &mut unit.decl, diagnostics)?;
if !self.has_package_body() {
scope.close(diagnostics);
}
let mut region = scope.into_region();
if self.is_standard_package() {
let implicits = {
let standard = StandardRegion::new(self.root, self.arena, ®ion);
standard.end_of_package_implicits(self.arena)
};
for imp in implicits.into_iter() {
region.add(imp, diagnostics);
}
}
let visibility = root_scope.into_visibility();
self.redefine(
id,
&mut unit.ident,
if unit.generic_clause.is_some() {
AnyEntKind::Design(Design::UninstPackage(visibility, region))
} else {
AnyEntKind::Design(Design::Package(visibility, region))
},
);
Ok(())
}
fn analyze_package_instance(
&self,
id: EntityId,
unit: &mut PackageInstantiation,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
let root_scope = Scope::default();
self.add_implicit_context_clause(&root_scope)?;
self.analyze_context_clause(&root_scope, &mut unit.context_clause, diagnostics)?;
if let Some(pkg_region) = self.generic_package_instance(&root_scope, unit, diagnostics)? {
self.redefine(
id,
&mut unit.ident,
AnyEntKind::Design(Design::PackageInstance(pkg_region)),
);
}
Ok(())
}
fn analyze_context(
&self,
unit: &mut ContextDeclaration,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
let root_scope = Scope::default();
self.add_implicit_context_clause(&root_scope)?;
let scope = root_scope.nested();
self.analyze_context_clause(&scope, &mut unit.items, diagnostics)?;
self.arena.define(
&mut unit.ident,
AnyEntKind::Design(Design::Context(scope.into_region())),
);
Ok(())
}
fn analyze_architecture(
&self,
unit: &mut ArchitectureBody,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
let primary = match self.lookup_in_library(
self.work_library_name(),
&unit.entity_name.item.pos,
&Designator::Identifier(unit.entity_name.item.item.clone()),
&mut unit.entity_name.reference,
) {
Ok(primary) => primary,
Err(err) => {
diagnostics.push(err.into_non_fatal()?);
return Ok(());
}
};
self.check_secondary_before_primary(&primary, unit.pos(), diagnostics);
let (visibility, region) =
if let Design::Entity(ref visibility, ref region) = primary.kind() {
(visibility, region)
} else {
let mut diagnostic = Diagnostic::error(unit.pos(), "Expected an entity");
if let Some(pos) = primary.decl_pos() {
diagnostic.add_related(pos, format!("Found {}", primary.describe()))
}
return Ok(());
};
let root_scope = Scope::new(Region::with_visibility(visibility.clone()));
self.analyze_context_clause(&root_scope, &mut unit.context_clause, diagnostics)?;
let scope = Scope::extend(region, Some(&root_scope));
scope.make_potentially_visible(
Some(unit.pos()),
self.arena
.explicit(unit.name().clone(), AnyEntKind::Label, Some(unit.pos())),
);
self.analyze_declarative_part(&scope, &mut unit.decl, diagnostics)?;
self.analyze_concurrent_part(&scope, &mut unit.statements, diagnostics)?;
scope.close(diagnostics);
Ok(())
}
fn analyze_package_body(
&self,
unit: &mut PackageBody,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
let primary = match self.lookup_in_library(
self.work_library_name(),
&unit.ident.item.pos,
&Designator::Identifier(unit.ident.item.item.clone()),
&mut unit.ident.reference,
) {
Ok(primary) => primary,
Err(err) => {
diagnostics.push(err.into_non_fatal()?);
return Ok(());
}
};
self.check_secondary_before_primary(&primary, unit.pos(), diagnostics);
let (visibility, region) = match primary.kind() {
Design::Package(ref visibility, ref region)
| Design::UninstPackage(ref visibility, ref region) => (visibility, region),
_ => {
let mut diagnostic = Diagnostic::error(unit.pos(), "Expected a package");
if let Some(pos) = primary.decl_pos() {
diagnostic.add_related(pos, format!("Found {}", primary.describe()))
}
return Ok(());
}
};
let root_scope = Scope::new(Region::with_visibility(visibility.clone()));
self.analyze_context_clause(&root_scope, &mut unit.context_clause, diagnostics)?;
let scope = Scope::extend(region, Some(&root_scope));
self.analyze_declarative_part(&scope, &mut unit.decl, diagnostics)?;
scope.close(diagnostics);
Ok(())
}
fn check_secondary_before_primary(
&self,
primary: &DesignEnt,
secondary_pos: &SrcPos,
diagnostics: &mut dyn DiagnosticHandler,
) {
if let Some(primary_pos) = primary.decl_pos() {
if primary_pos.source == secondary_pos.source
&& primary_pos.start() > secondary_pos.start()
{
diagnostics.push(Diagnostic::error(
secondary_pos,
format!(
"{} declared before {}",
capitalize(&self.current_unit_id().describe()),
primary.describe(),
),
));
}
}
}
fn lookup_entity_for_configuration(
&self,
scope: &Scope<'a>,
config: &mut ConfigurationDeclaration,
) -> AnalysisResult<DesignEnt> {
let ent_name = &mut config.entity_name;
match ent_name.item {
SelectedName::Designator(ref mut designator) => self.lookup_in_library(
self.work_library_name(),
&ent_name.pos,
&designator.item,
&mut designator.reference,
),
SelectedName::Selected(ref mut prefix, ref mut designator) => {
self.resolve_selected_name(scope, prefix)?
.into_non_overloaded()
.map_err(|ent|
AnalysisError::not_fatal_error(
&prefix.pos,
format!("{} does not denote a library", ent.first().describe()),
)
)
.and_then(|library_ent| match library_ent.kind() {
AnyEntKind::Library => {
let library_name = library_ent.designator().expect_identifier();
if library_name != self.work_library_name() {
Err(AnalysisError::not_fatal_error(
&prefix.pos,
format!("Configuration must be within the same library '{}' as the corresponding entity", self.work_library_name()),
))
} else {
let primary_ent = self.lookup_in_library(library_name, &designator.pos, &designator.item.item, &mut designator.item.reference)?;
match primary_ent.kind() {
Design::Entity(..) => Ok(primary_ent),
_ => {
Err(AnalysisError::not_fatal_error(
designator,
format!("{} does not denote an entity", primary_ent.describe()),
))
}
}
}
}
_ => {
Err(AnalysisError::not_fatal_error(
&prefix.pos,
format!("{} does not denote a library", library_ent.describe())
))
}
})
}
}
}
fn resolve_context_item_prefix(
&self,
scope: &Scope<'a>,
prefix: &mut WithPos<Name>,
) -> AnalysisResult<EntRef<'a>> {
match self.resolve_context_item_name(scope, prefix)? {
UsedNames::Single(visible) => visible.into_non_overloaded().map_err(|_| {
AnalysisError::not_fatal_error(&prefix, "Invalid prefix of a selected name")
}),
UsedNames::AllWithin(..) => Err(AnalysisError::not_fatal_error(
&prefix,
"'.all' may not be the prefix of a selected name",
)),
}
}
fn resolve_context_item_name(
&self,
scope: &Scope<'a>,
name: &mut WithPos<Name>,
) -> AnalysisResult<UsedNames<'a>> {
match &mut name.item {
Name::Selected(ref mut prefix, ref mut suffix) => {
let prefix_ent = self.resolve_context_item_prefix(scope, prefix)?;
let visible = self.lookup_selected(&prefix.pos, prefix_ent, suffix)?;
suffix.set_reference(&visible);
Ok(UsedNames::Single(visible))
}
Name::SelectedAll(prefix) => {
let prefix_ent = self.resolve_context_item_prefix(scope, prefix)?;
Ok(UsedNames::AllWithin(prefix.pos.clone(), prefix_ent))
}
Name::Designator(designator) => {
let visible = scope.lookup(&name.pos, designator.designator())?;
designator.set_reference(&visible);
Ok(UsedNames::Single(visible))
}
Name::Slice(..)
| Name::Attribute(..)
| Name::CallOrIndexed(..)
| Name::External(..) => Err(AnalysisError::not_fatal_error(
&name.pos,
"Invalid selected name",
)),
}
}
fn analyze_context_clause(
&self,
scope: &Scope<'a>,
context_clause: &mut [WithPos<ContextItem>],
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
for context_item in context_clause.iter_mut() {
match context_item.item {
ContextItem::Library(LibraryClause { ref mut name_list }) => {
for library_name in name_list.iter_mut() {
if self.work_sym == library_name.item.item {
diagnostics.push(Diagnostic::hint(
&library_name.item,
"Library clause not necessary for current working library",
))
} else if let Some(library) = self.get_library(&library_name.item.item) {
library_name.set_unique_reference(library);
scope.make_potentially_visible(Some(&library_name.item.pos), library);
} else {
diagnostics.push(Diagnostic::error(
&library_name.item,
format!("No such library '{}'", library_name.item),
));
}
}
}
ContextItem::Use(ref mut use_clause) => {
self.analyze_use_clause(scope, use_clause, diagnostics)?;
}
ContextItem::Context(ContextReference { ref mut name_list }) => {
for name in name_list.iter_mut() {
match name.item {
Name::Selected(..) => {}
_ => {
diagnostics.push(Diagnostic::error(
&name.pos,
"Context reference must be a selected name",
));
continue;
}
}
match self.resolve_context_item_name(scope, name) {
Ok(UsedNames::Single(visible)) => {
let ent = visible.first();
match ent.kind() {
AnyEntKind::Design(Design::Context(ref context_region)) => {
scope.add_context_visibility(
Some(&name.pos),
context_region,
);
}
_ => {
if let Name::Selected(_, ref suffix) = name.item {
diagnostics.push(Diagnostic::error(
suffix,
format!(
"{} does not denote a context declaration",
ent.describe()
),
));
}
}
}
}
Ok(UsedNames::AllWithin(..)) => {
}
Err(err) => {
err.add_to(diagnostics)?;
}
}
}
}
}
}
Ok(())
}
pub fn analyze_use_clause(
&self,
scope: &Scope<'a>,
use_clause: &mut UseClause,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
for name in use_clause.name_list.iter_mut() {
match name.item {
Name::Selected(..) => {}
Name::SelectedAll(..) => {}
_ => {
diagnostics.push(Diagnostic::error(
&name.pos,
"Use clause must be a selected name",
));
continue;
}
}
match self.resolve_context_item_name(scope, name) {
Ok(UsedNames::Single(visible)) => {
visible.make_potentially_visible_in(Some(&name.pos), scope);
}
Ok(UsedNames::AllWithin(visibility_pos, named_entity)) => {
match named_entity.kind() {
AnyEntKind::Library => {
let library_name = named_entity.designator().expect_identifier();
self.use_all_in_library(&name.pos, library_name, scope)?;
}
AnyEntKind::Design(design) => match design {
Design::UninstPackage(..) => {
diagnostics.push(Diagnostic::invalid_selected_name_prefix(
named_entity,
&visibility_pos,
));
}
Design::Package(_, ref primary_region)
| Design::PackageInstance(ref primary_region) => {
scope.make_all_potentially_visible(Some(&name.pos), primary_region);
}
_ => {
diagnostics
.error(visibility_pos, "Invalid prefix for selected name");
}
},
_ => {
diagnostics.error(visibility_pos, "Invalid prefix for selected name");
}
}
}
Err(err) => {
err.add_to(diagnostics)?;
}
}
}
Ok(())
}
pub fn analyze_package_instance_name(
&self,
scope: &Scope<'a>,
package_name: &mut WithPos<SelectedName>,
) -> AnalysisResult<&'a Region<'a>> {
let decl = self.resolve_selected_name(scope, package_name)?;
if let AnyEntKind::Design(Design::UninstPackage(_, ref package_region)) = decl.first_kind()
{
Ok(package_region)
} else {
Err(AnalysisError::not_fatal_error(
&package_name.pos,
format!("'{package_name}' is not an uninstantiated generic package"),
))
}
}
}
pub enum UsedNames<'a> {
Single(NamedEntities<'a>),
AllWithin(SrcPos, EntRef<'a>),
}