pub use super::named_entity::*;
use super::visibility::*;
use crate::ast::*;
use crate::data::*;
use fnv::FnvHashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
#[derive(Clone)]
pub struct OverloadedName {
entities: Vec<Arc<NamedEntity>>,
}
impl OverloadedName {
pub fn new(entities: Vec<Arc<NamedEntity>>) -> OverloadedName {
debug_assert!(!entities.is_empty());
debug_assert!(
entities.iter().all(|ent| ent.is_overloaded()),
"All must be overloaded"
);
OverloadedName { entities }
}
pub fn first(&self) -> &Arc<NamedEntity> {
self.entities.first().unwrap()
}
pub fn entities(&self) -> impl Iterator<Item = &Arc<NamedEntity>> {
self.entities.iter()
}
fn push(&mut self, ent: Arc<NamedEntity>) {
self.entities.push(ent);
}
}
#[derive(Clone)]
pub enum NamedEntities {
Single(Arc<NamedEntity>),
Overloaded(OverloadedName),
}
impl NamedEntities {
pub fn new(named_entity: Arc<NamedEntity>) -> NamedEntities {
if named_entity.is_overloaded() {
Self::Overloaded(OverloadedName::new(vec![named_entity]))
} else {
Self::Single(named_entity)
}
}
pub fn new_overloaded(named_entities: Vec<Arc<NamedEntity>>) -> NamedEntities {
Self::Overloaded(OverloadedName::new(named_entities))
}
pub fn into_non_overloaded(self) -> Result<Arc<NamedEntity>, OverloadedName> {
match self {
Self::Single(ent) => Ok(ent),
Self::Overloaded(ent_vec) => Err(ent_vec),
}
}
pub fn designator(&self) -> &Designator {
self.first().designator()
}
pub fn first(&self) -> &Arc<NamedEntity> {
match self {
Self::Single(ent) => ent,
Self::Overloaded(overloaded) => overloaded.first(),
}
}
pub fn first_kind(&self) -> &NamedEntityKind {
self.first().kind()
}
pub fn make_potentially_visible_in(
&self,
visible_pos: Option<&SrcPos>,
region: &mut Region<'_>,
) {
match self {
Self::Single(ent) => {
region.make_potentially_visible(visible_pos, ent.clone());
}
Self::Overloaded(overloaded) => {
for ent in overloaded.entities.iter() {
region.make_potentially_visible(visible_pos, ent.clone());
}
}
}
}
}
#[derive(Copy, Clone, PartialEq)]
enum RegionKind {
PackageDeclaration,
PackageBody,
Other,
}
impl Default for RegionKind {
fn default() -> RegionKind {
RegionKind::Other
}
}
#[derive(Clone, Default)]
pub struct Region<'a> {
parent: Option<&'a Region<'a>>,
visibility: Visibility,
entities: FnvHashMap<Designator, NamedEntities>,
protected_bodies: FnvHashMap<Symbol, SrcPos>,
kind: RegionKind,
}
impl<'a> Region<'a> {
pub fn default() -> Region<'static> {
Region {
parent: None,
visibility: Visibility::default(),
entities: FnvHashMap::default(),
protected_bodies: FnvHashMap::default(),
kind: RegionKind::Other,
}
}
pub fn nested(&'a self) -> Region<'a> {
Self::with_parent(self)
}
pub fn with_parent(parent: &'a Region<'a>) -> Region<'a> {
Region {
parent: Some(parent),
..Region::default()
}
}
pub fn without_parent(self) -> Region<'static> {
Region {
parent: None,
visibility: self.visibility,
entities: self.entities,
protected_bodies: self.protected_bodies,
kind: self.kind,
}
}
pub fn in_package_declaration(mut self) -> Region<'a> {
self.kind = RegionKind::PackageDeclaration;
self
}
pub fn extend(region: &'a Region<'a>, parent: Option<&'a Region<'a>>) -> Region<'a> {
let kind = match region.kind {
RegionKind::PackageDeclaration => RegionKind::PackageBody,
_ => RegionKind::Other,
};
debug_assert!(
region.parent.is_none(),
"Parent of extended region must be the same as the parent"
);
Region {
parent,
visibility: region.visibility.clone(),
entities: region.entities.clone(),
protected_bodies: region.protected_bodies.clone(),
kind,
}
}
fn check_deferred_constant_pairs(&self, diagnostics: &mut dyn DiagnosticHandler) {
match self.kind {
RegionKind::PackageDeclaration | RegionKind::PackageBody => {
for ent in self.entities.values() {
if let NamedEntityKind::DeferredConstant = ent.first_kind() {
ent.first().error(diagnostics, format!("Deferred constant '{}' lacks corresponding full constant declaration in package body", ent.designator()));
}
}
}
RegionKind::Other => {}
}
}
fn get_protected_body(&self, name: &Symbol) -> Option<&SrcPos> {
self.protected_bodies.get(name)
}
fn has_protected_body(&self, name: &Symbol) -> bool {
self.get_protected_body(name).is_some()
}
fn check_protected_types_have_body(&self, diagnostics: &mut dyn DiagnosticHandler) {
for ent in self.entities.values() {
if ent.first_kind().is_protected_type()
&& !self.has_protected_body(ent.designator().expect_identifier())
{
ent.first().error(
diagnostics,
format!("Missing body for protected type '{}'", ent.designator()),
);
}
}
}
pub fn close(&mut self, diagnostics: &mut dyn DiagnosticHandler) {
self.check_deferred_constant_pairs(diagnostics);
self.check_protected_types_have_body(diagnostics);
}
pub fn add_protected_body(&mut self, ident: Ident, diagnostics: &mut dyn DiagnosticHandler) {
if let Some(prev_pos) = self.get_protected_body(&ident.item) {
diagnostics.push(duplicate_error(&ident.item, &ident.pos, Some(prev_pos)));
} else {
self.protected_bodies.insert(ident.item, ident.pos);
}
}
pub fn add_named_entity(
&mut self,
ent: Arc<NamedEntity>,
diagnostics: &mut dyn DiagnosticHandler,
) {
if ent.kind().is_deferred_constant() && self.kind != RegionKind::PackageDeclaration {
ent.error(
diagnostics,
"Deferred constants are only allowed in package declarations (not body)",
);
return;
};
match self.entities.entry(ent.designator().clone()) {
Entry::Occupied(ref mut entry) => {
let prev_ents = entry.get_mut();
match prev_ents {
NamedEntities::Single(ref mut prev_ent) => {
if prev_ent.kind().is_deferred_constant()
&& ent.kind().is_non_deferred_constant()
{
if self.kind == RegionKind::PackageBody {
*prev_ent = ent;
} else {
ent.error(
diagnostics,
"Full declaration of deferred constant is only allowed in a package body",
);
}
} else if let Some(pos) = ent.decl_pos() {
diagnostics.push(duplicate_error(
prev_ent.designator(),
pos,
prev_ent.decl_pos(),
));
}
}
NamedEntities::Overloaded(ref mut overloaded) => {
if ent.is_overloaded() {
overloaded.push(ent);
} else if let Some(pos) = ent.decl_pos() {
diagnostics.push(duplicate_error(
overloaded.first().designator(),
pos,
overloaded.first().decl_pos(),
));
}
}
}
}
Entry::Vacant(entry) => {
entry.insert(NamedEntities::new(ent));
}
}
}
pub fn add(
&mut self,
designator: impl Into<WithPos<Designator>>,
kind: NamedEntityKind,
diagnostics: &mut dyn DiagnosticHandler,
) {
let designator = designator.into();
self.add_named_entity(
Arc::new(NamedEntity::new(
designator.item,
kind,
Some(&designator.pos),
)),
diagnostics,
);
}
pub fn overwrite(&mut self, ent: NamedEntity) {
let ent = NamedEntities::new(Arc::new(ent));
self.entities.insert(ent.designator().clone(), ent);
}
pub fn add_implicit_declaration_aliases(
&mut self,
decl_pos: Option<&SrcPos>,
ent: &NamedEntity,
diagnostics: &mut dyn DiagnosticHandler,
) {
if let NamedEntityKind::TypeDeclaration(ref implicit) = ent.actual_kind() {
for entity in implicit.iter() {
let entity = NamedEntity::new(
entity.designator().clone(),
NamedEntityKind::AliasOf(entity.clone()),
decl_pos,
);
self.add_named_entity(Arc::new(entity), diagnostics);
}
}
}
pub fn make_potentially_visible(
&mut self,
visible_pos: Option<&SrcPos>,
ent: Arc<NamedEntity>,
) {
self.visibility.make_potentially_visible_with_name(
visible_pos,
ent.designator().clone(),
ent,
);
}
pub fn make_potentially_visible_with_name(
&mut self,
visible_pos: Option<&SrcPos>,
designator: Designator,
ent: Arc<NamedEntity>,
) {
self.visibility
.make_potentially_visible_with_name(visible_pos, designator, ent);
}
pub fn make_all_potentially_visible(
&mut self,
visible_pos: Option<&SrcPos>,
region: &Arc<Region<'static>>,
) {
self.visibility
.make_all_potentially_visible(visible_pos, region);
}
pub fn add_context_visibility(&mut self, visible_pos: Option<&SrcPos>, region: &Region<'a>) {
self.visibility
.add_context_visibility(visible_pos, ®ion.visibility);
}
pub fn lookup_immediate(&self, designator: &Designator) -> Option<&NamedEntities> {
self.entities.get(designator)
}
fn lookup_enclosing(&self, designator: &Designator) -> Option<&NamedEntities> {
self.lookup_immediate(designator).or_else(|| {
self.parent
.as_ref()
.and_then(|region| region.lookup_enclosing(designator))
})
}
fn lookup_visiblity_into(&'a self, designator: &Designator, visible: &mut Visible<'a>) {
self.visibility.lookup_into(designator, visible);
if let Some(parent) = self.parent {
parent.lookup_visiblity_into(designator, visible);
}
}
fn lookup_visible(
&self,
pos: &SrcPos,
designator: &Designator,
) -> Result<Option<NamedEntities>, Diagnostic> {
let mut visible = Visible::default();
self.lookup_visiblity_into(designator, &mut visible);
visible.into_unambiguous(pos, designator)
}
pub fn lookup_selected(&self, designator: &Designator) -> Option<&NamedEntities> {
self.lookup_immediate(designator)
}
pub fn lookup_within(
&self,
pos: &SrcPos,
designator: &Designator,
) -> Result<NamedEntities, Diagnostic> {
let result = if let Some(visible) = self.lookup_enclosing(designator) {
Some(visible.clone())
} else {
self.lookup_visible(pos, designator)?
};
match result {
Some(visible) => Ok(visible),
None => Err(Diagnostic::error(
pos,
format!("No declaration of '{}'", designator),
)),
}
}
}
pub trait SetReference {
fn set_unique_reference(&mut self, ent: &NamedEntity) {
if !ent.is_overloaded() {
self.set_reference_pos(ent.decl_pos());
} else {
self.clear_reference();
}
}
fn set_reference(&mut self, visible: &NamedEntities) {
self.set_unique_reference(visible.first());
}
fn clear_reference(&mut self) {
self.set_reference_pos(None);
}
fn set_reference_pos(&mut self, pos: Option<&SrcPos>);
}
impl<T> SetReference for WithRef<T> {
fn set_reference_pos(&mut self, pos: Option<&SrcPos>) {
self.reference.set_reference_pos(pos);
}
}
impl<T: SetReference> SetReference for WithPos<T> {
fn set_reference_pos(&mut self, pos: Option<&SrcPos>) {
self.item.set_reference_pos(pos);
}
}
impl SetReference for Reference {
fn set_reference_pos(&mut self, pos: Option<&SrcPos>) {
*self = pos.cloned();
}
}
pub(super) fn duplicate_error(
name: &impl std::fmt::Display,
pos: &SrcPos,
prev_pos: Option<&SrcPos>,
) -> Diagnostic {
let mut diagnostic = Diagnostic::error(pos, format!("Duplicate declaration of '{}'", name));
if let Some(prev_pos) = prev_pos {
diagnostic.add_related(prev_pos, "Previously defined here");
}
diagnostic
}