Skip to content

Commit

Permalink
Fix: discrete ranges can be used as choice statements
Browse files Browse the repository at this point in the history
  • Loading branch information
Schottkyc137 committed Dec 5, 2024
1 parent 5cf0e4c commit 77f31d5
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 61 deletions.
4 changes: 2 additions & 2 deletions vhdl_lang/src/analysis/assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
} in alternatives.iter_mut()
{
self.analyze_expression_for_target(scope, ttyp, item, diagnostics)?;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.choices_with_ttyp(scope, ctyp, choices, diagnostics)?;
}
}
}
Expand Down Expand Up @@ -105,7 +105,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
} in alternatives.iter_mut()
{
self.analyze_waveform(scope, ttyp, item, diagnostics)?;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.choices_with_ttyp(scope, ctyp, choices, diagnostics)?;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion vhdl_lang/src/analysis/concurrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
ref mut item,
span: _,
} = alternative;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.choices_with_ttyp(scope, ctyp, choices, diagnostics)?;
let nested = scope.nested();
self.analyze_generate_body(&nested, parent, item, src_span, diagnostics)?;
}
Expand Down
3 changes: 3 additions & 0 deletions vhdl_lang/src/analysis/declarative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
return Err(EvalError::Unknown);
}
}
ResolvedName::Range(_) => {
return Err(EvalError::Unknown);
}
}
};

Expand Down
4 changes: 2 additions & 2 deletions vhdl_lang/src/analysis/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
"Ambiguous use of implicit boolean conversion ??",
ErrorCode::AmbiguousCall,
);
diag.add_type_candididates("Could be", implicit_bool_types);
diag.add_type_candidates("Could be", implicit_bool_types);
diagnostics.push(diag);
}

