Skip to content

Commit

Permalink
Enable arrays of views in interface lists (#326)
Browse files Browse the repository at this point in the history
  • Loading branch information
Schottkyc137 committed Aug 6, 2024
1 parent ea630c1 commit 0144e0e
Show file tree
Hide file tree
Showing 11 changed files with 450 additions and 103 deletions.
2 changes: 1 addition & 1 deletion vhdl_lang/src/analysis/concurrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
} else if statement.statement.item.can_have_label() {
// Generate an anonymous label if it is not explicitly defined
let ent = self.arena.alloc(
Designator::Anonymous(scope.next_anonymous()),
scope.anonymous_designator(),
Some(parent),
Related::None,
AnyEntKind::Concurrent(statement.statement.item.label_typ()),
Expand Down
210 changes: 155 additions & 55 deletions vhdl_lang/src/analysis/declarative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ use std::collections::HashSet;
use vhdl_lang::TokenSpan;

impl Declaration {
/// Returns whether the declaration denoted by `self` is allowed in the given context.
/// For example, within an architecture, only constants, signals and shared variables are allowed,
/// variables are not.
///
/// ### Conforming example:
/// ```vhdl
/// architecture arch of ent is
/// signal foo : bit;
/// begin
/// end arch;
/// ```
///
/// ### Non-Conforming example:
/// ```vhdl
/// architecture arch of ent is
/// variable foo : bit;
/// begin
/// end arch;
/// ```
///
/// The context is given by the parent element of the declaration.
pub fn is_allowed_in_context(&self, parent: &AnyEntKind) -> bool {
use Declaration::*;
use ObjectClass::*;
Expand Down Expand Up @@ -124,8 +145,6 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
let mut incomplete_types: FnvHashMap<Symbol, (EntRef<'a>, SrcPos)> = FnvHashMap::default();

for i in 0..declarations.len() {
// Handle incomplete types

let (WithTokenSpan { item: decl, span }, remaining) =
declarations[i..].split_first_mut().unwrap();

Expand All @@ -137,6 +156,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
)
}

// Handle incomplete types
match decl {
Declaration::Type(type_decl) => match type_decl.def {
TypeDefinition::Incomplete(ref mut reference) => {
Expand Down Expand Up @@ -369,9 +389,6 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
ent,
implicit.designator().clone(),
AnyEntKind::Overloaded(Overloaded::Alias(implicit)),
ent.decl_pos(),
ent.src_span,
Some(self.source()),
);
scope.add(impicit_alias, diagnostics);
}
Expand Down Expand Up @@ -943,39 +960,121 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
object_decl: &mut InterfaceObjectDeclaration,
diagnostics: &mut dyn DiagnosticHandler,
) -> EvalResult<Vec<EntRef<'a>>> {
let span = object_decl.span();
match &mut object_decl.mode {
ModeIndication::Simple(mode) => {
let (subtype, class) =
self.analyze_simple_mode_indication(scope, mode, diagnostics)?;
Ok(object_decl
.idents
.iter_mut()
.map(|ident| {
self.define(
ident,
parent,
AnyEntKind::Object(Object {
class,
iface: Some(ObjectInterface::simple(
object_decl.list_type,
mode.mode.as_ref().map(|mode| mode.item).unwrap_or_default(),
)),
subtype,
has_default: mode.expression.is_some(),
}),
span,
)
})
.collect_vec())
let objects = object_decl
.idents
.iter_mut()
.map(|ident| {
self.define(
ident,
parent,
AnyEntKind::Object(Object {
class: ObjectClass::Signal,
iface: None,
subtype: Subtype::new(self.universal_integer().into()),
has_default: false,
}),
object_decl.span,
)
})
.collect_vec();
for object in &objects {
let actual_object = match &mut object_decl.mode {
ModeIndication::Simple(mode) => {
let (subtype, class) =
self.analyze_simple_mode_indication(scope, mode, diagnostics)?;
Object {
class,
iface: Some(ObjectInterface::simple(
object_decl.list_type,
mode.mode.as_ref().map(|mode| mode.item).unwrap_or_default(),
)),
subtype,
has_default: mode.expression.is_some(),
}
}
ModeIndication::View(view) => {
let (view_ent, subtype) =
self.analyze_mode_indication(scope, object, view, diagnostics)?;
Object {
class: ObjectClass::Signal,
iface: Some(ObjectInterface::Port(InterfaceMode::View(view_ent))),
subtype,
has_default: false,
}
}
};
unsafe {
object.set_kind(AnyEntKind::Object(actual_object));
}
ModeIndication::View(view) => {
let resolved =
self.name_resolve(scope, view.name.span, &mut view.name.item, diagnostics)?;
let view_ent = self.resolve_view_ent(&resolved, diagnostics, view.name.span)?;
if let Some((_, ast_declared_subtype)) = &mut view.subtype_indication {
let declared_subtype =
self.resolve_subtype_indication(scope, ast_declared_subtype, diagnostics)?;
}

Ok(objects)
}

/// Analyzes a mode view indication of the form
/// ```vhdl
/// foo : view s_axis of axi_stream
/// ```
///
/// This function resolves all used types and verifies them.
/// If the provided view describes an array but no actual array type is given, i.e.:
/// ```vhdl
/// multiple_foos : view (s_axis)
/// ```
/// this function will declare an anonymous array indication with a single index and the
/// view's subtype as element, similar as if the view was declared like so:
/// ```vhdl
/// -- pseudo code
/// type anonymous is array (integer range <>) of axi_stream;
/// multiple_foos : view (s_axis) of anonymous
/// ```
/// The anonymous array type will be marked as being linked to the interface declaration
/// (in the example above: the `anonymous` type is implicitly declared by `multiple_foos`)
fn analyze_mode_indication(
&self,
scope: &Scope<'a>,
object_ent: EntRef<'a>,
view: &mut ModeViewIndication,
diagnostics: &mut dyn DiagnosticHandler,
) -> EvalResult<(ViewEnt<'a>, Subtype<'a>)> {
let resolved =
self.name_resolve(scope, view.name.span, &mut view.name.item, diagnostics)?;
let view_ent = self.resolve_view_ent(&resolved, diagnostics, view.name.span)?;
let subtype = if let Some((_, ast_declared_subtype)) = &mut view.subtype_indication {
let declared_subtype =
self.resolve_subtype_indication(scope, ast_declared_subtype, diagnostics)?;
match view.kind {
ModeViewIndicationKind::Array => {
let Type::Array {
indexes: _,
elem_type,
} = declared_subtype.type_mark().kind()
else {
bail!(
diagnostics,
Diagnostic::new(
ast_declared_subtype.type_mark.pos(self.ctx),
"Subtype must be an array",
ErrorCode::TypeMismatch
)
);
};
if *elem_type != view_ent.subtype().type_mark() {
bail!(
diagnostics,
Diagnostic::new(
ast_declared_subtype.type_mark.pos(self.ctx),
format!(
"Array element {} must match {} declared for the view",
elem_type.describe(),
view_ent.subtype().type_mark().describe()
),
ErrorCode::TypeMismatch
)
);
}
}
ModeViewIndicationKind::Record => {
if declared_subtype.type_mark() != view_ent.subtype().type_mark() {
bail!(
diagnostics,
Expand All @@ -987,25 +1086,26 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
);
}
}
Ok(object_decl
.idents
.iter_mut()
.map(|ident| {
self.define(
ident,
parent,
AnyEntKind::Object(Object {
class: ObjectClass::Signal,
iface: Some(ObjectInterface::Port(InterfaceMode::View(view_ent))),
subtype: *view_ent.subtype(),
has_default: false,
}),
span,
)
})
.collect_vec())
}
}
declared_subtype
} else {
match view.kind {
ModeViewIndicationKind::Array => {
let typ = Type::Array {
indexes: vec![Some(self.universal_integer())],
elem_type: view_ent.subtype().type_mark(),
};
let typ = self.arena.implicit(
object_ent,
scope.anonymous_designator(),
AnyEntKind::Type(typ),
);
Subtype::new(TypeEnt::from_any(typ).unwrap())
}
ModeViewIndicationKind::Record => *view_ent.subtype(),
}
};
Ok((view_ent, subtype))
}

pub fn analyze_simple_mode_indication(
Expand Down
6 changes: 3 additions & 3 deletions vhdl_lang/src/analysis/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ impl<'a, 't> AnalyzeContext<'a, 't> {

let types = match (left_types, right_types) {
(DisambiguatedType::Unambiguous(l), DisambiguatedType::Unambiguous(r)) => {
if let Some(typ) = self.common_type(l.base(), r.base()) {
return Ok(typ);
return if let Some(typ) = self.common_type(l.base(), r.base()) {
Ok(typ)
} else {
diagnostics.add(
constraint.span().pos(self.ctx),
Expand All @@ -185,7 +185,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
),
ErrorCode::TypeMismatch,
);
return Err(EvalError::Unknown);
Err(EvalError::Unknown)
}
}
(DisambiguatedType::Unambiguous(l), DisambiguatedType::Ambiguous(r)) => {
Expand Down
4 changes: 4 additions & 0 deletions vhdl_lang/src/analysis/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ impl<'a> Scope<'a> {
inner.anon_idx += 1;
idx
}

pub fn anonymous_designator(&self) -> Designator {
Designator::Anonymous(self.next_anonymous())
}
}

impl<'a> NamedEntities<'a> {
Expand Down
2 changes: 1 addition & 1 deletion vhdl_lang/src/analysis/sequential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
} else if statement.statement.item.can_have_label() {
// Generate an anonymous label if it is not explicitly defined
let ent = self.arena.alloc(
Designator::Anonymous(scope.next_anonymous()),
scope.anonymous_designator(),
Some(parent),
Related::None,
AnyEntKind::Sequential(statement.statement.item.label_typ()),
Expand Down
3 changes: 0 additions & 3 deletions vhdl_lang/src/analysis/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,6 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
FormalRegion::new_params(),
return_type,
))),
implicit_of.decl_pos(),
implicit_of.src_span,
Some(self.source()),
);

for (name, kind) in formals.into_iter() {
Expand Down
Loading

0 comments on commit 0144e0e

Please sign in to comment.