Skip to content

Commit

Permalink
Add support for matching operators
Browse files Browse the repository at this point in the history
  • Loading branch information
kraigher committed Feb 7, 2023
1 parent 20d51e2 commit 08e8d87
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 59 deletions.
6 changes: 6 additions & 0 deletions vhdl_lang/src/analysis/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ pub(super) struct AnalyzeContext<'a> {
pub work_sym: Symbol,
std_sym: Symbol,
standard_sym: Symbol,
pub(super) is_std_logic_1164: bool,

// Record dependencies and sensitivies when
// analyzing design units
Expand Down Expand Up @@ -166,6 +167,11 @@ impl<'a> AnalyzeContext<'a> {
work_sym: root.symbol_utf8("work"),
std_sym: root.symbol_utf8("std"),
standard_sym: root.symbol_utf8("standard"),
is_std_logic_1164: current_unit
== &UnitId::package(
&root.symbol_utf8("ieee"),
&root.symbol_utf8("std_logic_1164"),
),
root,
current_unit: current_unit.clone(),
arena,
Expand Down
28 changes: 26 additions & 2 deletions vhdl_lang/src/analysis/declarative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ impl<'a> AnalyzeContext<'a> {

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

for ent in self.enum_implicits(enum_type) {
for ent in self.enum_implicits(enum_type, self.has_matching_op(enum_type)) {
unsafe {
self.arena.add_implicit(enum_type.id(), ent);
}
Expand Down Expand Up @@ -766,6 +766,7 @@ impl<'a> AnalyzeContext<'a> {
}
};

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

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

for ent in self.array_implicits(array_ent) {
for ent in self.array_implicits(array_ent, is_1d && self.has_matching_op(elem_type))
{
unsafe {
self.arena.add_implicit(array_ent.id(), ent);
}
Expand Down Expand Up @@ -928,6 +930,28 @@ impl<'a> AnalyzeContext<'a> {
Ok(())
}

/// The matching operators such as ?= are defined for 1d arrays of bit and std_ulogic element type
fn has_matching_op(&self, typ: TypeEnt<'a>) -> bool {
if self.is_std_logic_1164 {
// Within the std_logic_1164 we do not have efficient access to the types
typ.designator() == &Designator::Identifier(self.root.symbol_utf8("std_ulogic"))
} else {
if let Some(ref standard_types) = self.root.standard_types {
if typ.id() == standard_types.bit {
return true;
}
}

if let Some(id) = self.root.std_ulogic {
if typ.id() == id {
return true;
}
}

false
}
}

pub fn resolve_signature(
&self,
scope: &Scope<'a>,
Expand Down
85 changes: 31 additions & 54 deletions vhdl_lang/src/analysis/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,6 @@ impl<'a> AnalyzeContext<'a> {
exprs: &mut [&mut WithPos<Expression>],
diagnostics: &mut dyn DiagnosticHandler,
) -> EvalResult<ExpressionType<'a>> {
if !can_handle(op.item.item) {
return Err(EvalError::Unknown);
}

let op_candidates = match self.lookup_operator(scope, &op.pos, op.item.item, exprs.len()) {
Ok(candidates) => candidates,
Err(err) => {
Expand Down Expand Up @@ -750,47 +746,41 @@ impl<'a> AnalyzeContext<'a> {
}
}
Expression::Binary(ref mut op, ref mut left, ref mut right) => {
if can_handle(op.item.item) {
let op_candidates = match self.lookup_operator(scope, &op.pos, op.item.item, 2)
{
Ok(candidates) => candidates,
Err(err) => {
diagnostics.push(err.into_non_fatal()?);
return Ok(());
}
};
let op_candidates = match self.lookup_operator(scope, &op.pos, op.item.item, 2) {
Ok(candidates) => candidates,
Err(err) => {
diagnostics.push(err.into_non_fatal()?);
return Ok(());
}
};

match as_fatal(self.disambiguate_op(
scope,
Some(target_type),
op,
op_candidates,
&mut [left.as_mut(), right.as_mut()],
diagnostics,
))? {
Some(Disambiguated::Unambiguous(overloaded)) => {
let op_type = overloaded.return_type().unwrap();

if !self.can_be_target_type(op_type, target_type.base()) {
diagnostics.push(Diagnostic::type_mismatch(
expr_pos,
&op_type.describe(),
target_type,
));
}
}
Some(Disambiguated::Ambiguous(candidates)) => {
diagnostics.push(Diagnostic::ambiguous_op(
&op.pos,
op.item.item,
candidates,
match as_fatal(self.disambiguate_op(
scope,
Some(target_type),
op,
op_candidates,
&mut [left.as_mut(), right.as_mut()],
diagnostics,
))? {
Some(Disambiguated::Unambiguous(overloaded)) => {
let op_type = overloaded.return_type().unwrap();

if !self.can_be_target_type(op_type, target_type.base()) {
diagnostics.push(Diagnostic::type_mismatch(
expr_pos,
&op_type.describe(),
target_type,
));
}
None => {}
}
} else {
self.expr_unknown_ttyp(scope, left, diagnostics)?;
self.expr_unknown_ttyp(scope, right, diagnostics)?;
Some(Disambiguated::Ambiguous(candidates)) => {
diagnostics.push(Diagnostic::ambiguous_op(
&op.pos,
op.item.item,
candidates,
));
}
None => {}
}
}
Expression::Unary(ref mut op, ref mut expr) => {
Expand Down Expand Up @@ -1091,19 +1081,6 @@ impl<'a> AnalyzeContext<'a> {
}
}

// @TODO skip operators we do not handle yet
fn can_handle(op: Operator) -> bool {
!matches!(
op,
Operator::QueEQ
| Operator::QueNE
| Operator::QueGT
| Operator::QueGTE
| Operator::QueLT
| Operator::QueLTE
)
}

impl Diagnostic {
fn ambiguous_op<'a>(
pos: &SrcPos,
Expand Down
53 changes: 50 additions & 3 deletions vhdl_lang/src/analysis/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,14 +737,37 @@ impl<'a> AnalyzeContext<'a> {
.chain(self.comparators(typ).into_iter())
}

pub fn enum_implicits(&self, typ: TypeEnt<'a>) -> impl Iterator<Item = EntRef<'a>> {
pub fn enum_implicits(
&self,
typ: TypeEnt<'a>,
matching_op: bool,
) -> impl Iterator<Item = EntRef<'a>> {
[
self.create_to_string(typ),
self.minimum(typ),
self.maximum(typ),
]
.into_iter()
.chain(self.comparators(typ).into_iter())
.chain(
if matching_op {
Some(
[
self.symmetric_binary(Operator::QueEQ, typ),
self.symmetric_binary(Operator::QueNE, typ),
self.symmetric_binary(Operator::QueGT, typ),
self.symmetric_binary(Operator::QueGTE, typ),
self.symmetric_binary(Operator::QueLT, typ),
self.symmetric_binary(Operator::QueLTE, typ),
]
.into_iter(),
)
} else {
None
}
.into_iter()
.flatten(),
)
}

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

pub fn array_implicits(&self, typ: TypeEnt<'a>) -> impl Iterator<Item = EntRef<'a>> {
pub fn array_implicits(
&self,
typ: TypeEnt<'a>,
matching_op: bool,
) -> impl Iterator<Item = EntRef<'a>> {
let Type::Array{indexes, elem_type, ..} = typ.kind() else {
unreachable!("Must be array type")
};
Expand Down Expand Up @@ -833,6 +860,25 @@ impl<'a> AnalyzeContext<'a> {
.into_iter()
.flatten(),
)
.chain(
if matching_op {
Some(
[
self.binary(Operator::QueEQ, typ, typ, typ, *elem_type),
self.binary(Operator::QueNE, typ, typ, typ, *elem_type),
self.binary(Operator::QueGT, typ, typ, typ, *elem_type),
self.binary(Operator::QueGTE, typ, typ, typ, *elem_type),
self.binary(Operator::QueLT, typ, typ, typ, *elem_type),
self.binary(Operator::QueLTE, typ, typ, typ, *elem_type),
]
.into_iter(),
)
} else {
None
}
.into_iter()
.flatten(),
)
}

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

// ?? for bit
// ?? operator for bit
{
let typ = self.bit();
let qq = self.unary(Operator::QueQue, typ, self.boolean());

unsafe {
self.arena.add_implicit(typ.id(), qq);
};
Expand Down
68 changes: 68 additions & 0 deletions vhdl_lang/src/analysis/tests/typecheck_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1649,3 +1649,71 @@ end units;
],
);
}

#[test]
fn scalar_bit_matching_operators() {
let mut builder = LibraryBuilder::new();
builder.in_declarative_region(
"
constant good1 : bit := '0' ?= '1';
constant good2 : bit := '0' ?/= '1';
constant good3 : bit := '0' ?< '1';
constant good4 : bit := '0' ?<= '1';
constant good5 : bit := '0' ?> '1';
constant good6 : bit := '0' ?>= '1';
",
);

let diagnostics = builder.analyze();
check_no_diagnostics(&diagnostics);
}

#[test]
fn bit_vector_matching_operators() {
let mut builder = LibraryBuilder::new();
builder.in_declarative_region(
"
constant good1 : bit := \"01\" ?= \"10\";
constant good2 : bit := \"01\" ?/= \"10\";
constant good3 : bit := \"01\" ?< \"10\";
constant good4 : bit := \"01\" ?<= \"10\";
constant good5 : bit := \"01\" ?> \"10\";
constant good6 : bit := \"01\" ?>= \"10\";
",
);

let diagnostics = builder.analyze();
check_no_diagnostics(&diagnostics);
}

#[test]
fn std_ulogic_matching_operators() {
let mut builder = LibraryBuilder::new();
builder.add_std_logic_1164();
builder.code(
"libname",
"
library ieee;
use ieee.std_logic_1164.all;
package pkg is
constant good1s : std_ulogic := '0' ?= '1';
constant good2s : std_ulogic := '0' ?/= '1';
constant good3s : std_ulogic := '0' ?< '1';
constant good4s : std_ulogic := '0' ?<= '1';
constant good5s : std_ulogic := '0' ?> '1';
constant good6s : std_ulogic := '0' ?>= '1';
constant good1v : std_ulogic := \"10\" ?= \"10\";
constant good2v : std_ulogic := \"10\" ?/= \"10\";
constant good3v : std_ulogic := \"10\" ?< \"10\";
constant good4v : std_ulogic := \"10\" ?<= \"10\";
constant good5v : std_ulogic := \"10\" ?> \"10\";
constant good6v : std_ulogic := \"10\" ?>= \"10\";
end package;
",
);

let diagnostics = builder.analyze();
check_no_diagnostics(&diagnostics);
}
15 changes: 15 additions & 0 deletions vhdl_lang/src/analysis/tests/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ end architecture;"
)
}

pub fn add_std_logic_1164(&mut self) {
let std_logic_1164 = self.code_builder.code_from_source(std_logic_1164_package());
self.add_code("ieee", std_logic_1164);
}

pub fn get_analyzed_root(&self) -> (DesignRoot, Vec<Diagnostic>) {
let mut root = DesignRoot::new(self.code_builder.symbols.clone());
let mut diagnostics = Vec::new();
Expand Down Expand Up @@ -122,6 +127,16 @@ fn env_package() -> Source {
)
}

fn std_logic_1164_package() -> Source {
Source::inline(
Path::new("std_logic_1164.vhd"),
&Latin1String::new(include_bytes!(
"../../../../vhdl_libraries/ieee2008/std_logic_1164.vhdl"
))
.to_string(),
)
}

pub fn add_standard_library(symbols: Arc<Symbols>, root: &mut DesignRoot) {
let builder = CodeBuilder {
symbols: symbols.clone(),
Expand Down

0 comments on commit 08e8d87

Please sign in to comment.