Expand All @@ -697,7 +697,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
),
ErrorCode::AmbiguousExpression,
);
diag.add_type_candididates(
diag.add_type_candidates(
"Implicit boolean conversion operator ?? is not defined for",
types,
);
Expand Down
104 changes: 65 additions & 39 deletions vhdl_lang/src/analysis/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ pub enum ResolvedName<'a> {
Expression(DisambiguatedType<'a>),
// Something that cannot be further selected
Final(EntRef<'a>),
Range(TypeEnt<'a>),
}

impl<'a> ResolvedName<'a> {
Expand Down Expand Up @@ -307,6 +308,7 @@ impl<'a> ResolvedName<'a> {
ResolvedName::Final(ent) => ent.describe(),
ResolvedName::Expression(DisambiguatedType::Unambiguous(_)) => "Expression".to_owned(),
ResolvedName::Expression(_) => "Ambiguous expression".to_owned(),
ResolvedName::Range(_) => "Range".to_owned(),
}
}

Expand All @@ -324,6 +326,7 @@ impl<'a> ResolvedName<'a> {
ObjectBase::ExternalName(_) => None,
},
ResolvedName::Expression(_) | ResolvedName::Final(_) => None,
ResolvedName::Range(typ) => typ.decl_pos(),
}
}

Expand Down Expand Up @@ -395,6 +398,7 @@ impl<'a> ResolvedName<'a> {
ResolvedName::Overloaded(_, _) => None,
ResolvedName::Expression(_) => None,
ResolvedName::Final(_) => None,
ResolvedName::Range(typ) => Some((*typ).into()),
}
}
}
Expand Down Expand Up @@ -478,12 +482,13 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
name: ResolvedName<'a>,
) -> Result<Option<DisambiguatedType<'a>>, Diagnostic> {
match name {
ResolvedName::Library(_) | ResolvedName::Design(_) | ResolvedName::Type(_) => {
Err(Diagnostic::mismatched_kinds(
pos.pos(self.ctx),
format!("{} cannot be used in an expression", name.describe_type()),
))
}
ResolvedName::Library(_)
| ResolvedName::Design(_)
| ResolvedName::Type(_)
| ResolvedName::Range(_) => Err(Diagnostic::mismatched_kinds(
pos.pos(self.ctx),
format!("{} cannot be used in an expression", name.describe_type()),
)),
ResolvedName::Final(ent) => match ent.actual_kind() {
AnyEntKind::LoopParameter(typ) => {
Ok(typ.map(|typ| DisambiguatedType::Unambiguous(typ.into())))
Expand Down Expand Up @@ -517,7 +522,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
}
}

fn name_to_unambiguous_type(
pub(crate) fn name_to_unambiguous_type(
&self,
span: TokenSpan,
name: &ResolvedName<'a>,
Expand All @@ -526,12 +531,13 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
suffix_ref: Option<&mut Reference>,
) -> Result<Option<TypeEnt<'a>>, Diagnostic> {
match name {
ResolvedName::Library(_) | ResolvedName::Design(_) | ResolvedName::Type(_) => {
Err(Diagnostic::mismatched_kinds(
span.pos(self.ctx),
format!("{} cannot be used in an expression", name.describe_type()),
))
}
ResolvedName::Library(_)
| ResolvedName::Design(_)
| ResolvedName::Type(_)
| ResolvedName::Range(_) => Err(Diagnostic::mismatched_kinds(
span.pos(self.ctx),
format!("{} cannot be used in an expression", name.describe_type()),
)),
ResolvedName::Final(ent) => match ent.actual_kind() {
AnyEntKind::LoopParameter(typ) => Ok(typ.map(|typ| typ.into())),
AnyEntKind::PhysicalLiteral(typ) => Ok(Some(*typ)),
Expand Down Expand Up @@ -1067,14 +1073,17 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
Err(EvalError::Unknown)
}
}
AttributeDesignator::Range(_) => {
diagnostics.add(
name_pos.pos(self.ctx),
"Range cannot be used as an expression",
ErrorCode::MismatchedKinds,
);
Err(EvalError::Unknown)
}
AttributeDesignator::Range(_) => match prefix {
ResolvedName::Type(typ) => Ok(ResolvedName::Range(*typ)),
_ => {
diagnostics.add(
name_pos.pos(self.ctx),
format!("Range attribute cannot be used on {}", prefix.describe()),
ErrorCode::MismatchedKinds,
);
Err(EvalError::Unknown)
}
},
AttributeDesignator::Type(attr) => self
.resolve_type_attribute_suffix(prefix, prefix_pos, &attr, name_pos, diagnostics)
.map(|typ| ResolvedName::Type(typ.base().into())),
Expand Down Expand Up @@ -1526,6 +1535,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
return Err(EvalError::Unknown);
}
}
ResolvedName::Range(_) => {
bail!(
diagnostics,
Diagnostic::cannot_be_prefix(&span.pos(self.ctx), resolved, suffix)
);
}
ResolvedName::Type(typ) => match suffix {
Suffix::Selected(selected) => {
let typed_selection = match typ.selected(self.ctx, prefix.span, selected) {
Expand Down Expand Up @@ -1593,7 +1608,8 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
| ResolvedName::Type(_)
| ResolvedName::Overloaded { .. }
| ResolvedName::Expression(_)
| ResolvedName::Final(_) => {
| ResolvedName::Final(_)
| ResolvedName::Range(_) => {
diagnostics.add(
name_pos.pos(self.ctx),
format!("{} {}", resolved.describe(), err_msg),
Expand All @@ -1619,6 +1635,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
| ResolvedName::ObjectName(_)
| ResolvedName::Overloaded { .. }
| ResolvedName::Expression(_)
| ResolvedName::Range(_)
| ResolvedName::Final(_) => {
bail!(
diagnostics,
Expand Down Expand Up @@ -1708,25 +1725,34 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
false,
diagnostics,
))? {
// @TODO target_type already used above, functions could probably be simplified
match self.name_to_unambiguous_type(span, &resolved, ttyp, name.suffix_reference_mut())
{
Ok(Some(type_mark)) => {
if !self.can_be_target_type(type_mark, ttyp.base()) {
diagnostics.push(Diagnostic::type_mismatch(
&span.pos(self.ctx),
&resolved.describe_type(),
ttyp,
));
}
}
Ok(None) => {}
Err(diag) => {
diagnostics.push(diag);
self.check_resolved_name_type(span, &resolved, ttyp, name, diagnostics);
}
Ok(())
}

pub fn check_resolved_name_type(
&self,
span: TokenSpan,
resolved: &ResolvedName<'a>,
ttyp: TypeEnt<'a>,
name: &mut Name,
diagnostics: &mut dyn DiagnosticHandler,
) {
match self.name_to_unambiguous_type(span, &resolved, ttyp, name.suffix_reference_mut()) {
Ok(Some(type_mark)) => {
if !self.can_be_target_type(type_mark, ttyp.base()) {
diagnostics.push(Diagnostic::type_mismatch(
&span.pos(self.ctx),
&resolved.describe_type(),
ttyp,
));
}
}
Ok(None) => {}
Err(diag) => {
diagnostics.push(diag);
}
}
Ok(())
}

/// Analyze an indexed name where the prefix entity is already known
Expand Down Expand Up @@ -3093,7 +3119,7 @@ variable thevar : integer_vector(0 to 1);
diagnostics,
vec![Diagnostic::new(
code,
"Range cannot be used as an expression",
"Range attribute cannot be used on variable 'thevar'",
ErrorCode::MismatchedKinds,
)],
)
Expand Down
2 changes: 1 addition & 1 deletion vhdl_lang/src/analysis/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
let resolved =
self.name_resolve(scope, attr.name.span, &mut attr.name.item, diagnostics)?;
let typ = match resolved {
ResolvedName::Type(typ) => typ,
ResolvedName::Type(typ) | ResolvedName::Range(typ) => typ,
ResolvedName::ObjectName(oname) => oname.type_mark(),
ResolvedName::Overloaded(ref des, ref overloaded) => {
let disamb = self
Expand Down
91 changes: 76 additions & 15 deletions vhdl_lang/src/analysis/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,92 @@ use crate::data::*;
use crate::named_entity::*;

impl<'a, 't> AnalyzeContext<'a, 't> {
pub fn choice_with_ttyp(
pub fn choices_with_ttyp(
&self,
scope: &Scope<'a>,
ttyp: Option<TypeEnt<'a>>,
choices: &mut [WithTokenSpan<Choice>],
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
for choice in choices.iter_mut() {
match choice.item {
Choice::Expression(ref mut expr) => {
if let Some(ttyp) = ttyp {
self.expr_pos_with_ttyp(scope, ttyp, choice.span, expr, diagnostics)?;
} else {
self.expr_pos_unknown_ttyp(scope, choice.span, expr, diagnostics)?;
self.choice_with_ttyp(scope, ttyp, choice, diagnostics)?;
}
Ok(())
}

fn check_resolved_name<T>(
&self,
ttyp: Option<TypeEnt<'a>>,
typ: TypeEnt<'a>,
diagnostics: &mut dyn DiagnosticHandler,
pos: &WithTokenSpan<T>,
) {
if let Some(ttyp) = ttyp {
if !self.can_be_target_type(typ, ttyp.base()) {
diagnostics.push(Diagnostic::type_mismatch(
&pos.pos(self.ctx),
&typ.describe(),
ttyp,
));
}
}
}

pub fn choice_with_ttyp(
&self,
scope: &Scope<'a>,
ttyp: Option<TypeEnt<'a>>,
choice: &mut WithTokenSpan<Choice>,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
match choice.item {
Choice::Expression(ref mut expr) => {
if let Expression::Name(name) = expr {
if let Some(resolved_name) =
as_fatal(self.name_resolve(scope, choice.span, name, diagnostics))?
{
match resolved_name {
ResolvedName::Range(typ) => {
self.check_resolved_name(ttyp, typ, diagnostics, choice)
}
ResolvedName::Type(typ) => {
if !matches!(typ.kind(), Type::Subtype(_)) {
diagnostics.add(
choice.pos(self.ctx),
format!("{} must be a subtype", typ.describe(),),
ErrorCode::MismatchedKinds,
)
} else {
self.check_resolved_name(ttyp, typ, diagnostics, choice)
}
}
_ => {
if let Some(ttyp) = ttyp {
self.check_resolved_name_type(
choice.span,
&resolved_name,
ttyp,
name,
diagnostics,
);
}
}
}
}
} else if let Some(ttyp) = ttyp {
self.expr_pos_with_ttyp(scope, ttyp, choice.span, expr, diagnostics)?;
} else {
self.expr_pos_unknown_ttyp(scope, choice.span, expr, diagnostics)?;
}
Choice::DiscreteRange(ref mut drange) => {
if let Some(ttyp) = ttyp {
self.drange_with_ttyp(scope, ttyp, drange, diagnostics)?;
} else {
self.drange_unknown_type(scope, drange, diagnostics)?;
}
}
Choice::DiscreteRange(ref mut drange) => {
if let Some(ttyp) = ttyp {
self.drange_with_ttyp(scope, ttyp, drange, diagnostics)?;
} else {
self.drange_unknown_type(scope, drange, diagnostics)?;
}
Choice::Others => {}
}
Choice::Others => {}
}
Ok(())
}
Expand Down Expand Up @@ -180,7 +241,7 @@ impl Diagnostic {
}
}

pub fn add_type_candididates<'a>(
pub fn add_type_candidates<'a>(
&mut self,
prefix: &str,
candidates: impl IntoIterator<Item = BaseType<'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 @@ -252,7 +252,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
item,
span: _,
} = alternative;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.choices_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.analyze_sequential_part(scope, parent, item, diagnostics)?;
}
}
Expand Down
Loading

0 comments on commit 77f31d5

Please sign in to comment.