#![allow(clippy::only_used_in_recursion)]
use super::analyze::*;
use super::region::*;
use super::target::AssignmentType;
use crate::ast::Range;
use crate::ast::*;
use crate::data::*;
use std::sync::Arc;
impl<'a> AnalyzeContext<'a> {
pub fn lookup_selected(
&self,
prefix_pos: &SrcPos,
prefix: &NamedEntity,
suffix: &WithPos<WithRef<Designator>>,
) -> AnalysisResult<NamedEntities> {
match prefix.actual_kind() {
NamedEntityKind::Library => {
let library_name = prefix.designator().expect_identifier();
let named_entity =
self.lookup_in_library(library_name, &suffix.pos, suffix.designator())?;
Ok(NamedEntities::new(named_entity))
}
NamedEntityKind::UninstPackage(..) => Err(AnalysisError::NotFatal(
invalid_selected_name_prefix(prefix, prefix_pos),
)),
NamedEntityKind::Object(ref object) => {
self.lookup_type_selected(prefix_pos, object.subtype.type_mark(), suffix)
}
NamedEntityKind::ObjectAlias { ref type_mark, .. } => {
self.lookup_type_selected(prefix_pos, type_mark, suffix)
}
NamedEntityKind::ExternalAlias { ref type_mark, .. } => {
self.lookup_type_selected(prefix_pos, type_mark, suffix)
}
NamedEntityKind::ElementDeclaration(ref subtype) => {
self.lookup_type_selected(prefix_pos, subtype.type_mark(), suffix)
}
NamedEntityKind::Package(ref region)
| NamedEntityKind::PackageInstance(ref region)
| NamedEntityKind::LocalPackageInstance(ref region) => {
if let Some(decl) = region.lookup_selected(suffix.designator()) {
Ok(decl.clone())
} else {
Err(no_declaration_within(prefix, suffix).into())
}
}
_ => Err(invalid_selected_name_prefix(prefix, prefix_pos).into()),
}
}
pub fn lookup_type_selected(
&self,
prefix_pos: &SrcPos,
prefix_type: &TypeEnt,
suffix: &WithPos<WithRef<Designator>>,
) -> AnalysisResult<NamedEntities> {
match prefix_type.flatten_alias().kind() {
Type::Record(ref region) => {
if let Some(decl) = region.lookup_selected(suffix.designator()) {
Ok(decl.clone())
} else {
Err(no_declaration_within(prefix_type, suffix).into())
}
}
Type::Protected(region) => {
if let Some(decl) = region.lookup_selected(suffix.designator()) {
Ok(decl.clone())
} else {
Err(no_declaration_within(prefix_type, suffix).into())
}
}
Type::Incomplete(full_type_ref) => {
if let Some(full_type) = full_type_ref
.load()
.upgrade()
.and_then(|e| TypeEnt::from_any(e).ok())
{
self.lookup_type_selected(prefix_pos, &full_type, suffix)
} else {
Err(Diagnostic::error(
prefix_pos,
"Internal error when referencing full type of incomplete type",
)
.into())
}
}
Type::Subtype(subtype) => {
self.lookup_type_selected(prefix_pos, subtype.type_mark(), suffix)
}
Type::Access(subtype, ..) => {
self.lookup_type_selected(prefix_pos, subtype.type_mark(), suffix)
}
_ => Err(invalid_selected_name_prefix(prefix_type, prefix_pos).into()),
}
}
pub fn resolve_selected_name(
&self,
region: &Region<'_>,
name: &mut WithPos<SelectedName>,
) -> AnalysisResult<NamedEntities> {
match name.item {
SelectedName::Selected(ref mut prefix, ref mut suffix) => {
suffix.clear_reference();
let prefix_ent = self
.resolve_selected_name(region, prefix)?
.into_non_overloaded();
if let Ok(prefix_ent) = prefix_ent {
let visible = self.lookup_selected(&prefix.pos, &prefix_ent, suffix)?;
suffix.set_reference(&visible);
return Ok(visible);
};
Err(AnalysisError::NotFatal(Diagnostic::error(
&prefix.pos,
"Invalid prefix for selected name",
)))
}
SelectedName::Designator(ref mut designator) => {
designator.clear_reference();
let visible = region.lookup_within(&name.pos, designator.designator())?;
designator.set_reference(&visible);
Ok(visible)
}
}
}
pub fn resolve_name(
&self,
region: &Region<'_>,
name_pos: &SrcPos,
name: &mut Name,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult<Option<NamedEntities>> {
match name {
Name::Selected(prefix, suffix) => {
suffix.clear_reference();
match self.resolve_name(region, &prefix.pos, &mut prefix.item, diagnostics)? {
Some(NamedEntities::Single(ref named_entity)) => {
match self.lookup_selected(&prefix.pos, named_entity, suffix) {
Ok(visible) => {
suffix.set_reference(&visible);
Ok(Some(visible))
}
Err(err) => {
err.add_to(diagnostics)?;
Ok(None)
}
}
}
Some(NamedEntities::Overloaded(..)) => Ok(None),
None => Ok(None),
}
}
Name::SelectedAll(prefix) => {
self.resolve_name(region, &prefix.pos, &mut prefix.item, diagnostics)?;
Ok(None)
}
Name::Designator(designator) => {
designator.clear_reference();
match region.lookup_within(name_pos, designator.designator()) {
Ok(visible) => {
designator.set_reference(&visible);
Ok(Some(visible))
}
Err(diagnostic) => {
diagnostics.push(diagnostic);
Ok(None)
}
}
}
Name::Indexed(ref mut prefix, ref mut exprs) => {
self.resolve_name(region, &prefix.pos, &mut prefix.item, diagnostics)?;
for expr in exprs.iter_mut() {
self.analyze_expression(region, expr, diagnostics)?;
}
Ok(None)
}
Name::Slice(ref mut prefix, ref mut drange) => {
self.resolve_name(region, &prefix.pos, &mut prefix.item, diagnostics)?;
self.analyze_discrete_range(region, drange.as_mut(), diagnostics)?;
Ok(None)
}
Name::Attribute(ref mut attr) => {
self.analyze_attribute_name(region, attr, diagnostics)?;
Ok(None)
}
Name::FunctionCall(..) => {
self.analyze_function_call_or_indexed_name(region, name_pos, name, diagnostics)?;
Ok(None)
}
Name::External(ref mut ename) => {
let ExternalName { subtype, .. } = ename.as_mut();
self.analyze_subtype_indication(region, subtype, diagnostics)?;
Ok(None)
}
}
}
pub fn resolve_non_overloaded_with_kind(
&self,
named_entities: NamedEntities,
pos: &SrcPos,
kind_ok: &impl Fn(&NamedEntityKind) -> bool,
expected: &str,
) -> AnalysisResult<Arc<NamedEntity>> {
let ent = self.resolve_non_overloaded(named_entities, pos, expected)?;
if kind_ok(ent.actual_kind()) {
Ok(ent)
} else {
Err(AnalysisError::NotFatal(ent.kind_error(pos, expected)))
}
}
pub fn resolve_non_overloaded(
&self,
named_entities: NamedEntities,
pos: &SrcPos,
expected: &str,
) -> AnalysisResult<Arc<NamedEntity>> {
Ok(named_entities.expect_non_overloaded(pos, || {
format!("Expected {}, got overloaded name", expected)
})?)
}
pub fn resolve_type_mark_name(
&self,
region: &Region<'_>,
type_mark: &mut WithPos<SelectedName>,
) -> AnalysisResult<TypeEnt> {
let entities = self.resolve_selected_name(region, type_mark)?;
let pos = type_mark.suffix_pos();
let expected = "type";
let ent = self.resolve_non_overloaded(entities, pos, expected)?;
TypeEnt::from_any(ent).map_err(|ent| AnalysisError::NotFatal(ent.kind_error(pos, expected)))
}
pub fn resolve_type_mark(
&self,
region: &Region<'_>,
type_mark: &mut WithPos<TypeMark>,
) -> AnalysisResult<TypeEnt> {
if !type_mark.item.subtype {
self.resolve_type_mark_name(region, &mut type_mark.item.name)
} else {
let entities = self.resolve_selected_name(region, &mut type_mark.item.name)?;
let pos = type_mark.item.name.suffix_pos();
let expected = "object or alias";
let named_entity = self.resolve_non_overloaded(entities, pos, expected)?;
match named_entity.kind() {
NamedEntityKind::Object(obj) => Ok(obj.subtype.type_mark().to_owned()),
NamedEntityKind::ObjectAlias { type_mark, .. } => Ok(type_mark.clone()),
NamedEntityKind::ElementDeclaration(subtype) => Ok(subtype.type_mark().to_owned()),
_ => Err(AnalysisError::NotFatal(
named_entity.kind_error(pos, expected),
)),
}
}
}
fn analyze_attribute_name(
&self,
region: &Region<'_>,
attr: &mut AttributeName,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
let AttributeName {
name,
signature,
expr,
..
} = attr;
self.resolve_name(region, &name.pos, &mut name.item, diagnostics)?;
if let Some(ref mut signature) = signature {
if let Err(err) = self.resolve_signature(region, signature) {
err.add_to(diagnostics)?;
}
}
if let Some(ref mut expr) = expr {
self.analyze_expression(region, expr, diagnostics)?;
}
Ok(())
}
pub fn analyze_range(
&self,
region: &Region<'_>,
range: &mut Range,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
match range {
Range::Range(ref mut constraint) => {
self.analyze_expression(region, &mut constraint.left_expr, diagnostics)?;
self.analyze_expression(region, &mut constraint.right_expr, diagnostics)?;
}
Range::Attribute(ref mut attr) => {
self.analyze_attribute_name(region, attr, diagnostics)?
}
}
Ok(())
}
pub fn analyze_discrete_range(
&self,
region: &Region<'_>,
drange: &mut DiscreteRange,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
match drange {
DiscreteRange::Discrete(ref mut type_mark, ref mut range) => {
if let Err(err) = self.resolve_type_mark_name(region, type_mark) {
err.add_to(diagnostics)?;
}
if let Some(ref mut range) = range {
self.analyze_range(region, range, diagnostics)?;
}
}
DiscreteRange::Range(ref mut range) => {
self.analyze_range(region, range, diagnostics)?;
}
}
Ok(())
}
pub fn analyze_choices(
&self,
region: &Region<'_>,
choices: &mut [Choice],
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
for choice in choices.iter_mut() {
match choice {
Choice::Expression(ref mut expr) => {
self.analyze_expression(region, expr, diagnostics)?;
}
Choice::DiscreteRange(ref mut drange) => {
self.analyze_discrete_range(region, drange, diagnostics)?;
}
Choice::Others => {}
}
}
Ok(())
}
pub fn analyze_expression(
&self,
region: &Region<'_>,
expr: &mut WithPos<Expression>,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
self.analyze_expression_pos(region, &expr.pos, &mut expr.item, diagnostics)
}
pub fn analyze_waveform(
&self,
region: &Region<'_>,
wavf: &mut Waveform,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
match wavf {
Waveform::Elements(ref mut elems) => {
for elem in elems.iter_mut() {
let WaveformElement { value, after } = elem;
self.analyze_expression(region, value, diagnostics)?;
if let Some(expr) = after {
self.analyze_expression(region, expr, diagnostics)?;
}
}
}
Waveform::Unaffected => {}
}
Ok(())
}
pub fn analyze_assoc_elems(
&self,
region: &Region<'_>,
elems: &mut [AssociationElement],
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
for AssociationElement { actual, .. } in elems.iter_mut() {
match actual.item {
ActualPart::Expression(ref mut expr) => {
self.analyze_expression_pos(region, &actual.pos, expr, diagnostics)?;
}
ActualPart::Open => {}
}
}
Ok(())
}
pub fn analyze_procedure_call(
&self,
region: &Region<'_>,
fcall: &mut FunctionCall,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
let FunctionCall { name, parameters } = fcall;
if let Some(entities) = self.resolve_name(region, &name.pos, &mut name.item, diagnostics)? {
match entities {
NamedEntities::Single(ent) => {
let mut diagnostic = Diagnostic::error(&name.pos, "Invalid procedure call");
if let Some(decl_pos) = ent.decl_pos() {
diagnostic.add_related(
decl_pos,
format!("{} is not a procedure", ent.describe()),
);
}
diagnostics.push(diagnostic);
self.analyze_assoc_elems(region, parameters, diagnostics)?;
}
NamedEntities::Overloaded(names) => {
let mut found = false;
for ent in names.entities() {
if let Some(sig) = ent.signature() {
if sig.return_type().is_none() {
found = true;
break;
}
}
}
if found {
if let Some(reference) = fcall.name.item.suffix_reference_mut() {
self.resolve_overloaded_with_target_type(
region,
names,
None,
&fcall.name.pos,
reference,
&mut fcall.parameters,
diagnostics,
)?;
} else {
self.analyze_assoc_elems(region, parameters, diagnostics)?;
}
} else {
let mut diagnostic = Diagnostic::error(&name.pos, "Invalid procedure call");
for ent in names.sorted_entities() {
if let Some(decl_pos) = ent.decl_pos() {
diagnostic.add_related(
decl_pos,
format!("{} is not a procedure", ent.describe()),
);
}
}
diagnostics.push(diagnostic);
self.analyze_assoc_elems(region, parameters, diagnostics)?;
}
}
};
} else {
self.analyze_assoc_elems(region, parameters, diagnostics)?;
}
Ok(())
}
pub fn analyze_aggregate(
&self,
region: &Region<'_>,
assocs: &mut [ElementAssociation],
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
for assoc in assocs.iter_mut() {
match assoc {
ElementAssociation::Named(ref mut choices, ref mut expr) => {
for choice in choices.iter_mut() {
match choice {
Choice::Expression(..) => {
}
Choice::DiscreteRange(ref mut drange) => {
self.analyze_discrete_range(region, drange, diagnostics)?;
}
Choice::Others => {}
}
}
self.analyze_expression(region, expr, diagnostics)?;
}
ElementAssociation::Positional(ref mut expr) => {
self.analyze_expression(region, expr, diagnostics)?;
}
}
}
Ok(())
}
fn analyze_qualified_expression(
&self,
region: &Region<'_>,
qexpr: &mut QualifiedExpression,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult<Option<TypeEnt>> {
let QualifiedExpression { type_mark, expr } = qexpr;
match self.resolve_type_mark(region, type_mark) {
Ok(target_type) => {
self.analyze_expression_with_target_type(
region,
&target_type,
&expr.pos,
&mut expr.item,
diagnostics,
)?;
Ok(Some(target_type))
}
Err(e) => {
self.analyze_expression(region, expr, diagnostics)?;
e.add_to(diagnostics)?;
Ok(None)
}
}
}
pub fn analyze_expression_pos(
&self,
region: &Region<'_>,
pos: &SrcPos,
expr: &mut Expression,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
match expr {
Expression::Binary(_, ref mut left, ref mut right) => {
self.analyze_expression(region, left, diagnostics)?;
self.analyze_expression(region, right, diagnostics)
}
Expression::Unary(_, ref mut inner) => {
self.analyze_expression(region, inner, diagnostics)
}
Expression::Name(ref mut name) => {
self.resolve_name(region, pos, name, diagnostics)?;
Ok(())
}
Expression::Aggregate(ref mut assocs) => {
self.analyze_aggregate(region, assocs, diagnostics)
}
Expression::Qualified(ref mut qexpr) => {
self.analyze_qualified_expression(region, qexpr, diagnostics)?;
Ok(())
}
Expression::New(ref mut alloc) => match alloc.item {
Allocator::Qualified(ref mut qexpr) => {
self.analyze_qualified_expression(region, qexpr, diagnostics)?;
Ok(())
}
Allocator::Subtype(ref mut subtype) => {
self.analyze_subtype_indication(region, subtype, diagnostics)
}
},
Expression::Literal(ref mut literal) => match literal {
Literal::Physical(PhysicalLiteral { ref mut unit, .. }) => {
if let Err(diagnostic) = self.resolve_physical_unit(region, unit) {
diagnostics.push(diagnostic);
}
Ok(())
}
_ => Ok(()),
},
}
}
pub fn analyze_indexed_name(
&self,
region: &Region<'_>,
name_pos: &SrcPos,
suffix_pos: &SrcPos,
type_mark: &TypeEnt,
indexes: &mut [WithPos<Expression>],
diagnostics: &mut dyn DiagnosticHandler,
) -> AnalysisResult<TypeEnt> {
let base_type = type_mark.base_type();
let base_type = if let Type::Access(ref subtype, ..) = base_type.kind() {
subtype.base_type()
} else {
base_type
};
if let Type::Array {
indexes: ref index_types,
elem_type,
..
} = base_type.kind()
{
if indexes.len() != index_types.len() {
diagnostics.push(dimension_mismatch(
name_pos,
base_type,
indexes.len(),
index_types.len(),
))
}
for index in indexes.iter_mut() {
self.analyze_expression(region, index, diagnostics)?;
}
Ok(elem_type.clone())
} else {
Err(Diagnostic::error(
suffix_pos,
format!("{} cannot be indexed", type_mark.describe()),
)
.into())
}
}
pub fn analyze_sliced_name(
&self,
suffix_pos: &SrcPos,
type_mark: &TypeEnt,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
let base_type = type_mark.base_type();
let base_type = if let Type::Access(ref subtype, ..) = base_type.kind() {
subtype.base_type()
} else {
base_type
};
if let Type::Array { .. } = base_type.kind() {
} else {
diagnostics.error(
suffix_pos,
format!("{} cannot be sliced", type_mark.describe()),
);
}
Ok(())
}
pub fn analyze_function_call_or_indexed_name(
&self,
region: &Region<'_>,
name_pos: &SrcPos,
name: &mut Name,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
match name {
Name::FunctionCall(ref mut fcall) => {
match self.resolve_name(
region,
&fcall.name.pos,
&mut fcall.name.item,
diagnostics,
)? {
Some(NamedEntities::Single(ent)) => {
if ent.actual_kind().is_type() {
self.analyze_assoc_elems(region, &mut fcall.parameters, diagnostics)?;
} else if let Some((prefix, indexes)) = fcall.to_indexed() {
*name = Name::Indexed(prefix, indexes);
let Name::Indexed(ref mut prefix, ref mut indexes) = name else { unreachable!()};
if let Some(type_mark) = type_mark_of_sliced_or_indexed(&ent) {
if let Err(err) = self.analyze_indexed_name(
region,
name_pos,
prefix.suffix_pos(),
type_mark,
indexes,
diagnostics,
) {
err.add_to(diagnostics)?;
}
} else {
diagnostics.error(
prefix.suffix_pos(),
format!("{} cannot be indexed", ent.describe()),
)
}
} else {
diagnostics.push(Diagnostic::error(
&fcall.name.pos,
format!(
"{} cannot be the prefix of a function call",
ent.describe()
),
));
self.analyze_assoc_elems(region, &mut fcall.parameters, diagnostics)?;
}
}
Some(NamedEntities::Overloaded(..)) => {
self.analyze_assoc_elems(region, &mut fcall.parameters, diagnostics)?;
}
None => {
self.analyze_assoc_elems(region, &mut fcall.parameters, diagnostics)?;
}
};
}
_ => {
debug_assert!(false);
}
}
Ok(())
}
pub fn analyze_name_with_target_type(
&self,
region: &Region<'_>,
target_type: &TypeEnt,
name_pos: &SrcPos,
name: &mut Name,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult<Option<bool>> {
match name {
Name::Designator(designator) => {
designator.clear_reference();
match region.lookup_within(name_pos, designator.designator()) {
Ok(entities) => {
designator.set_reference(&entities);
match entities {
NamedEntities::Single(ent) => {
designator.set_unique_reference(&ent);
let is_correct = ent.match_with_target_type(target_type);
if is_correct == Some(false) {
diagnostics.push(type_mismatch(name_pos, &ent, target_type));
}
Ok(is_correct)
}
NamedEntities::Overloaded(overloaded) => self
.resolve_overloaded_with_target_type(
region,
overloaded,
Some(target_type),
name_pos,
designator,
&mut [],
diagnostics,
),
}
}
Err(diagnostic) => {
diagnostics.push(diagnostic);
Ok(None)
}
}
}
Name::Selected(prefix, designator) => {
designator.clear_reference();
if let Some(NamedEntities::Single(ref named_entity)) =
self.resolve_name(region, &prefix.pos, &mut prefix.item, diagnostics)?
{
match self.lookup_selected(&prefix.pos, named_entity, designator) {
Ok(entities) => {
designator.set_reference(&entities);
match entities {
NamedEntities::Single(ent) => {
designator.set_unique_reference(&ent);
let is_correct = ent.match_with_target_type(target_type);
if is_correct == Some(false) {
diagnostics.push(type_mismatch(
&designator.pos,
&ent,
target_type,
));
}
Ok(is_correct)
}
NamedEntities::Overloaded(overloaded) => self
.resolve_overloaded_with_target_type(
region,
overloaded,
Some(target_type),
&designator.pos,
&mut designator.item,
&mut [],
diagnostics,
),
}
}
Err(err) => {
err.add_to(diagnostics)?;
Ok(None)
}
}
} else {
Ok(None)
}
}
Name::FunctionCall(fcall) => {
match self.resolve_name(
region,
&fcall.name.pos,
&mut fcall.name.item,
diagnostics,
)? {
Some(NamedEntities::Single(ent)) => {
if ent.actual_kind().is_type() {
self.analyze_assoc_elems(region, &mut fcall.parameters, diagnostics)?;
} else if let Some((prefix, indexes)) = fcall.to_indexed() {
*name = Name::Indexed(prefix, indexes);
let Name::Indexed(ref mut prefix, ref mut indexes) = name else { unreachable!()};
if let Some(type_mark) = type_mark_of_sliced_or_indexed(&ent) {
if let Err(err) = self.analyze_indexed_name(
region,
name_pos,
prefix.suffix_pos(),
type_mark,
indexes,
diagnostics,
) {
err.add_to(diagnostics)?;
}
} else {
diagnostics.error(
prefix.suffix_pos(),
format!("{} cannot be indexed", ent.describe()),
)
}
} else {
diagnostics.push(Diagnostic::error(
&fcall.name.pos,
format!(
"{} cannot be the prefix of a function call",
ent.describe()
),
));
self.analyze_assoc_elems(region, &mut fcall.parameters, diagnostics)?;
}
}
Some(NamedEntities::Overloaded(overloaded)) => {
if let Some(reference) = fcall.name.item.suffix_reference_mut() {
self.resolve_overloaded_with_target_type(
region,
overloaded,
Some(target_type),
&fcall.name.pos,
reference,
fcall.parameters.as_mut_slice(),
diagnostics,
)?;
}
}
None => {
self.analyze_assoc_elems(region, &mut fcall.parameters, diagnostics)?;
}
};
Ok(None)
}
Name::Indexed(..) => {
Ok(None)
}
Name::SelectedAll(..) => {
self.resolve_name(region, name_pos, name, diagnostics)?;
Ok(None)
}
Name::External(..) => {
self.resolve_name(region, name_pos, name, diagnostics)?;
Ok(None)
}
Name::Attribute(..) => {
self.resolve_name(region, name_pos, name, diagnostics)?;
Ok(None)
}
Name::Slice(ref mut prefix, ref mut drange) => {
if let Some(NamedEntities::Single(ref named_entity)) =
self.resolve_name(region, &prefix.pos, &mut prefix.item, diagnostics)?
{
if let Some(type_mark) = type_mark_of_sliced_or_indexed(named_entity) {
self.analyze_sliced_name(prefix.suffix_pos(), type_mark, diagnostics)?;
} else {
diagnostics.error(
prefix.suffix_pos(),
format!("{} cannot be sliced", named_entity.describe()),
)
}
}
self.analyze_discrete_range(region, drange.as_mut(), diagnostics)?;
Ok(None)
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn resolve_overloaded_with_target_type(
&self,
region: &Region<'_>,
overloaded: OverloadedName,
target_type: Option<&TypeEnt>,
pos: &SrcPos,
designator: &mut WithRef<Designator>,
parameters: &mut [AssociationElement],
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult<Option<bool>> {
let mut good = Vec::with_capacity(overloaded.len());
let mut bad = Vec::with_capacity(overloaded.len());
let mut uncertain = false;
for name in overloaded.entities() {
if let Some(sig) = name.signature() {
let is_correct = if sig.match_return_type(target_type) {
let mut temp_diagnostics = Vec::new();
self.analyze_assoc_elems_with_formal_region(
pos,
&sig.params,
region,
parameters,
&mut temp_diagnostics,
)?
} else {
Some(false)
};
match is_correct {
Some(true) => good.push((name, sig)),
Some(false) => bad.push((name, sig)),
None => uncertain = true,
}
}
}
#[allow(clippy::if_same_then_else)]
if good.len() > 1 {
let mut diagnostic =
Diagnostic::error(pos, format!("Ambiguous use of '{}'", designator));
diagnostic.add_subprogram_candidates(
"Migth be",
good.into_iter().map(|(ent, _)| ent).collect(),
);
diagnostics.push(diagnostic);
self.analyze_assoc_elems(region, parameters, diagnostics)?;
Ok(None)
} else if uncertain {
self.analyze_assoc_elems(region, parameters, diagnostics)?;
Ok(None)
} else if let &[(ent, sig)] = good.as_slice() {
designator.set_unique_reference(ent);
self.analyze_assoc_elems_with_formal_region(
pos,
&sig.params,
region,
parameters,
diagnostics,
)?;
Ok(Some(true))
} else if let &[(ent, sig)] = bad.as_slice() {
designator.set_unique_reference(ent);
if parameters.is_empty() && sig.params.is_empty() {
if let Some(target_type) = target_type {
diagnostics.error(
pos,
format!("'{}' does not match {}", designator, target_type.describe()),
)
} else {
let mut diagnostic =
Diagnostic::error(pos, format!("Could not resolve '{}'", designator));
diagnostic
.add_subprogram_candidates("Does not match", overloaded.sorted_entities());
diagnostics.push(diagnostic)
};
} else {
self.analyze_assoc_elems_with_formal_region(
pos,
&sig.params,
region,
parameters,
diagnostics,
)?;
}
Ok(Some(false))
} else {
if let (Some(ent), Some(target_type)) = (overloaded.as_unique(), target_type) {
designator.set_unique_reference(ent);
diagnostics.error(
pos,
format!("'{}' does not match {}", designator, target_type.describe()),
)
} else {
let mut diagnostic =
Diagnostic::error(pos, format!("Could not resolve '{}'", designator));
diagnostic
.add_subprogram_candidates("Does not match", overloaded.sorted_entities());
diagnostics.push(diagnostic)
}
self.analyze_assoc_elems(region, parameters, diagnostics)?;
Ok(Some(false))
}
}
pub fn analyze_literal_with_target_type(
&self,
region: &Region<'_>,
target_type: &TypeEnt,
pos: &SrcPos,
literal: &mut Literal,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult<Option<bool>> {
let target_base = target_type.base_type();
let is_correct = match literal {
Literal::AbstractLiteral(AbstractLiteral::Integer(_)) => {
let is_correct = matches!(target_base.kind(), Type::Integer(..));
if !is_correct {
diagnostics.push(Diagnostic::error(
pos,
format!("integer literal does not match {}", target_type.describe()),
));
}
Some(is_correct)
}
Literal::AbstractLiteral(AbstractLiteral::Real(_)) => {
let is_correct = matches!(target_base.kind(), Type::Real(..));
if !is_correct {
diagnostics.push(Diagnostic::error(
pos,
format!("real literal does not match {}", target_type.describe()),
));
}
Some(is_correct)
}
Literal::Character(char) => match target_base.kind() {
Type::Enum(_, literals) => {
if literals.contains(&Designator::Character(*char)) {
Some(true)
} else {
diagnostics.push(Diagnostic::error(
pos,
format!(
"character literal does not match {}",
target_type.describe()
),
));
Some(false)
}
}
_ => {
diagnostics.push(Diagnostic::error(
pos,
format!(
"character literal does not match {}",
target_type.describe()
),
));
Some(false)
}
},
Literal::String(string_lit) => match target_base.kind() {
Type::Array {
indexes, elem_type, ..
} => {
if indexes.len() == 1 {
match elem_type.base_type().kind() {
Type::Enum(_, literals) => {
let mut is_correct = true;
for chr in string_lit.chars() {
let chr = Designator::Character(*chr);
if !literals.contains(&chr) {
is_correct = false;
diagnostics.push(Diagnostic::error(
pos,
format!(
"{} does not define character {}",
elem_type.describe(),
chr
),
))
}
}
Some(is_correct)
}
_ => {
diagnostics.push(Diagnostic::error(
pos,
format!(
"string literal does not match {}",
target_type.describe()
),
));
Some(false)
}
}
} else {
diagnostics.push(Diagnostic::error(
pos,
format!("string literal does not match {}", target_type.describe()),
));
Some(false)
}
}
_ => {
diagnostics.push(Diagnostic::error(
pos,
format!("string literal does not match {}", target_type.describe()),
));
Some(false)
}
},
Literal::Physical(PhysicalLiteral { ref mut unit, .. }) => {
match self.resolve_physical_unit(region, unit) {
Ok(physical_type) => Some(physical_type.base_type() == target_base),
Err(diagnostic) => {
diagnostics.push(diagnostic);
Some(false)
}
}
}
_ => None,
};
Ok(is_correct)
}
pub fn resolve_physical_unit(
&self,
region: &Region<'_>,
unit: &mut WithRef<Ident>,
) -> Result<TypeEnt, Diagnostic> {
match region.lookup_within(
&unit.item.pos,
&Designator::Identifier(unit.item.item.clone()),
)? {
NamedEntities::Single(unit_ent) => {
unit.set_unique_reference(&unit_ent);
if let NamedEntityKind::PhysicalLiteral(physical_ent) = unit_ent.actual_kind() {
Ok(physical_ent.clone())
} else {
Err(Diagnostic::error(
&unit.item.pos,
format!("{} is not a physical unit", unit_ent.describe()),
))
}
}
NamedEntities::Overloaded(_) => Err(Diagnostic::error(
&unit.item.pos,
"Overloaded name may not be physical unit",
)),
}
}
pub fn analyze_expression_with_target_type(
&self,
region: &Region<'_>,
target_type: &TypeEnt,
expr_pos: &SrcPos,
expr: &mut Expression,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult<Option<bool>> {
match expr {
Expression::Literal(ref mut lit) => self.analyze_literal_with_target_type(
region,
target_type,
expr_pos,
lit,
diagnostics,
),
Expression::Name(ref mut name) => {
self.analyze_name_with_target_type(region, target_type, expr_pos, name, diagnostics)
}
Expression::Qualified(ref mut qexpr) => {
let is_correct = if let Some(type_mark) =
self.analyze_qualified_expression(region, qexpr, diagnostics)?
{
let is_correct = target_type.base_type() == type_mark.base_type();
if !is_correct {
diagnostics.push(type_mismatch(expr_pos, &type_mark, target_type));
}
Some(is_correct)
} else {
None
};
Ok(is_correct)
}
_ => {
self.analyze_expression_pos(region, expr_pos, expr, diagnostics)?;
Ok(None)
}
}
}
pub fn analyze_expr_assignment(
&self,
region: &Region<'_>,
target: &mut WithPos<Target>,
assignment_type: AssignmentType,
rhs: &mut AssignmentRightHand<WithPos<Expression>>,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
match rhs {
AssignmentRightHand::Simple(expr) => {
self.analyze_target(region, target, assignment_type, diagnostics)?;
self.analyze_expression(region, expr, diagnostics)?;
}
AssignmentRightHand::Conditional(conditionals) => {
let Conditionals {
conditionals,
else_item,
} = conditionals;
self.analyze_target(region, target, assignment_type, diagnostics)?;
for conditional in conditionals {
let Conditional { condition, item } = conditional;
self.analyze_expression(region, item, diagnostics)?;
self.analyze_expression(region, condition, diagnostics)?;
}
if let Some(expr) = else_item {
self.analyze_expression(region, expr, diagnostics)?;
}
}
AssignmentRightHand::Selected(selection) => {
let Selection {
expression,
alternatives,
} = selection;
self.analyze_expression(region, expression, diagnostics)?;
self.analyze_target(region, target, assignment_type, diagnostics)?;
for Alternative { choices, item } in alternatives.iter_mut() {
self.analyze_expression(region, item, diagnostics)?;
self.analyze_choices(region, choices, diagnostics)?;
}
}
}
Ok(())
}
pub fn analyze_waveform_assignment(
&self,
region: &Region<'_>,
target: &mut WithPos<Target>,
assignment_type: AssignmentType,
rhs: &mut AssignmentRightHand<Waveform>,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalNullResult {
match rhs {
AssignmentRightHand::Simple(wavf) => {
self.analyze_target(region, target, assignment_type, diagnostics)?;
self.analyze_waveform(region, wavf, diagnostics)?;
}
AssignmentRightHand::Conditional(conditionals) => {
let Conditionals {
conditionals,
else_item,
} = conditionals;
self.analyze_target(region, target, assignment_type, diagnostics)?;
for conditional in conditionals {
let Conditional { condition, item } = conditional;
self.analyze_waveform(region, item, diagnostics)?;
self.analyze_expression(region, condition, diagnostics)?;
}
if let Some(wavf) = else_item {
self.analyze_waveform(region, wavf, diagnostics)?;
}
}
AssignmentRightHand::Selected(selection) => {
let Selection {
expression,
alternatives,
} = selection;
self.analyze_expression(region, expression, diagnostics)?;
self.analyze_target(region, target, assignment_type, diagnostics)?;
for Alternative { choices, item } in alternatives.iter_mut() {
self.analyze_waveform(region, item, diagnostics)?;
self.analyze_choices(region, choices, diagnostics)?;
}
}
}
Ok(())
}
}
pub fn type_mark_of_sliced_or_indexed(ent: &Arc<NamedEntity>) -> Option<&TypeEnt> {
Some(match ent.kind() {
NamedEntityKind::NonObjectAlias(ref alias) => {
return type_mark_of_sliced_or_indexed(alias);
}
NamedEntityKind::Object(ref ent) => ent.subtype.type_mark(),
NamedEntityKind::DeferredConstant(ref subtype) => subtype.type_mark(),
NamedEntityKind::ElementDeclaration(ref subtype) => subtype.type_mark(),
NamedEntityKind::ObjectAlias { type_mark, .. } => type_mark,
_ => {
return None;
}
})
}
impl Diagnostic {
pub fn add_subprogram_candidates(
&mut self,
prefix: &str,
mut candidates: Vec<&Arc<NamedEntity>>,
) {
candidates.sort_by_key(|ent| ent.decl_pos());
for ent in candidates {
if let Some(signature) = ent.signature() {
if let Some(decl_pos) = ent.decl_pos() {
self.add_related(
decl_pos,
format!("{} {}{}", prefix, ent.designator(), signature.describe()),
)
}
}
}
}
}
impl NamedEntity {
pub fn kind_error(&self, pos: &SrcPos, expected: &str) -> Diagnostic {
let mut error = Diagnostic::error(
pos,
format!("Expected {}, got {}", expected, self.describe()),
);
if let Some(decl_pos) = self.decl_pos() {
error.add_related(decl_pos, "Defined here");
}
error
}
fn match_with_target_type(&self, target_type: &TypeEnt) -> Option<bool> {
let typ = match self.actual_kind() {
NamedEntityKind::ObjectAlias { ref type_mark, .. } => type_mark.base_type(),
NamedEntityKind::Object(ref ent) => ent.subtype.base_type(),
NamedEntityKind::DeferredConstant(ref subtype) => subtype.base_type(),
NamedEntityKind::ElementDeclaration(ref subtype) => subtype.base_type(),
NamedEntityKind::PhysicalLiteral(ref base_type) => base_type,
NamedEntityKind::InterfaceFile(ref file) => file.base_type(),
NamedEntityKind::File(ref file) => file.base_type(),
_ => {
return None;
}
};
let target_base = target_type.base_type();
if matches!(typ.kind(), Type::Interface) || matches!(target_base.kind(), Type::Interface) {
None
} else {
Some(typ == target_base)
}
}
}
fn type_mismatch(pos: &SrcPos, ent: &NamedEntity, expected_type: &NamedEntity) -> Diagnostic {
Diagnostic::error(
pos,
format!(
"{} does not match {}",
ent.describe(),
expected_type.describe()
),
)
}
pub fn invalid_selected_name_prefix(named_entity: &NamedEntity, prefix: &SrcPos) -> Diagnostic {
Diagnostic::error(
prefix,
capitalize(&format!(
"{} may not be the prefix of a selected name",
named_entity.describe(),
)),
)
}
pub fn no_declaration_within(
named_entity: &NamedEntity,
suffix: &WithPos<WithRef<Designator>>,
) -> Diagnostic {
Diagnostic::error(
suffix.as_ref(),
format!(
"No declaration of '{}' within {}",
suffix.item,
named_entity.describe(),
),
)
}
fn plural(singular: &'static str, plural: &'static str, count: usize) -> &'static str {
if count == 1 {
singular
} else {
plural
}
}
fn dimension_mismatch(
pos: &SrcPos,
base_type: &TypeEnt,
got: usize,
expected: usize,
) -> Diagnostic {
let mut diag = Diagnostic::error(pos, "Number of indexes does not match array dimension");
if let Some(decl_pos) = base_type.decl_pos() {
diag.add_related(
decl_pos,
capitalize(&format!(
"{} has {} {}, got {} {}",
base_type.describe(),
expected,
plural("dimension", "dimensions", expected),
got,
plural("index", "indexes", got),
)),
);
}
diag
}