Skip to content

Commit 5af68f9

Browse files
authored
Parse and analyse VHDL2019 Views (#285)
1 parent 6a892af commit 5af68f9

26 files changed

+1670
-192
lines changed

vhdl_lang/src/analysis/concurrent.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,8 @@ impl<'a> AnalyzeContext<'a> {
442442
),
443443
ErrorCode::DisallowedInSensitivityList,
444444
)
445-
} else if object_name.base.mode() == Some(Mode::Out) && !object_name.base.is_port()
445+
} else if object_name.base.mode() == Some(&InterfaceMode::Simple(Mode::Out))
446+
&& !object_name.base.is_port()
446447
{
447448
diagnostics.add(
448449
&name.pos,

vhdl_lang/src/analysis/declarative.rs

Lines changed: 214 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ use crate::named_entity::{Signature, *};
1313
use crate::{ast, named_entity, HasTokenSpan};
1414
use analyze::*;
1515
use fnv::FnvHashMap;
16+
use itertools::Itertools;
1617
use std::collections::hash_map::Entry;
18+
use std::collections::HashSet;
1719

1820
impl Declaration {
1921
pub fn is_allowed_in_context(&self, parent: &AnyEntKind) -> bool {
2022
use Declaration::*;
2123
use ObjectClass::*;
2224
match parent {
25+
// LRM: block_declarative_item
2326
AnyEntKind::Design(Design::Architecture(..))
2427
| AnyEntKind::Concurrent(Some(Concurrent::Block | Concurrent::Generate)) => matches!(
2528
self,
@@ -37,10 +40,13 @@ impl Declaration {
3740
| Use(_)
3841
| Package(_)
3942
| Configuration(_)
43+
| View(_)
4044
),
45+
// LRM: configuration_declarative_item
4146
AnyEntKind::Design(Design::Configuration) => {
4247
matches!(self, Use(_) | Attribute(ast::Attribute::Specification(_)))
4348
}
49+
// LRM: entity_declarative_item
4450
AnyEntKind::Design(Design::Entity(..)) => matches!(
4551
self,
4652
Object(_)
@@ -53,7 +59,9 @@ impl Declaration {
5359
| SubprogramBody(_)
5460
| Use(_)
5561
| Package(_)
62+
| View(_)
5663
),
64+
// LRM: package_body_declarative_item
5765
AnyEntKind::Design(Design::PackageBody | Design::UninstPackage(..))
5866
| AnyEntKind::Overloaded(
5967
Overloaded::SubprogramDecl(_)
@@ -77,6 +85,7 @@ impl Declaration {
7785
| Use(_)
7886
| Package(_)
7987
),
88+
// LRM: package_declarative_item
8089
AnyEntKind::Design(Design::Package(..)) => matches!(
8190
self,
8291
Object(_)
@@ -89,6 +98,7 @@ impl Declaration {
8998
| SubprogramInstantiation(_)
9099
| Use(_)
91100
| Package(_)
101+
| View(_)
92102
),
93103
_ => {
94104
// AnyEntKind::Library is used in tests for a generic declarative region
@@ -297,9 +307,13 @@ impl<'a> AnalyzeContext<'a> {
297307
return Err(EvalError::Unknown);
298308
}
299309
}
300-
ResolvedName::Final(_) => {
301-
// @TODO some of these can probably be aliased
302-
return Err(EvalError::Unknown);
310+
ResolvedName::Final(ent) => {
311+
if let Some(ent) = ViewEnt::from_any(ent) {
312+
AnyEntKind::View(*ent.subtype())
313+
} else {
314+
// @TODO some of these can probably be aliased
315+
return Err(EvalError::Unknown);
316+
}
303317
}
304318
}
305319
};
@@ -566,7 +580,6 @@ impl<'a> AnalyzeContext<'a> {
566580
Declaration::Use(ref mut use_clause) => {
567581
self.analyze_use_clause(scope, use_clause, diagnostics)?;
568582
}
569-
570583
Declaration::Package(ref mut instance) => {
571584
let ent = self.arena.define(
572585
&mut instance.ident,
@@ -586,12 +599,79 @@ impl<'a> AnalyzeContext<'a> {
586599
}
587600
}
588601
Declaration::Configuration(..) => {}
602+
Declaration::View(view) => {
603+
if let Some(view) =
604+
as_fatal(self.analyze_view_declaration(scope, parent, view, diagnostics))?
605+
{
606+
scope.add(view, diagnostics);
607+
}
608+
}
589609
Declaration::Type(..) => unreachable!("Handled elsewhere"),
590610
};
591611

592612
Ok(())
593613
}
594614

615+
/// Analyzes a mode view declaration.
616+
/// * Checks that the type of the view declaration is a record type
617+
/// * Checks that all elements are associated in the view
618+
fn analyze_view_declaration(
619+
&self,
620+
scope: &Scope<'a>,
621+
parent: EntRef<'a>,
622+
view: &mut ModeViewDeclaration,
623+
diagnostics: &mut dyn DiagnosticHandler,
624+
) -> EvalResult<EntRef<'a>> {
625+
let typ = self.resolve_subtype_indication(scope, &mut view.typ, diagnostics)?;
626+
let record_region = match typ.type_mark().kind() {
627+
Type::Record(region) => region,
628+
_ => {
629+
let diag = Diagnostic::new(
630+
&view.typ.type_mark,
631+
format!(
632+
"The type of a view must be a record type, not {}",
633+
typ.type_mark().describe()
634+
),
635+
ErrorCode::TypeMismatch,
636+
)
637+
.opt_related(
638+
typ.type_mark().decl_pos.as_ref(),
639+
format!("{} declared here", typ.type_mark().describe()),
640+
);
641+
bail!(diagnostics, diag);
642+
}
643+
};
644+
let mut unassociated: HashSet<_> = record_region.elems.iter().collect();
645+
for element in view.elements.iter_mut() {
646+
for name in element.names.items.iter_mut() {
647+
let desi = Designator::Identifier(name.item.item.clone());
648+
let Some(record_element) = record_region.lookup(&desi) else {
649+
diagnostics.push(Diagnostic::new(
650+
&name.item.pos,
651+
format!("Not a part of {}", typ.type_mark().describe()),
652+
ErrorCode::Unresolved,
653+
));
654+
continue;
655+
};
656+
name.set_unique_reference(&record_element);
657+
unassociated.remove(&record_element);
658+
}
659+
}
660+
if !unassociated.is_empty() {
661+
diagnostics.add(
662+
&view.ident.tree,
663+
pretty_format_unassociated_message(&unassociated),
664+
ErrorCode::Unassociated,
665+
);
666+
}
667+
Ok(self.arena.define(
668+
&mut view.ident,
669+
parent,
670+
AnyEntKind::View(typ),
671+
Some(view.span),
672+
))
673+
}
674+
595675
fn find_deferred_constant_declaration(
596676
&self,
597677
scope: &Scope<'a>,
@@ -792,41 +872,7 @@ impl<'a> AnalyzeContext<'a> {
792872
)
793873
}
794874
InterfaceDeclaration::Object(ref mut object_decl) => {
795-
let subtype = self.resolve_subtype_indication(
796-
scope,
797-
&mut object_decl.subtype_indication,
798-
diagnostics,
799-
);
800-
801-
if let Some(ref mut expression) = object_decl.expression {
802-
if let Ok(ref subtype) = subtype {
803-
self.expr_pos_with_ttyp(
804-
scope,
805-
subtype.type_mark(),
806-
&expression.pos,
807-
&mut expression.item,
808-
diagnostics,
809-
)?;
810-
} else {
811-
self.expr_unknown_ttyp(scope, expression, diagnostics)?
812-
}
813-
}
814-
815-
let subtype = subtype?;
816-
self.arena.define(
817-
&mut object_decl.ident,
818-
parent,
819-
AnyEntKind::Object(Object {
820-
class: object_decl.class,
821-
iface: Some(ObjectInterface::new(
822-
object_decl.list_type,
823-
object_decl.mode,
824-
)),
825-
subtype,
826-
has_default: object_decl.expression.is_some(),
827-
}),
828-
None,
829-
)
875+
self.analyze_interface_object_declaration(scope, parent, object_decl, diagnostics)?
830876
}
831877
InterfaceDeclaration::Type(ref mut ident) => {
832878
let typ = TypeEnt::from_any(self.arena.define(
@@ -880,6 +926,91 @@ impl<'a> AnalyzeContext<'a> {
880926
Ok(ent)
881927
}
882928

929+
pub fn analyze_interface_object_declaration(
930+
&self,
931+
scope: &Scope<'a>,
932+
parent: EntRef<'a>,
933+
object_decl: &mut InterfaceObjectDeclaration,
934+
diagnostics: &mut dyn DiagnosticHandler,
935+
) -> EvalResult<EntRef<'a>> {
936+
match &mut object_decl.mode {
937+
ModeIndication::Simple(mode) => {
938+
let (subtype, class) =
939+
self.analyze_simple_mode_indication(scope, mode, diagnostics)?;
940+
Ok(self.arena.define(
941+
&mut object_decl.ident,
942+
parent,
943+
AnyEntKind::Object(Object {
944+
class,
945+
iface: Some(ObjectInterface::simple(
946+
object_decl.list_type,
947+
mode.mode.unwrap_or_default(),
948+
)),
949+
subtype,
950+
has_default: mode.expression.is_some(),
951+
}),
952+
None,
953+
))
954+
}
955+
ModeIndication::View(view) => {
956+
let resolved =
957+
self.name_resolve(scope, &view.name.pos, &mut view.name.item, diagnostics)?;
958+
let view_ent = self.resolve_view_ent(&resolved, diagnostics, &view.name.pos)?;
959+
if let Some(ast_declared_subtype) = &mut view.subtype_indication {
960+
let declared_subtype =
961+
self.resolve_subtype_indication(scope, ast_declared_subtype, diagnostics)?;
962+
if declared_subtype.type_mark() != view_ent.subtype().type_mark() {
963+
bail!(
964+
diagnostics,
965+
Diagnostic::new(
966+
&ast_declared_subtype.type_mark,
967+
"Specified subtype must match the subtype declared for the view",
968+
ErrorCode::TypeMismatch
969+
)
970+
);
971+
}
972+
}
973+
Ok(self.arena.define(
974+
&mut object_decl.ident,
975+
parent,
976+
AnyEntKind::Object(Object {
977+
class: ObjectClass::Signal,
978+
iface: Some(ObjectInterface::Port(InterfaceMode::View(view_ent))),
979+
subtype: *view_ent.subtype(),
980+
has_default: false,
981+
}),
982+
None,
983+
))
984+
}
985+
}
986+
}
987+
988+
pub fn analyze_simple_mode_indication(
989+
&self,
990+
scope: &Scope<'a>,
991+
mode: &mut SimpleModeIndication,
992+
diagnostics: &mut dyn DiagnosticHandler,
993+
) -> EvalResult<(Subtype<'a>, ObjectClass)> {
994+
let subtype =
995+
self.resolve_subtype_indication(scope, &mut mode.subtype_indication, diagnostics);
996+
997+
if let Some(ref mut expression) = mode.expression {
998+
if let Ok(ref subtype) = subtype {
999+
self.expr_pos_with_ttyp(
1000+
scope,
1001+
subtype.type_mark(),
1002+
&expression.pos,
1003+
&mut expression.item,
1004+
diagnostics,
1005+
)?;
1006+
} else {
1007+
self.expr_unknown_ttyp(scope, expression, diagnostics)?
1008+
}
1009+
}
1010+
1011+
Ok((subtype?, mode.class))
1012+
}
1013+
8831014
pub fn analyze_interface_list(
8841015
&self,
8851016
scope: &Scope<'a>,
@@ -1020,6 +1151,7 @@ fn get_entity_class(ent: EntRef) -> Option<EntityClass> {
10201151
Design::InterfacePackageInstance(_) => None,
10211152
Design::Context(_) => None,
10221153
},
1154+
AnyEntKind::View(_) => None,
10231155
}
10241156
}
10251157

@@ -1043,3 +1175,46 @@ fn find_full_type_definition<'a>(
10431175
}
10441176
None
10451177
}
1178+
1179+
const UNASSOCIATED_DISPLAY_THRESHOLD: usize = 3;
1180+
1181+
/// Pretty formats a hash set with unassociated record elements.
1182+
/// This is for an improved user experience.
1183+
/// The returned message has the format "Missing association of x".
1184+
/// * If there is only one element, the message becomes "Missing association of element the_element"
1185+
/// * If there are more elements, the message becomes
1186+
/// "Missing association of element the_element1, the_element2 and the_element3"
1187+
/// * If there are more elements than [UNASSOCIATED_DISPLAY_THRESHOLD], the message will be truncated
1188+
/// to "Missing association of element the_element1, the_element2, the_element3 and 17 more"
1189+
fn pretty_format_unassociated_message(unassociated: &HashSet<&RecordElement>) -> String {
1190+
assert!(
1191+
!unassociated.is_empty(),
1192+
"Should never be called with an empty set"
1193+
);
1194+
let mut as_string_vec = unassociated
1195+
.iter()
1196+
.sorted_by_key(|el| el.decl_pos())
1197+
.map(|el| el.designator().describe())
1198+
.collect_vec();
1199+
let description = if as_string_vec.len() == 1 {
1200+
as_string_vec.pop().unwrap()
1201+
} else if as_string_vec.len() > UNASSOCIATED_DISPLAY_THRESHOLD {
1202+
let mut desc = as_string_vec[..UNASSOCIATED_DISPLAY_THRESHOLD].join(", ");
1203+
desc.push_str(" and ");
1204+
desc.push_str(&(as_string_vec.len() - UNASSOCIATED_DISPLAY_THRESHOLD).to_string());
1205+
desc.push_str(" more");
1206+
desc
1207+
} else {
1208+
// len > 1, therefore we always have a last element
1209+
let last = as_string_vec.pop().unwrap();
1210+
let mut desc = as_string_vec.join(", ");
1211+
desc.push_str(" and ");
1212+
desc.push_str(&last);
1213+
desc
1214+
};
1215+
if unassociated.len() == 1 {
1216+
format!("Missing association of element {}", description)
1217+
} else {
1218+
format!("Missing association of elements {}", description)
1219+
}
1220+
}

0 commit comments

Comments
 (0)