Skip to content

Commit 08e8d87

Browse files
committed
Add support for matching operators
1 parent 20d51e2 commit 08e8d87

File tree

6 files changed

+196
-59
lines changed

6 files changed

+196
-59
lines changed

vhdl_lang/src/analysis/analyze.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ pub(super) struct AnalyzeContext<'a> {
135135
pub work_sym: Symbol,
136136
std_sym: Symbol,
137137
standard_sym: Symbol,
138+
pub(super) is_std_logic_1164: bool,
138139

139140
// Record dependencies and sensitivies when
140141
// analyzing design units
@@ -166,6 +167,11 @@ impl<'a> AnalyzeContext<'a> {
166167
work_sym: root.symbol_utf8("work"),
167168
std_sym: root.symbol_utf8("std"),
168169
standard_sym: root.symbol_utf8("standard"),
170+
is_std_logic_1164: current_unit
171+
== &UnitId::package(
172+
&root.symbol_utf8("ieee"),
173+
&root.symbol_utf8("std_logic_1164"),
174+
),
169175
root,
170176
current_unit: current_unit.clone(),
171177
arena,

vhdl_lang/src/analysis/declarative.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ impl<'a> AnalyzeContext<'a> {
567567

568568
scope.add(enum_type.into(), diagnostics);
569569

570-
for ent in self.enum_implicits(enum_type) {
570+
for ent in self.enum_implicits(enum_type, self.has_matching_op(enum_type)) {
571571
unsafe {
572572
self.arena.add_implicit(enum_type.id(), ent);
573573
}
@@ -766,6 +766,7 @@ impl<'a> AnalyzeContext<'a> {
766766
}
767767
};
768768

769+
let is_1d = indexes.len() == 1;
769770
let array_ent = TypeEnt::define_with_opt_id(
770771
self.arena,
771772
overwrite_id,
@@ -775,7 +776,8 @@ impl<'a> AnalyzeContext<'a> {
775776

776777
scope.add(array_ent.into(), diagnostics);
777778

778-
for ent in self.array_implicits(array_ent) {
779+
for ent in self.array_implicits(array_ent, is_1d && self.has_matching_op(elem_type))
780+
{
779781
unsafe {
780782
self.arena.add_implicit(array_ent.id(), ent);
781783
}
@@ -928,6 +930,28 @@ impl<'a> AnalyzeContext<'a> {
928930
Ok(())
929931
}
930932

933+
/// The matching operators such as ?= are defined for 1d arrays of bit and std_ulogic element type
934+
fn has_matching_op(&self, typ: TypeEnt<'a>) -> bool {
935+
if self.is_std_logic_1164 {
936+
// Within the std_logic_1164 we do not have efficient access to the types
937+
typ.designator() == &Designator::Identifier(self.root.symbol_utf8("std_ulogic"))
938+
} else {
939+
if let Some(ref standard_types) = self.root.standard_types {
940+
if typ.id() == standard_types.bit {
941+
return true;
942+
}
943+
}
944+
945+
if let Some(id) = self.root.std_ulogic {
946+
if typ.id() == id {
947+
return true;
948+
}
949+
}
950+
951+
false
952+
}
953+
}
954+
931955
pub fn resolve_signature(
932956
&self,
933957
scope: &Scope<'a>,

vhdl_lang/src/analysis/expression.rs

Lines changed: 31 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -385,10 +385,6 @@ impl<'a> AnalyzeContext<'a> {
385385
exprs: &mut [&mut WithPos<Expression>],
386386
diagnostics: &mut dyn DiagnosticHandler,
387387
) -> EvalResult<ExpressionType<'a>> {
388-
if !can_handle(op.item.item) {
389-
return Err(EvalError::Unknown);
390-
}
391-
392388
let op_candidates = match self.lookup_operator(scope, &op.pos, op.item.item, exprs.len()) {
393389
Ok(candidates) => candidates,
394390
Err(err) => {
@@ -750,47 +746,41 @@ impl<'a> AnalyzeContext<'a> {
750746
}
751747
}
752748
Expression::Binary(ref mut op, ref mut left, ref mut right) => {
753-
if can_handle(op.item.item) {
754-
let op_candidates = match self.lookup_operator(scope, &op.pos, op.item.item, 2)
755-
{
756-
Ok(candidates) => candidates,
757-
Err(err) => {
758-
diagnostics.push(err.into_non_fatal()?);
759-
return Ok(());
760-
}
761-
};
749+
let op_candidates = match self.lookup_operator(scope, &op.pos, op.item.item, 2) {
750+
Ok(candidates) => candidates,
751+
Err(err) => {
752+
diagnostics.push(err.into_non_fatal()?);
753+
return Ok(());
754+
}
755+
};
762756

763-
match as_fatal(self.disambiguate_op(
764-
scope,
765-
Some(target_type),
766-
op,
767-
op_candidates,
768-
&mut [left.as_mut(), right.as_mut()],
769-
diagnostics,
770-
))? {
771-
Some(Disambiguated::Unambiguous(overloaded)) => {
772-
let op_type = overloaded.return_type().unwrap();
773-
774-
if !self.can_be_target_type(op_type, target_type.base()) {
775-
diagnostics.push(Diagnostic::type_mismatch(
776-
expr_pos,
777-
&op_type.describe(),
778-
target_type,
779-
));
780-
}
781-
}
782-
Some(Disambiguated::Ambiguous(candidates)) => {
783-
diagnostics.push(Diagnostic::ambiguous_op(
784-
&op.pos,
785-
op.item.item,
786-
candidates,
757+
match as_fatal(self.disambiguate_op(
758+
scope,
759+
Some(target_type),
760+
op,
761+
op_candidates,
762+
&mut [left.as_mut(), right.as_mut()],
763+
diagnostics,
764+
))? {
765+
Some(Disambiguated::Unambiguous(overloaded)) => {
766+
let op_type = overloaded.return_type().unwrap();
767+
768+
if !self.can_be_target_type(op_type, target_type.base()) {
769+
diagnostics.push(Diagnostic::type_mismatch(
770+
expr_pos,
771+
&op_type.describe(),
772+
target_type,
787773
));
788774
}
789-
None => {}
790775
}
791-
} else {
792-
self.expr_unknown_ttyp(scope, left, diagnostics)?;
793-
self.expr_unknown_ttyp(scope, right, diagnostics)?;
776+
Some(Disambiguated::Ambiguous(candidates)) => {
777+
diagnostics.push(Diagnostic::ambiguous_op(
778+
&op.pos,
779+
op.item.item,
780+
candidates,
781+
));
782+
}
783+
None => {}
794784
}
795785
}
796786
Expression::Unary(ref mut op, ref mut expr) => {
@@ -1091,19 +1081,6 @@ impl<'a> AnalyzeContext<'a> {
10911081
}
10921082
}
10931083

1094-
// @TODO skip operators we do not handle yet
1095-
fn can_handle(op: Operator) -> bool {
1096-
!matches!(
1097-
op,
1098-
Operator::QueEQ
1099-
| Operator::QueNE
1100-
| Operator::QueGT
1101-
| Operator::QueGTE
1102-
| Operator::QueLT
1103-
| Operator::QueLTE
1104-
)
1105-
}
1106-
11071084
impl Diagnostic {
11081085
fn ambiguous_op<'a>(
11091086
pos: &SrcPos,

vhdl_lang/src/analysis/standard.rs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -737,14 +737,37 @@ impl<'a> AnalyzeContext<'a> {
737737
.chain(self.comparators(typ).into_iter())
738738
}
739739

740-
pub fn enum_implicits(&self, typ: TypeEnt<'a>) -> impl Iterator<Item = EntRef<'a>> {
740+
pub fn enum_implicits(
741+
&self,
742+
typ: TypeEnt<'a>,
743+
matching_op: bool,
744+
) -> impl Iterator<Item = EntRef<'a>> {
741745
[
742746
self.create_to_string(typ),
743747
self.minimum(typ),
744748
self.maximum(typ),
745749
]
746750
.into_iter()
747751
.chain(self.comparators(typ).into_iter())
752+
.chain(
753+
if matching_op {
754+
Some(
755+
[
756+
self.symmetric_binary(Operator::QueEQ, typ),
757+
self.symmetric_binary(Operator::QueNE, typ),
758+
self.symmetric_binary(Operator::QueGT, typ),
759+
self.symmetric_binary(Operator::QueGTE, typ),
760+
self.symmetric_binary(Operator::QueLT, typ),
761+
self.symmetric_binary(Operator::QueLTE, typ),
762+
]
763+
.into_iter(),
764+
)
765+
} else {
766+
None
767+
}
768+
.into_iter()
769+
.flatten(),
770+
)
748771
}
749772

750773
pub fn record_implicits(&self, typ: TypeEnt<'a>) -> impl Iterator<Item = EntRef<'a>> {
@@ -787,7 +810,11 @@ impl<'a> AnalyzeContext<'a> {
787810
.into_iter()
788811
}
789812

790-
pub fn array_implicits(&self, typ: TypeEnt<'a>) -> impl Iterator<Item = EntRef<'a>> {
813+
pub fn array_implicits(
814+
&self,
815+
typ: TypeEnt<'a>,
816+
matching_op: bool,
817+
) -> impl Iterator<Item = EntRef<'a>> {
791818
let Type::Array{indexes, elem_type, ..} = typ.kind() else {
792819
unreachable!("Must be array type")
793820
};
@@ -833,6 +860,25 @@ impl<'a> AnalyzeContext<'a> {
833860
.into_iter()
834861
.flatten(),
835862
)
863+
.chain(
864+
if matching_op {
865+
Some(
866+
[
867+
self.binary(Operator::QueEQ, typ, typ, typ, *elem_type),
868+
self.binary(Operator::QueNE, typ, typ, typ, *elem_type),
869+
self.binary(Operator::QueGT, typ, typ, typ, *elem_type),
870+
self.binary(Operator::QueGTE, typ, typ, typ, *elem_type),
871+
self.binary(Operator::QueLT, typ, typ, typ, *elem_type),
872+
self.binary(Operator::QueLTE, typ, typ, typ, *elem_type),
873+
]
874+
.into_iter(),
875+
)
876+
} else {
877+
None
878+
}
879+
.into_iter()
880+
.flatten(),
881+
)
836882
}
837883

838884
pub fn access_implicits(&self, typ: TypeEnt<'a>) -> impl Iterator<Item = EntRef<'a>> {
@@ -1112,10 +1158,11 @@ impl<'a> AnalyzeContext<'a> {
11121158
}
11131159
}
11141160

1115-
// ?? for bit
1161+
// ?? operator for bit
11161162
{
11171163
let typ = self.bit();
11181164
let qq = self.unary(Operator::QueQue, typ, self.boolean());
1165+
11191166
unsafe {
11201167
self.arena.add_implicit(typ.id(), qq);
11211168
};

vhdl_lang/src/analysis/tests/typecheck_expression.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,3 +1649,71 @@ end units;
16491649
],
16501650
);
16511651
}
1652+
1653+
#[test]
1654+
fn scalar_bit_matching_operators() {
1655+
let mut builder = LibraryBuilder::new();
1656+
builder.in_declarative_region(
1657+
"
1658+
constant good1 : bit := '0' ?= '1';
1659+
constant good2 : bit := '0' ?/= '1';
1660+
constant good3 : bit := '0' ?< '1';
1661+
constant good4 : bit := '0' ?<= '1';
1662+
constant good5 : bit := '0' ?> '1';
1663+
constant good6 : bit := '0' ?>= '1';
1664+
",
1665+
);
1666+
1667+
let diagnostics = builder.analyze();
1668+
check_no_diagnostics(&diagnostics);
1669+
}
1670+
1671+
#[test]
1672+
fn bit_vector_matching_operators() {
1673+
let mut builder = LibraryBuilder::new();
1674+
builder.in_declarative_region(
1675+
"
1676+
constant good1 : bit := \"01\" ?= \"10\";
1677+
constant good2 : bit := \"01\" ?/= \"10\";
1678+
constant good3 : bit := \"01\" ?< \"10\";
1679+
constant good4 : bit := \"01\" ?<= \"10\";
1680+
constant good5 : bit := \"01\" ?> \"10\";
1681+
constant good6 : bit := \"01\" ?>= \"10\";
1682+
",
1683+
);
1684+
1685+
let diagnostics = builder.analyze();
1686+
check_no_diagnostics(&diagnostics);
1687+
}
1688+
1689+
#[test]
1690+
fn std_ulogic_matching_operators() {
1691+
let mut builder = LibraryBuilder::new();
1692+
builder.add_std_logic_1164();
1693+
builder.code(
1694+
"libname",
1695+
"
1696+
library ieee;
1697+
use ieee.std_logic_1164.all;
1698+
1699+
package pkg is
1700+
constant good1s : std_ulogic := '0' ?= '1';
1701+
constant good2s : std_ulogic := '0' ?/= '1';
1702+
constant good3s : std_ulogic := '0' ?< '1';
1703+
constant good4s : std_ulogic := '0' ?<= '1';
1704+
constant good5s : std_ulogic := '0' ?> '1';
1705+
constant good6s : std_ulogic := '0' ?>= '1';
1706+
1707+
constant good1v : std_ulogic := \"10\" ?= \"10\";
1708+
constant good2v : std_ulogic := \"10\" ?/= \"10\";
1709+
constant good3v : std_ulogic := \"10\" ?< \"10\";
1710+
constant good4v : std_ulogic := \"10\" ?<= \"10\";
1711+
constant good5v : std_ulogic := \"10\" ?> \"10\";
1712+
constant good6v : std_ulogic := \"10\" ?>= \"10\";
1713+
end package;
1714+
",
1715+
);
1716+
1717+
let diagnostics = builder.analyze();
1718+
check_no_diagnostics(&diagnostics);
1719+
}

vhdl_lang/src/analysis/tests/util.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ end architecture;"
6464
)
6565
}
6666

67+
pub fn add_std_logic_1164(&mut self) {
68+
let std_logic_1164 = self.code_builder.code_from_source(std_logic_1164_package());
69+
self.add_code("ieee", std_logic_1164);
70+
}
71+
6772
pub fn get_analyzed_root(&self) -> (DesignRoot, Vec<Diagnostic>) {
6873
let mut root = DesignRoot::new(self.code_builder.symbols.clone());
6974
let mut diagnostics = Vec::new();
@@ -122,6 +127,16 @@ fn env_package() -> Source {
122127
)
123128
}
124129

130+
fn std_logic_1164_package() -> Source {
131+
Source::inline(
132+
Path::new("std_logic_1164.vhd"),
133+
&Latin1String::new(include_bytes!(
134+
"../../../../vhdl_libraries/ieee2008/std_logic_1164.vhdl"
135+
))
136+
.to_string(),
137+
)
138+
}
139+
125140
pub fn add_standard_library(symbols: Arc<Symbols>, root: &mut DesignRoot) {
126141
let builder = CodeBuilder {
127142
symbols: symbols.clone(),

0 commit comments

Comments
 (0)