use super::formal_region::FormalRegion;
use super::formal_region::GpkgInterfaceEnt;
use super::formal_region::GpkgRegion;
use super::formal_region::InterfaceEnt;
use super::named_entity::*;
use super::{named_entity, visibility::*};
use crate::ast::*;
use crate::data::*;
use fnv::FnvHashMap;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::rc::Rc;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OverloadedName<'a> {
entities: FnvHashMap<SignatureKey, OverloadedEnt<'a>>,
}
impl<'a> OverloadedName<'a> {
pub fn new(entities: Vec<OverloadedEnt>) -> OverloadedName {
debug_assert!(!entities.is_empty());
let mut map = FnvHashMap::default();
for ent in entities.into_iter() {
map.insert(ent.signature().key(), ent);
}
OverloadedName { entities: map }
}
pub fn single(ent: OverloadedEnt) -> OverloadedName {
let mut map = FnvHashMap::default();
map.insert(ent.signature().key(), ent);
OverloadedName { entities: map }
}
pub fn first(&self) -> OverloadedEnt<'a> {
let first_key = self.entities.keys().next().unwrap();
*self.entities.get(first_key).unwrap()
}
pub fn designator(&self) -> &Designator {
self.first().designator()
}
pub fn len(&self) -> usize {
self.entities.len()
}
pub fn entities(&self) -> impl Iterator<Item = OverloadedEnt<'a>> + '_ {
self.entities.values().cloned()
}
pub fn sorted_entities(&self) -> Vec<OverloadedEnt<'a>> {
let mut res: Vec<_> = self.entities.values().cloned().collect();
res.sort_by(|x, y| x.decl_pos().cmp(&y.decl_pos()));
res
}
pub fn signatures(&self) -> impl Iterator<Item = &named_entity::Signature<'a>> + '_ {
self.entities().map(|ent| ent.signature())
}
pub fn get(&self, key: &SignatureKey) -> Option<OverloadedEnt<'a>> {
self.entities.get(key).cloned()
}
#[allow(clippy::if_same_then_else)]
fn insert(&mut self, ent: OverloadedEnt<'a>) -> Result<(), Diagnostic> {
match self.entities.entry(ent.signature().key()) {
Entry::Occupied(mut entry) => {
let old_ent = entry.get();
if (old_ent.is_implicit() && ent.is_explicit())
|| (old_ent.is_subprogram_decl() && ent.is_subprogram())
{
entry.insert(ent);
return Ok(());
} else if old_ent.is_implicit()
&& ent.is_implicit()
&& (old_ent.as_actual().id() == ent.as_actual().id())
{
return Ok(());
} else if old_ent.is_explicit() && ent.is_implicit() {
return Ok(());
}
let pos = ent.decl_pos().unwrap();
let mut diagnostic = Diagnostic::error(
pos,
format!(
"Duplicate declaration of '{}' with signature {}",
ent.designator(),
ent.signature().describe()
),
);
if let Some(old_pos) = old_ent.decl_pos() {
diagnostic.add_related(old_pos, "Previously defined here");
}
Err(diagnostic)
}
Entry::Vacant(entry) => {
entry.insert(ent);
Ok(())
}
}
}
fn with_visible(mut self, visible: Self) -> Self {
for (signature, visible_entity) in visible.entities.into_iter() {
if let Entry::Vacant(entry) = self.entities.entry(signature) {
entry.insert(visible_entity);
}
}
self
}
}
#[derive(Clone, Debug)]
pub enum NamedEntities<'a> {
Single(EntRef<'a>),
Overloaded(OverloadedName<'a>),
}
impl<'a> NamedEntities<'a> {
pub fn new(ent: EntRef<'a>) -> NamedEntities<'a> {
match OverloadedEnt::from_any(ent) {
Ok(ent) => Self::Overloaded(OverloadedName::new(vec![ent])),
Err(ent) => Self::Single(ent),
}
}
pub fn new_overloaded(named_entities: Vec<OverloadedEnt<'a>>) -> NamedEntities<'a> {
Self::Overloaded(OverloadedName::new(named_entities))
}
pub fn into_non_overloaded(self) -> Result<EntRef<'a>, OverloadedName<'a>> {
match self {
Self::Single(ent) => Ok(ent),
Self::Overloaded(ent_vec) => Err(ent_vec),
}
}
pub fn expect_non_overloaded(
self,
pos: &SrcPos,
message: impl FnOnce() -> String,
) -> Result<EntRef<'a>, Diagnostic> {
match self {
Self::Single(ent) => Ok(ent),
Self::Overloaded(overloaded) => {
let mut error = Diagnostic::error(pos, message());
for ent in overloaded.entities() {
if let Some(decl_pos) = ent.decl_pos() {
error.add_related(decl_pos, "Defined here");
}
}
Err(error)
}
}
}
pub fn as_non_overloaded(&self) -> Option<EntRef<'a>> {
match self {
Self::Single(ent) => Some(ent),
Self::Overloaded(..) => None,
}
}
pub fn designator(&self) -> &Designator {
self.first().designator()
}
pub fn first(&self) -> EntRef<'a> {
match self {
Self::Single(ent) => ent,
Self::Overloaded(overloaded) => overloaded.first().into(),
}
}
pub fn first_kind(&self) -> &'a AnyEntKind<'a> {
self.first().kind()
}
pub fn make_potentially_visible_in(&self, visible_pos: Option<&SrcPos>, scope: &Scope<'a>) {
match self {
Self::Single(ent) => {
scope.make_potentially_visible(visible_pos, ent);
}
Self::Overloaded(overloaded) => {
for ent in overloaded.entities() {
scope.make_potentially_visible(visible_pos, ent.into());
}
}
}
}
}
#[derive(Copy, Clone, PartialEq)]
pub(crate) enum RegionKind {
PackageDeclaration,
PackageBody,
Other,
}
impl Default for RegionKind {
fn default() -> RegionKind {
RegionKind::Other
}
}
#[derive(Default, Clone)]
pub struct Scope<'a>(Rc<RefCell<ScopeInner<'a>>>);
#[derive(Default)]
struct ScopeInner<'a> {
parent: Option<Scope<'a>>,
region: Region<'a>,
cache: FnvHashMap<Designator, NamedEntities<'a>>,
}
impl<'a> ScopeInner<'a> {
pub fn into_region(self) -> Region<'a> {
self.region
}
pub fn into_visibility(self) -> Visibility<'a> {
self.region.visibility
}
pub fn close(&self, diagnostics: &mut dyn DiagnosticHandler) {
self.region.close(diagnostics)
}
pub fn add(&mut self, ent: EntRef<'a>, diagnostics: &mut dyn DiagnosticHandler) {
self.cache.remove(&ent.designator);
self.region.add(ent, diagnostics)
}
fn make_potentially_visible(
&mut self,
visible_pos: Option<&SrcPos>,
designator: Designator,
ent: EntRef<'a>,
) {
self.cache.remove(&ent.designator);
self.region
.visibility
.make_potentially_visible_with_name(visible_pos, designator, ent);
}
pub fn make_all_potentially_visible(
&mut self,
visible_pos: Option<&SrcPos>,
region: &'a Region<'a>,
) {
self.cache.clear();
self.region
.visibility
.make_all_potentially_visible(visible_pos, region);
}
pub fn add_context_visibility(&mut self, visible_pos: Option<&SrcPos>, region: &Region<'a>) {
self.cache.clear();
self.region
.visibility
.add_context_visibility(visible_pos, ®ion.visibility);
}
pub fn lookup_immediate(&self, designator: &Designator) -> Option<&NamedEntities<'a>> {
self.region.lookup_immediate(designator)
}
fn lookup_enclosing(&self, designator: &Designator) -> Option<NamedEntities<'a>> {
match self.lookup_immediate(designator).cloned() {
Some(NamedEntities::Single(single)) => Some(NamedEntities::Single(single)),
Some(NamedEntities::Overloaded(immediate)) => {
if let Some(NamedEntities::Overloaded(enclosing)) = self
.parent
.as_ref()
.and_then(|region| region.0.borrow().lookup_enclosing(designator))
{
Some(NamedEntities::Overloaded(immediate.with_visible(enclosing)))
} else {
Some(NamedEntities::Overloaded(immediate))
}
}
None => self
.parent
.as_ref()
.and_then(|region| region.0.borrow().lookup_enclosing(designator)),
}
}
fn lookup_visiblity_into(&self, designator: &Designator, visible: &mut Visible<'a>) {
self.region.visibility.lookup_into(designator, visible);
if let Some(ref parent) = self.parent {
parent.0.borrow().lookup_visiblity_into(designator, visible);
}
}
fn lookup_visible(
&self,
pos: &SrcPos,
designator: &Designator,
) -> Result<Option<NamedEntities<'a>>, Diagnostic> {
let mut visible = Visible::default();
self.lookup_visiblity_into(designator, &mut visible);
visible.into_unambiguous(pos, designator)
}
fn lookup_uncached(
&self,
pos: &SrcPos,
designator: &Designator,
) -> Result<NamedEntities<'a>, Diagnostic> {
let result = if let Some(enclosing) = self.lookup_enclosing(designator) {
match enclosing {
NamedEntities::Single(..) => Some(enclosing),
NamedEntities::Overloaded(enclosing_overloaded) => {
if let Ok(Some(NamedEntities::Overloaded(overloaded))) =
self.lookup_visible(pos, designator)
{
Some(NamedEntities::Overloaded(
enclosing_overloaded.with_visible(overloaded),
))
} else {
Some(NamedEntities::Overloaded(enclosing_overloaded))
}
}
}
} else {
self.lookup_visible(pos, designator)?
};
match result {
Some(visible) => Ok(visible),
None => Err(Diagnostic::error(
pos,
match designator {
Designator::Identifier(ident) => {
format!("No declaration of '{ident}'")
}
Designator::OperatorSymbol(operator) => {
format!("No declaration of operator '{operator}'")
}
Designator::Character(chr) => {
format!("No declaration of '{chr}'")
}
},
)),
}
}
fn lookup(
&mut self,
pos: &SrcPos,
designator: &Designator,
) -> Result<NamedEntities<'a>, Diagnostic> {
if let Some(res) = self.cache.get(designator) {
return Ok(res.clone());
}
let ents = self.lookup_uncached(pos, designator)?;
if let Entry::Vacant(vacant) = self.cache.entry(designator.clone()) {
Ok(vacant.insert(ents).clone())
} else {
unreachable!("Cache miss cannot be followed by occupied entry")
}
}
}
impl<'a> Scope<'a> {
pub fn new(region: Region<'a>) -> Scope<'a> {
Self(Rc::new(RefCell::new(ScopeInner {
parent: None,
region,
cache: Default::default(),
})))
}
pub fn nested(&self) -> Scope<'a> {
Self(Rc::new(RefCell::new(ScopeInner {
region: Region::default(),
parent: Some(self.clone()),
cache: self.0.borrow().cache.clone(),
})))
}
pub fn with_parent(self, scope: &Scope<'a>) -> Scope<'a> {
Self(Rc::new(RefCell::new(ScopeInner {
parent: Some(scope.clone()),
region: self.into_inner().region,
cache: Default::default(),
})))
}
pub fn extend(region: &Region<'a>, parent: Option<&Scope<'a>>) -> Scope<'a> {
let kind = match region.kind {
RegionKind::PackageDeclaration => RegionKind::PackageBody,
_ => RegionKind::Other,
};
let extended_region = Region {
visibility: region.visibility.clone(),
entities: region.entities.clone(),
kind,
};
if let Some(parent) = parent {
Scope::new(extended_region).with_parent(parent)
} else {
Scope::new(extended_region)
}
}
pub fn in_package_declaration(self) -> Scope<'a> {
let inner = self.into_inner();
Self(Rc::new(RefCell::new(ScopeInner {
parent: inner.parent,
region: inner.region.in_package_declaration(),
cache: inner.cache,
})))
}
pub fn add(&self, ent: EntRef<'a>, diagnostics: &mut dyn DiagnosticHandler) {
self.0.as_ref().borrow_mut().add(ent, diagnostics);
}
pub fn make_potentially_visible(&self, visible_pos: Option<&SrcPos>, ent: &'a AnyEnt) {
self.0.as_ref().borrow_mut().make_potentially_visible(
visible_pos,
ent.designator().clone(),
ent,
);
}
pub fn make_potentially_visible_with_name(
&self,
visible_pos: Option<&SrcPos>,
designator: Designator,
ent: EntRef<'a>,
) {
self.0
.as_ref()
.borrow_mut()
.make_potentially_visible(visible_pos, designator, ent);
}
pub fn make_all_potentially_visible(
&self,
visible_pos: Option<&SrcPos>,
region: &'a Region<'a>,
) {
self.0
.as_ref()
.borrow_mut()
.make_all_potentially_visible(visible_pos, region);
}
pub fn close(&self, diagnostics: &mut dyn DiagnosticHandler) {
self.0.as_ref().borrow().close(diagnostics)
}
fn into_inner(self) -> ScopeInner<'a> {
if let Ok(cell) = Rc::try_unwrap(self.0) {
cell.into_inner()
} else {
panic!("Expect no child regions");
}
}
pub fn into_region(self) -> Region<'a> {
self.into_inner().into_region()
}
pub fn into_visibility(self) -> Visibility<'a> {
self.into_inner().into_visibility()
}
pub fn lookup_immediate(&self, designator: &Designator) -> Option<NamedEntities<'a>> {
let inner = self.0.as_ref().borrow();
let names = inner.lookup_immediate(designator)?;
Some(names.clone())
}
pub fn lookup(
&self,
pos: &SrcPos,
designator: &Designator,
) -> Result<NamedEntities<'a>, Diagnostic> {
self.0.as_ref().borrow_mut().lookup(pos, designator)
}
pub fn add_context_visibility(&self, visible_pos: Option<&SrcPos>, region: &Region<'a>) {
self.0
.as_ref()
.borrow_mut()
.add_context_visibility(visible_pos, region)
}
}
#[derive(Clone)]
pub struct Region<'a> {
visibility: Visibility<'a>,
pub(crate) entities: FnvHashMap<Designator, NamedEntities<'a>>,
pub(crate) kind: RegionKind,
}
impl<'a> Default for Region<'a> {
fn default() -> Region<'a> {
Region {
visibility: Visibility::default(),
entities: FnvHashMap::default(),
kind: RegionKind::Other,
}
}
}
impl<'a> Region<'a> {
pub fn with_visibility(visibility: Visibility<'a>) -> Self {
Self {
visibility,
..Default::default()
}
}
fn in_package_declaration(mut self) -> Region<'a> {
self.kind = RegionKind::PackageDeclaration;
self
}
pub fn to_entity_formal(&self) -> (FormalRegion, FormalRegion) {
let mut generics = Vec::with_capacity(self.entities.len());
let mut ports = Vec::with_capacity(self.entities.len());
for ent in self.entities.values() {
if let NamedEntities::Single(ent) = ent {
if let Some(ent) = InterfaceEnt::from_any(ent) {
if ent.is_signal() {
ports.push(ent);
} else {
generics.push(ent);
}
}
}
}
generics.sort_by_key(|ent| ent.decl_pos().map(|pos| pos.range().start));
ports.sort_by_key(|ent| ent.decl_pos().map(|pos| pos.range().start));
(
FormalRegion::new_with(InterfaceListType::Generic, generics),
FormalRegion::new_with(InterfaceListType::Port, ports),
)
}
pub fn to_package_generic(&self) -> (GpkgRegion<'a>, Vec<EntRef<'a>>) {
let mut generics = Vec::with_capacity(self.entities.len());
let mut other = Vec::with_capacity(self.entities.len());
for ent in self.entities.values() {
match ent {
NamedEntities::Single(ent) => {
if let Some(ent) = GpkgInterfaceEnt::from_any(ent) {
generics.push(ent);
continue;
}
other.push(*ent);
}
NamedEntities::Overloaded(overloaded) => {
if overloaded.len() == 1 {
if let Some(ent) = GpkgInterfaceEnt::from_any(overloaded.first().into()) {
generics.push(ent);
continue;
}
}
other.extend(overloaded.entities().map(EntRef::from));
}
}
}
generics.sort_by_key(|ent| ent.decl_pos().map(|pos| pos.range().start));
other.sort_by_key(|ent| ent.decl_pos().map(|pos| pos.range().start));
(GpkgRegion::new(generics), other)
}
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 AnyEntKind::DeferredConstant(..) = ent.first_kind() {
ent.first().error(diagnostics, format!("Deferred constant '{}' lacks corresponding full constant declaration in package body", ent.designator()));
}
}
}
RegionKind::Other => {}
}
}
fn check_protected_types_have_body(&self, diagnostics: &mut dyn DiagnosticHandler) {
for ent in self.entities.values() {
if let AnyEntKind::Type(Type::Protected(_, body_pos)) = ent.first_kind() {
if body_pos.load().is_none() {
ent.first().error(
diagnostics,
format!("Missing body for protected type '{}'", ent.designator()),
);
}
}
}
}
pub fn close(&self, diagnostics: &mut dyn DiagnosticHandler) {
self.check_deferred_constant_pairs(diagnostics);
self.check_protected_types_have_body(diagnostics);
}
pub fn add(&mut self, ent: EntRef<'a>, 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.id() == ent.id() {
*prev_ent = ent;
} else 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) => {
match OverloadedEnt::from_any(ent) {
Ok(ent) => {
if let Err(err) = overloaded.insert(ent) {
diagnostics.push(err);
}
}
Err(ent) => {
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 lookup_immediate(&self, designator: &Designator) -> Option<&NamedEntities<'a>> {
self.entities.get(designator)
}
pub fn immediates(&self) -> impl Iterator<Item = &NamedEntities<'a>> {
self.entities.values()
}
}
pub trait SetReference {
fn set_unique_reference(&mut self, ent: &AnyEnt);
fn set_reference<'a>(&mut self, value: &'a impl AsUnique<'a>) {
if let Some(ent) = value.as_unique() {
self.set_unique_reference(ent);
}
}
}
pub trait AsUnique<'a> {
fn as_unique(&self) -> Option<EntRef<'a>>;
}
impl<'a> AsUnique<'a> for OverloadedName<'a> {
fn as_unique(&self) -> Option<EntRef<'a>> {
if self.entities.len() == 1 {
self.entities.values().next().map(|ent| (*ent).into())
} else {
None
}
}
}
impl<'a> AsUnique<'a> for NamedEntities<'a> {
fn as_unique(&self) -> Option<EntRef<'a>> {
match self {
NamedEntities::Single(ent) => Some(ent),
NamedEntities::Overloaded(overloaded) => overloaded.as_unique(),
}
}
}
impl<T> SetReference for WithRef<T> {
fn set_unique_reference(&mut self, ent: &AnyEnt) {
self.reference.set_unique_reference(ent);
}
}
impl<T: SetReference> SetReference for WithPos<T> {
fn set_unique_reference(&mut self, ent: &AnyEnt) {
self.item.set_unique_reference(ent);
}
}
impl SetReference for Reference {
fn set_unique_reference(&mut self, ent: &AnyEnt) {
*self = Some(ent.id());
}
}
impl SetReference for Name {
fn set_unique_reference(&mut self, ent: &AnyEnt) {
if let Some(r) = self.suffix_reference_mut() {
r.set_unique_reference(ent);
}
}
}
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
}