Skip to content

Commit f8175eb

Browse files
committed
Allow array type in array aggregates
1 parent 64b3ab2 commit f8175eb

File tree

3 files changed

+109
-49
lines changed

3 files changed

+109
-49
lines changed

vhdl_lang/src/analysis/semantic.rs

Lines changed: 79 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -672,68 +672,99 @@ impl<'a> AnalyzeContext<'a> {
672672
pub fn analyze_1d_array_assoc_elem(
673673
&self,
674674
region: &Region<'_>,
675+
array_type: &TypeEnt,
675676
index_type: Option<&TypeEnt>,
676677
elem_type: &TypeEnt,
677678
assoc: &mut ElementAssociation,
678679
diagnostics: &mut dyn DiagnosticHandler,
679680
) -> FatalResult<TypeCheck> {
680-
match assoc {
681+
let mut can_be_array = true;
682+
let mut check = TypeCheck::Ok;
683+
684+
let expr = match assoc {
681685
ElementAssociation::Named(ref mut choices, ref mut expr) => {
682-
if let &[Choice::Others] = choices.as_slice() {
683-
self.analyze_expression_with_target_type(
684-
region,
685-
elem_type,
686-
&expr.pos,
687-
&mut expr.item,
688-
diagnostics,
689-
)
690-
} else {
691-
let mut check = TypeCheck::Ok;
692-
for choice in choices.iter_mut() {
693-
match choice {
694-
Choice::Expression(index_expr) => {
695-
if let Some(index_type) = index_type {
696-
check.add(self.analyze_expression_with_target_type(
697-
region,
698-
index_type,
699-
&index_expr.pos,
700-
&mut index_expr.item,
701-
diagnostics,
702-
)?);
703-
}
704-
}
705-
Choice::DiscreteRange(ref mut drange) => {
706-
if let Some(index_type) = index_type {
707-
check.add(self.analyze_discrete_range_with_target_type(
708-
region,
709-
index_type,
710-
drange,
711-
diagnostics,
712-
)?);
713-
} else {
714-
self.analyze_discrete_range(region, drange, diagnostics)?;
715-
check.add(TypeCheck::Unknown)
716-
}
686+
for choice in choices.iter_mut() {
687+
match choice {
688+
Choice::Expression(index_expr) => {
689+
if let Some(index_type) = index_type {
690+
check.add(self.analyze_expression_with_target_type(
691+
region,
692+
index_type,
693+
&index_expr.pos,
694+
&mut index_expr.item,
695+
diagnostics,
696+
)?);
717697
}
718-
Choice::Others => {
719-
// @TODO choice must be alone so cannot appear here
720-
check.add(TypeCheck::Unknown);
698+
can_be_array = false;
699+
}
700+
Choice::DiscreteRange(ref mut drange) => {
701+
if let Some(index_type) = index_type {
702+
check.add(self.analyze_discrete_range_with_target_type(
703+
region,
704+
index_type,
705+
drange,
706+
diagnostics,
707+
)?);
708+
} else {
709+
self.analyze_discrete_range(region, drange, diagnostics)?;
710+
check.add(TypeCheck::Unknown)
721711
}
722712
}
713+
Choice::Others => {
714+
// @TODO choice must be alone so cannot appear here
715+
check.add(TypeCheck::Unknown);
716+
can_be_array = false;
717+
}
723718
}
724-
self.analyze_expression(region, expr, diagnostics)?;
725-
Ok(check)
726719
}
720+
expr
727721
}
728-
ElementAssociation::Positional(ref mut expr) => self
729-
.analyze_expression_with_target_type(
722+
ElementAssociation::Positional(ref mut expr) => expr,
723+
};
724+
725+
if can_be_array {
726+
// If the choice is only a range or positional the expression can be an array
727+
let mut elem_diagnostics = Vec::new();
728+
729+
let elem_check = self.analyze_expression_with_target_type(
730+
region,
731+
elem_type,
732+
&expr.pos,
733+
&mut expr.item,
734+
&mut elem_diagnostics,
735+
)?;
736+
737+
if elem_check == TypeCheck::Ok {
738+
diagnostics.append(elem_diagnostics);
739+
check.add(elem_check);
740+
} else {
741+
let mut array_diagnostics = Vec::new();
742+
let array_check = self.analyze_expression_with_target_type(
730743
region,
731-
elem_type,
744+
array_type,
732745
&expr.pos,
733746
&mut expr.item,
734-
diagnostics,
735-
),
747+
&mut array_diagnostics,
748+
)?;
749+
750+
if array_check == TypeCheck::Ok {
751+
diagnostics.append(array_diagnostics);
752+
check.add(array_check);
753+
} else {
754+
diagnostics.append(elem_diagnostics);
755+
check.add(elem_check);
756+
}
757+
};
758+
} else {
759+
check.add(self.analyze_expression_with_target_type(
760+
region,
761+
elem_type,
762+
&expr.pos,
763+
&mut expr.item,
764+
diagnostics,
765+
)?);
736766
}
767+
Ok(check)
737768
}
738769

739770
fn analyze_qualified_expression(
@@ -1184,13 +1215,12 @@ impl<'a> AnalyzeContext<'a> {
11841215
for name in overloaded.entities() {
11851216
if let Some(sig) = name.signature() {
11861217
let is_correct = if sig.match_return_type(target_type) {
1187-
let mut temp_diagnostics = Vec::new();
11881218
self.analyze_assoc_elems_with_formal_region(
11891219
pos,
11901220
&sig.params,
11911221
region,
11921222
parameters,
1193-
&mut temp_diagnostics,
1223+
&mut NullDiagnostics,
11941224
)?
11951225
} else {
11961226
TypeCheck::NotOk
@@ -1335,6 +1365,7 @@ impl<'a> AnalyzeContext<'a> {
13351365
for assoc in assocs.iter_mut() {
13361366
check.add(self.analyze_1d_array_assoc_elem(
13371367
region,
1368+
target_base,
13381369
index_type.as_ref(),
13391370
elem_type,
13401371
assoc,

vhdl_lang/src/analysis/tests/typecheck_expression.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,8 @@ constant good2 : arr2_t := ((0, 1), (2, 3));
743743
constant good3 : rec_t := (field => 0);
744744
constant bad1 : integer_vector := (3, 4, 'c');
745745
constant bad2 : integer_vector := (others => 'd');
746-
constant bad3 : rec_t := (field => 'e');
746+
constant bad3 : integer_vector := (1 to 3 => 'e');
747+
constant bad4 : rec_t := (field => 'f');
747748
748749
",
749750
);
@@ -764,6 +765,10 @@ constant bad3 : rec_t := (field => 'e');
764765
code.s1("'e'"),
765766
"character literal does not match integer type 'INTEGER'",
766767
),
768+
Diagnostic::error(
769+
code.s1("'f'"),
770+
"character literal does not match integer type 'INTEGER'",
771+
),
767772
],
768773
);
769774
}
@@ -798,3 +803,19 @@ constant bad2 : integer_vector := ('a' to 'z' => 0);
798803
],
799804
);
800805
}
806+
807+
/// LRM 9.3.3.3 Array aggregates
808+
#[test]
809+
fn array_element_association_may_be_array_of_element_type() {
810+
let mut builder = LibraryBuilder::new();
811+
builder.in_declarative_region(
812+
"
813+
constant good1 : integer_vector := (0 to 2 => (0, 1, 2));
814+
constant good2 : string(1 to 6) := (\"text\", others => ' ');
815+
816+
",
817+
);
818+
819+
let diagnostics = builder.analyze();
820+
check_no_diagnostics(&diagnostics);
821+
}

vhdl_lang/src/data/diagnostic.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ impl DiagnosticHandler for Vec<Diagnostic> {
147147
}
148148
}
149149

150+
pub struct NullDiagnostics;
151+
152+
impl DiagnosticHandler for NullDiagnostics {
153+
fn push(&mut self, _diagnostic: Diagnostic) {
154+
// Ignore
155+
}
156+
}
157+
150158
#[cfg(test)]
151159
mod tests {
152160
use super::*;

0 commit comments

Comments
 (0)