Skip to content

Commit

Permalink
Analyze Subprogram Instantiation Declarations (#209)
Browse files Browse the repository at this point in the history
* Parse subprogram instantiation to declaration

* Fix logic for identifier

* Make item and token public

* Check kind mismatch and resolve declaration

* revert accidentally deleted test

* Error on ambiguously instantiated subprograms

* Resolve overloaded uninstantiated subprograms

* return Err when the signature of the instantiated subprogram does not match

* Add comments

* Disallow instantiating an instantiable subprogram

* Disallow instantiating a non-instantiable subprogram

* Disallow calling an uninstantiated subprogram

* Disambiguate based on uninstantiated subprogram

* Fix: no longer always complain when instantiating a function

* Merge master aftermath

* Revert: Signature no longer contains optional generic map

* Refactor boolean to two-state enum

* Refactor: Region to GpkgRegion

* Refactor: subprogram instantiation to own function

* Revert: Use region instead of GpkgRegion

* Instantiate subprograms

* Remove debug print

* Refactor into more generic code

* Inline check_ent_is_uninstantiated_subprogram

* Fill in todos and false unreachable calls

* Add documentation

* Add more checks to subprogram instance testcase

* Set reference to the uninstantiated subprogram

* Fix: Analyze the return of a function type in the correct scope

* Add subprogram instance tests for future improvements

* Enable subprogram declarations as reference to the overloaded entity

* Add comment explaining the limitations of the current uninstantiated subprogram resolution

* Rename is_uninst* for less ambiguity

* Revert making fields public

* Revert accidental change

* Improve test case and add other ognored one

* Add copyright header

* Refactor SignatureKey to SubprogramKey

* Add uninstantiated subprograms to checked declarations
  • Loading branch information
Schottkyc137 committed Nov 19, 2023
1 parent f9d0ee9 commit aa1a719
Show file tree
Hide file tree
Showing 13 changed files with 1,097 additions and 86 deletions.
363 changes: 338 additions & 25 deletions vhdl_lang/src/analysis/declarative.rs

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions vhdl_lang/src/analysis/overloaded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,21 @@ impl<'a> AnalyzeContext<'a> {
return Err(EvalError::Unknown);
}

// Disambiguate based on uninstantiated subprogram
ok_kind.retain(|ent| !ent.is_uninst_subprogram());

if ok_kind.len() == 1 {
let ent = ok_kind[0];
self.check_call(scope, call_pos, ent, assocs, diagnostics)?;
return Ok(Disambiguated::Unambiguous(ent));
} else if ok_kind.is_empty() {
diagnostics.push(Diagnostic::error(
call_name,
format!("uninstantiated subprogram {} cannot be called", call_name),
));
return Err(EvalError::Unknown);
}

let ok_formals = self.disambiguate_by_assoc_formals(scope, call_pos, &ok_kind, assocs)?;

// Only one candidate matched actual/formal profile
Expand Down
132 changes: 81 additions & 51 deletions vhdl_lang/src/analysis/package_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,56 @@
//! Copyright (c) 2023, Olof Kraigher [email protected]

use fnv::FnvHashMap;
use vhdl_lang::SrcPos;

use super::analyze::*;
use super::names::ResolvedName;
use super::scope::*;
use crate::ast::ActualPart;
use crate::ast::AssociationElement;
use crate::ast::Expression;
use crate::ast::Literal;
use crate::ast::Name;
use crate::ast::Operator;
use crate::ast::PackageInstantiation;
use crate::ast::{ActualPart, MapAspect};
use crate::data::DiagnosticHandler;
use crate::named_entity::*;
use crate::Diagnostic;
use crate::NullDiagnostics;

impl<'a> AnalyzeContext<'a> {
fn package_generic_map(
pub fn generic_package_instance(
&self,
scope: &Scope<'a>,
package_ent: EntRef<'a>,
unit: &mut PackageInstantiation,
diagnostics: &mut dyn DiagnosticHandler,
) -> EvalResult<Region<'a>> {
let PackageInstantiation {
package_name,
generic_map,
..
} = unit;

match self.analyze_package_instance_name(scope, package_name) {
Ok(package_region) => self
.generic_instance(
package_ent,
scope,
&unit.ident.tree.pos,
package_region,
generic_map,
diagnostics,
)
.map(|(region, _)| region),
Err(err) => {
diagnostics.push(err.into_non_fatal()?);
Err(EvalError::Unknown)
}
}
}

pub fn generic_map(
&self,
scope: &Scope<'a>,
generics: GpkgRegion<'a>,
Expand Down Expand Up @@ -124,7 +156,7 @@ impl<'a> AnalyzeContext<'a> {
let resolved =
self.name_resolve(scope, &assoc.actual.pos, name, diagnostics)?;
if let ResolvedName::Overloaded(des, overloaded) = resolved {
let signature = target.signature().key().map(|base_type| {
let signature = target.subprogram_key().map(|base_type| {
mapping
.get(&base_type.id())
.map(|ent| ent.base())
Expand All @@ -139,7 +171,7 @@ impl<'a> AnalyzeContext<'a> {
"Cannot map '{}' to subprogram generic {}{}",
des,
target.designator(),
signature.describe()
signature.key().describe()
),
);

Expand Down Expand Up @@ -188,62 +220,50 @@ impl<'a> AnalyzeContext<'a> {
Ok(mapping)
}

pub fn generic_package_instance(
pub fn generic_instance(
&self,
ent: EntRef<'a>,
scope: &Scope<'a>,
package_ent: EntRef<'a>,
unit: &mut PackageInstantiation,
decl_pos: &SrcPos,
uninst_region: &Region<'a>,
generic_map: &mut Option<MapAspect>,
diagnostics: &mut dyn DiagnosticHandler,
) -> EvalResult<Region<'a>> {
let PackageInstantiation {
package_name,
generic_map,
..
} = unit;
) -> EvalResult<(Region<'a>, FnvHashMap<EntityId, TypeEnt>)> {
let nested = scope.nested().in_package_declaration();
let (generics, other) = uninst_region.to_package_generic();

let mapping = if let Some(generic_map) = generic_map {
self.generic_map(
&nested,
generics,
generic_map.list.items.as_mut_slice(),
diagnostics,
)?
} else {
FnvHashMap::default()
};

match self.analyze_package_instance_name(scope, package_name) {
Ok(package_region) => {
let nested = scope.nested().in_package_declaration();
let (generics, other) = package_region.to_package_generic();

let mapping = if let Some(generic_map) = generic_map {
self.package_generic_map(
&nested,
generics,
generic_map.list.items.as_mut_slice(),
diagnostics,
)?
} else {
FnvHashMap::default()
};

for uninst in other {
match self.instantiate(Some(package_ent), &mapping, uninst) {
Ok(inst) => {
// We ignore diagnostics here, for example when adding implicit operators EQ and NE for interface types
// They can collide if there are more than one interface type that map to the same actual type
nested.add(inst, &mut NullDiagnostics);
}
Err(err) => {
let mut diag = Diagnostic::error(&unit.ident.tree.pos, err);
if let Some(pos) = uninst.decl_pos() {
diag.add_related(pos, "When instantiating this declaration");
}
diagnostics.push(diag);
}
for uninst in other {
match self.instantiate(Some(ent), &mapping, uninst) {
Ok(inst) => {
// We ignore diagnostics here, for example when adding implicit operators EQ and NE for interface types
// They can collide if there are more than one interface type that map to the same actual type
nested.add(inst, &mut NullDiagnostics);
}
Err(err) => {
let mut diag = Diagnostic::error(decl_pos, err);
if let Some(pos) = uninst.decl_pos() {
diag.add_related(pos, "When instantiating this declaration");
}
diagnostics.push(diag);
}

Ok(nested.into_region())
}
Err(err) => {
diagnostics.push(err.into_non_fatal()?);
Err(EvalError::Unknown)
}
}

Ok((nested.into_region(), mapping))
}

fn instantiate(
pub(crate) fn instantiate(
&self,
parent: Option<EntRef<'a>>,
mapping: &FnvHashMap<EntityId, TypeEnt<'a>>,
Expand Down Expand Up @@ -358,6 +378,16 @@ impl<'a> AnalyzeContext<'a> {
Overloaded::Subprogram(signature) => {
Overloaded::Subprogram(self.map_signature(parent, mapping, signature)?)
}
Overloaded::UninstSubprogramDecl(signature, generic_map) => {
Overloaded::UninstSubprogramDecl(
self.map_signature(parent, mapping, signature)?,
generic_map.clone(),
)
}
Overloaded::UninstSubprogram(signature, generic_map) => Overloaded::UninstSubprogram(
self.map_signature(parent, mapping, signature)?,
generic_map.clone(),
),
Overloaded::InterfaceSubprogram(signature) => {
Overloaded::InterfaceSubprogram(self.map_signature(parent, mapping, signature)?)
}
Expand All @@ -379,7 +409,7 @@ impl<'a> AnalyzeContext<'a> {
})
}

fn map_signature(
pub(crate) fn map_signature(
&self,
parent: Option<EntRef<'a>>,
mapping: &FnvHashMap<EntityId, TypeEnt<'a>>,
Expand Down
5 changes: 5 additions & 0 deletions vhdl_lang/src/analysis/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ impl<'a> AnalyzeContext<'a> {
}
}
diagnostics.push(diagnostic);
} else if ent.is_uninst_subprogram_body() {
diagnostics.error(
&name.pos,
format!("uninstantiated {} cannot be called", ent.describe()),
)
}
}
None => {}
Expand Down
1 change: 0 additions & 1 deletion vhdl_lang/src/analysis/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ impl<'a> AnalyzeContext<'a> {

fn binary(
&self,

op: Operator,
implicit_of: TypeEnt<'a>,
left: TypeEnt<'a>,
Expand Down
1 change: 1 addition & 0 deletions vhdl_lang/src/analysis/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod resolves_names;
mod resolves_type_mark;
mod sensitivity_list;
mod subprogram_arguments;
mod subprogram_instance;
mod tool_directive;
mod typecheck_expression;
mod util;
Expand Down
Loading

0 comments on commit aa1a719

Please sign in to comment.