diff --git a/vhdl_lang/src/analysis/concurrent.rs b/vhdl_lang/src/analysis/concurrent.rs index e4184d18..f1f684d2 100644 --- a/vhdl_lang/src/analysis/concurrent.rs +++ b/vhdl_lang/src/analysis/concurrent.rs @@ -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()), diff --git a/vhdl_lang/src/analysis/declarative.rs b/vhdl_lang/src/analysis/declarative.rs index 35030af9..6fc93ded 100644 --- a/vhdl_lang/src/analysis/declarative.rs +++ b/vhdl_lang/src/analysis/declarative.rs @@ -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::*; @@ -124,8 +145,6 @@ impl<'a, 't> AnalyzeContext<'a, 't> { let mut incomplete_types: FnvHashMap, SrcPos)> = FnvHashMap::default(); for i in 0..declarations.len() { - // Handle incomplete types - let (WithTokenSpan { item: decl, span }, remaining) = declarations[i..].split_first_mut().unwrap(); @@ -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) => { @@ -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); } @@ -943,39 +960,121 @@ impl<'a, 't> AnalyzeContext<'a, 't> { object_decl: &mut InterfaceObjectDeclaration, diagnostics: &mut dyn DiagnosticHandler, ) -> EvalResult>> { - 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, @@ -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( diff --git a/vhdl_lang/src/analysis/range.rs b/vhdl_lang/src/analysis/range.rs index 36199693..f2e4119f 100644 --- a/vhdl_lang/src/analysis/range.rs +++ b/vhdl_lang/src/analysis/range.rs @@ -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), @@ -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)) => { diff --git a/vhdl_lang/src/analysis/scope.rs b/vhdl_lang/src/analysis/scope.rs index 3abf6ef4..f9aaf81f 100644 --- a/vhdl_lang/src/analysis/scope.rs +++ b/vhdl_lang/src/analysis/scope.rs @@ -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> { diff --git a/vhdl_lang/src/analysis/sequential.rs b/vhdl_lang/src/analysis/sequential.rs index 2a6bdfa4..858221d1 100644 --- a/vhdl_lang/src/analysis/sequential.rs +++ b/vhdl_lang/src/analysis/sequential.rs @@ -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()), diff --git a/vhdl_lang/src/analysis/standard.rs b/vhdl_lang/src/analysis/standard.rs index 030d9b4b..b06fae41 100644 --- a/vhdl_lang/src/analysis/standard.rs +++ b/vhdl_lang/src/analysis/standard.rs @@ -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() { diff --git a/vhdl_lang/src/analysis/tests/association_formal.rs b/vhdl_lang/src/analysis/tests/association_formal.rs index e59ab2a6..ea485849 100644 --- a/vhdl_lang/src/analysis/tests/association_formal.rs +++ b/vhdl_lang/src/analysis/tests/association_formal.rs @@ -5,6 +5,7 @@ // // Copyright (c) 2022, Olof Kraigher olof.kraigher@gmail.com use super::*; +use crate::VHDLStandard::VHDL2019; use pretty_assertions::assert_eq; use vhdl_lang::data::error_codes::ErrorCode; @@ -377,9 +378,8 @@ end architecture; ", ); - let diagnostics = builder.analyze(); check_diagnostics( - diagnostics, + builder.analyze(), vec![Diagnostic::new( code.s1("fun1(theport, 2)"), "Invalid formal conversion", @@ -422,9 +422,8 @@ end architecture; ", ); - let diagnostics = builder.analyze(); check_diagnostics( - diagnostics, + builder.analyze(), vec![Diagnostic::new( code.s1("fun1(arg => theport)"), "Invalid formal conversion", @@ -587,3 +586,161 @@ end architecture; code.s1("prt1").pos() ); } + +#[test] +fn view_array_with_explicit_type() { + let mut builder = LibraryBuilder::with_standard(VHDL2019); + let code = builder.code( + "libname", + " +package test_pkg is + type test_t is record + foo : bit; + end record; + + view vone of test_t is + foo : in; + end view; + + type test_array is array (natural range <>) of test_t; +end package; + +use work.test_pkg.all; + +entity test_sub_entity is + port ( + my_array_if: view (vone) of test_array(1 downto 0) + ); +end entity; + +architecture arch of test_sub_entity is +begin + my_array_if(0).foo <= '1'; +end architecture; + ", + ); + let (root, diagnostics) = builder.get_analyzed_root(); + check_no_diagnostics(&diagnostics); + + assert_eq!( + root.search_reference_pos(code.source(), code.s1("test_array(1 downto 0)").start()) + .unwrap(), + code.s1("test_array").pos() + ); + + assert_eq!( + root.search_reference_pos(code.source(), code.s1("my_array_if(0).foo").end()) + .unwrap(), + code.s1("foo").pos() + ); +} + +#[test] +fn view_array_without_explicit_type() { + let mut builder = LibraryBuilder::with_standard(VHDL2019); + let code = builder.code( + "libname", + " +package test_pkg is + type test_t is record + foo : bit; + end record; + + view vone of test_t is + foo : in; + end view; +end package; + +use work.test_pkg.all; + +entity test_sub_entity is + port ( + my_array_if: view (vone) + ); +end entity; + +architecture arch of test_sub_entity is +begin + my_array_if(0).foo <= '1'; +end architecture; + ", + ); + let (root, diagnostics) = builder.get_analyzed_root(); + check_no_diagnostics(&diagnostics); + + assert_eq!( + root.search_reference_pos(code.source(), code.s1("my_array_if(0).foo").end()) + .unwrap(), + code.s1("foo").pos() + ); +} + +#[test] +fn view_array_with_non_array_type() { + let mut builder = LibraryBuilder::with_standard(VHDL2019); + let code = builder.code( + "libname", + " +package test_pkg is + type test_t is record + foo : natural; + end record; + + view vone of test_t is + foo : in; + end view; +end package; + +use work.test_pkg.all; + +entity test_sub_entity is + port ( + my_array_if: view (vone) of bit + ); +end entity; + ", + ); + check_diagnostics( + builder.analyze(), + vec![Diagnostic::new( + code.s1("bit"), + "Subtype must be an array", + ErrorCode::TypeMismatch, + )], + ); +} + +#[test] +fn view_array_with_mismatched_element_type() { + let mut builder = LibraryBuilder::with_standard(VHDL2019); + let code = builder.code( + "libname", + " +package test_pkg is + type test_t is record + foo : natural; + end record; + + view vone of test_t is + foo : in; + end view; +end package; + +use work.test_pkg.all; + +entity test_sub_entity is + port ( + my_array_if: view (vone) of bit_vector + ); +end entity; + ", + ); + check_diagnostics( + builder.analyze(), + vec![Diagnostic::new( + code.s1("bit_vector"), + "Array element type 'BIT' must match record type 'test_t' declared for the view", + ErrorCode::TypeMismatch, + )], + ); +} diff --git a/vhdl_lang/src/analysis/tests/subprogram_instance.rs b/vhdl_lang/src/analysis/tests/subprogram_instance.rs index 222138c6..cbc1d146 100644 --- a/vhdl_lang/src/analysis/tests/subprogram_instance.rs +++ b/vhdl_lang/src/analysis/tests/subprogram_instance.rs @@ -22,14 +22,13 @@ procedure proc is new foo; ", ); - let diagnostics = builder.analyze(); - assert_eq!( - diagnostics, + check_diagnostics( + builder.analyze(), vec![Diagnostic::new( code.s1("foo").pos(), "No declaration of 'foo'", - ErrorCode::Unresolved - )] + ErrorCode::Unresolved, + )], ); } @@ -44,14 +43,13 @@ function proc is new x; ", ); - let diagnostics = builder.analyze(); - assert_eq!( - diagnostics, + check_diagnostics( + builder.analyze(), vec![Diagnostic::mismatched_kinds( code.s1("new x").s1("x"), "signal 'x' does not denote an uninstantiated subprogram", - )] - ) + )], + ); } #[test] @@ -75,9 +73,8 @@ procedure proc is new foo; ", ); - let diagnostics = builder.analyze(); check_diagnostics( - diagnostics, + builder.analyze(), vec![Diagnostic::new( code.s1("new foo").s1("foo"), "Ambiguous instantiation of 'foo'", @@ -110,8 +107,7 @@ procedure proc2 is new foo [bit, bit] generic map (a => 5); ", ); - let diagnostics = builder.analyze(); - check_no_diagnostics(&diagnostics); + check_no_diagnostics(&builder.analyze()); } #[test] @@ -129,9 +125,8 @@ procedure proc is new foo [bit, bit]; ", ); - let diagnostics = builder.analyze(); check_diagnostics( - diagnostics, + builder.analyze(), vec![Diagnostic::new( code.s1("[bit, bit]").pos(), "Signature does not match the the signature of procedure foo[BIT]", @@ -154,8 +149,7 @@ procedure proc is new proc; ", ); - let diagnostics = builder.analyze(); - check_no_diagnostics(&diagnostics); + check_no_diagnostics(&builder.analyze()); } #[test] diff --git a/vhdl_lang/src/analysis/tests/tool_directive.rs b/vhdl_lang/src/analysis/tests/tool_directive.rs index 278fdd84..3e3c6068 100644 --- a/vhdl_lang/src/analysis/tests/tool_directive.rs +++ b/vhdl_lang/src/analysis/tests/tool_directive.rs @@ -9,9 +9,7 @@ use crate::analysis::tests::{check_no_diagnostics, LibraryBuilder}; fn simple_tool_directive() { let mut builder = LibraryBuilder::new(); builder.code("libname", "`protect begin"); - let (_, diagnostics) = builder.get_analyzed_root(); - - check_no_diagnostics(&diagnostics); + check_no_diagnostics(&builder.analyze()); } #[test] @@ -31,7 +29,5 @@ end my_ent; `protect encoding = (enctype = \"BASE64\", line_length = 76, bytes = 64) ", ); - let (_, diagnostics) = builder.get_analyzed_root(); - - check_no_diagnostics(&diagnostics); + check_no_diagnostics(&builder.analyze()); } diff --git a/vhdl_lang/src/analysis/tests/view_declarations.rs b/vhdl_lang/src/analysis/tests/view_declarations.rs index 8795d9aa..e9134d7c 100644 --- a/vhdl_lang/src/analysis/tests/view_declarations.rs +++ b/vhdl_lang/src/analysis/tests/view_declarations.rs @@ -13,9 +13,8 @@ view my_view of undeclared is end view; ", ); - let (_, diag) = builder.get_analyzed_root(); check_diagnostics( - diag, + builder.analyze(), vec![Diagnostic::new( code.s1("undeclared"), "No declaration of 'undeclared'", @@ -35,9 +34,8 @@ view my_view of foo is end view; ", ); - let (_, diag) = builder.get_analyzed_root(); check_diagnostics( - diag, + builder.analyze(), vec![Diagnostic::new( code.s("foo", 2), "The type of a view must be a record type, not type 'foo'", @@ -58,8 +56,7 @@ view my_view of foo is end view; ", ); - let (_, diag) = builder.get_analyzed_root(); - check_no_diagnostics(&diag); + check_no_diagnostics(&builder.analyze()); } #[test] @@ -468,3 +465,108 @@ end arch; )], ); } + +#[test] +fn arrays_of_views_with_matching_type() { + let mut builder = LibraryBuilder::with_standard(VHDL2019); + builder.code( + "libname", + "\ +package test_pkg is + type test_t is record + a : bit; + end record; + + view vone of test_t is + a : in; + end view; + + type test_array is array (natural range <>) of test_t; +end package; + +use work.test_pkg.all; + +entity test_sub_entity is + port ( + my_if : view vone; + my_array_if: view (vone) of test_array(0 to 1) + ); +end entity; + ", + ); + check_no_diagnostics(&builder.analyze()); +} + +#[test] +fn arrays_of_views_with_non_matching_type() { + let mut builder = LibraryBuilder::with_standard(VHDL2019); + let code = builder.code( + "libname", + "\ +package test_pkg is + type test_t is record + a : bit; + end record; + + view vone of test_t is + a : in; + end view; + + type test_array is array (natural range <>) of bit; +end package; + +use work.test_pkg.all; + +entity test_sub_entity is + port ( + my_if : view vone; + my_array_if: view (vone) of test_array(0 to 1) + ); +end entity; + ", + ); + check_diagnostics( + builder.analyze(), + vec![Diagnostic::new( + code.s1("test_array(0 to 1)").s1("test_array"), + "Array element type 'BIT' must match record type 'test_t' declared for the view", + ErrorCode::TypeMismatch, + )], + ) +} + +#[test] +fn arrays_of_views_that_are_not_arrays() { + let mut builder = LibraryBuilder::with_standard(VHDL2019); + let code = builder.code( + "libname", + "\ +package test_pkg is + type test_t is record + a : bit; + end record; + + view vone of test_t is + a : in; + end view; +end package; + +use work.test_pkg.all; + +entity test_sub_entity is + port ( + my_if : view vone; + my_array_if: view (vone) of test_t + ); +end entity; + ", + ); + check_diagnostics( + builder.analyze(), + vec![Diagnostic::new( + code.s1("view (vone) of test_t").s1("test_t"), + "Subtype must be an array", + ErrorCode::TypeMismatch, + )], + ) +} diff --git a/vhdl_lang/src/named_entity.rs b/vhdl_lang/src/named_entity.rs index 6a83e1ea..4b850baf 100644 --- a/vhdl_lang/src/named_entity.rs +++ b/vhdl_lang/src/named_entity.rs @@ -286,18 +286,15 @@ impl Arena { of_ent: EntRef<'a>, designator: impl Into, kind: AnyEntKind<'a>, - decl_pos: Option<&SrcPos>, - src_span: TokenSpan, - source: Option, ) -> EntRef<'a> { self.alloc( designator.into(), of_ent.parent, Related::ImplicitOf(of_ent), kind, - decl_pos.cloned(), - src_span, - source, + of_ent.decl_pos().cloned(), + of_ent.src_span, + of_ent.source.clone(), ) }