Skip to content

Commit 5c78bd1

Browse files
committed
Continue analyzing port/generic map in case of missing associations. Closes #213
1 parent fda048b commit 5c78bd1

File tree

5 files changed

+182
-104
lines changed

5 files changed

+182
-104
lines changed

vhdl_lang/src/analysis/association.rs

Lines changed: 113 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#![allow(clippy::only_used_in_recursion)]
88

99
use fnv::FnvHashMap;
10-
use fnv::FnvHashSet;
10+
use itertools::Itertools;
1111

1212
use super::analyze::*;
1313
use super::names::ResolvedName;
@@ -297,147 +297,169 @@ impl<'a> AnalyzeContext<'a> {
297297
}
298298
}
299299

300-
pub fn resolve_association_formals<'e>(
300+
fn combine_formal_with_actuals<'e>(
301301
&self,
302-
error_pos: &SrcPos, // The position of the instance/call-site
303302
formal_region: &FormalRegion<'a>,
304303
scope: &Scope<'a>,
305304
elems: &'e mut [AssociationElement],
306305
diagnostics: &mut dyn DiagnosticHandler,
307-
) -> EvalResult<Vec<TypeEnt<'a>>> {
306+
) -> EvalResult<Vec<(&'e SrcPos, Result<ResolvedFormal<'a>, Diagnostic>)>> {
308307
self.check_positional_before_named(elems, diagnostics)?;
309308

310309
// Formal region index => actual position, resolved formal
311-
let mut result: Vec<(&SrcPos, ResolvedFormal)> = Vec::default();
312-
313-
let mut missing = false;
314-
let mut extra_associations: Vec<SrcPos> = Default::default();
310+
let mut result: Vec<(&SrcPos, Result<ResolvedFormal, Diagnostic>)> = Vec::default();
315311

316312
for (actual_idx, AssociationElement { formal, actual }) in elems.iter_mut().enumerate() {
317313
if let Some(ref mut formal) = formal {
318314
// Named argument
319-
match self.resolve_formal(
315+
let resolved_formal = match self.resolve_formal(
320316
formal_region,
321317
scope,
322318
&formal.pos,
323319
&mut formal.item,
324320
diagnostics,
325321
) {
326-
Err(err) => {
327-
missing = true;
328-
diagnostics.push(err.into_non_fatal()?);
329-
}
330-
Ok(resolved_formal) => {
331-
result.push((&formal.pos, resolved_formal));
332-
}
333-
}
322+
Err(err) => Err(err.into_non_fatal()?),
323+
Ok(resolved_formal) => Ok(resolved_formal),
324+
};
325+
326+
result.push((&formal.pos, resolved_formal));
334327
} else if let Some(formal) = formal_region.nth(actual_idx) {
335328
// Actual index is same as formal index for positional argument
336329
let formal = ResolvedFormal::new_basic(actual_idx, formal);
337-
result.push((&actual.pos, formal));
330+
result.push((&actual.pos, Ok(formal)));
338331
} else {
339-
extra_associations.push(actual.pos.clone());
332+
result.push((
333+
&actual.pos,
334+
Err(Diagnostic::error(&actual.pos, "Unexpected extra argument")),
335+
));
340336
};
341337
}
338+
Ok(result)
339+
}
340+
341+
fn check_missing_and_duplicates<'e>(
342+
&self,
343+
error_pos: &SrcPos, // The position of the instance/call-site
344+
resolved_pairs: &[(&'e SrcPos, Result<ResolvedFormal<'a>, Diagnostic>)],
345+
formal_region: &FormalRegion<'a>,
346+
diagnostics: &mut dyn DiagnosticHandler,
347+
) -> EvalResult<Vec<TypeEnt<'a>>> {
348+
let mut is_error = false;
349+
let mut result = Vec::default();
350+
351+
let mut associated: FnvHashMap<usize, (&SrcPos, ResolvedFormal)> = Default::default();
352+
for (actual_pos, resolved_formal) in resolved_pairs.iter() {
353+
match resolved_formal {
354+
Ok(resolved_formal) => {
355+
if let Some((prev_pos, prev_formal)) = associated.get(&resolved_formal.idx) {
356+
if !(resolved_formal.is_partial && prev_formal.is_partial) {
357+
let mut diag = Diagnostic::error(
358+
actual_pos,
359+
format!(
360+
"{} has already been associated",
361+
resolved_formal.iface.describe()
362+
),
363+
);
342364

343-
let mut not_associated = Vec::new();
344-
let mut has_duplicates = false;
365+
diag.add_related(prev_pos, "Previously associated here");
366+
is_error = true;
367+
diagnostics.push(diag);
368+
}
369+
}
370+
result.push(resolved_formal.type_mark);
371+
associated.insert(resolved_formal.idx, (actual_pos, *resolved_formal));
372+
}
373+
Err(diagnostic) => {
374+
is_error = true;
375+
diagnostics.push(diagnostic.clone());
376+
}
377+
}
378+
}
345379

346-
{
347-
let associated_indexes =
348-
FnvHashSet::from_iter(result.iter().map(|(_, formal)| formal.idx));
349-
for (idx, formal) in formal_region.iter().enumerate() {
350-
if !(associated_indexes.contains(&idx)
380+
for (idx, formal) in formal_region.iter().enumerate() {
381+
if !(associated.contains_key(&idx)
351382
// Default may be unconnected
352383
|| formal.has_default()
353384
// Output ports are allowed to be unconnected
354385
|| (formal_region.typ == InterfaceType::Port && formal.is_out_or_inout_signal()))
355-
{
356-
not_associated.push(idx);
357-
}
358-
}
359-
}
386+
{
387+
let mut diagnostic = Diagnostic::error(
388+
error_pos,
389+
format!("No association of {}", formal.describe()),
390+
);
360391

361-
{
362-
let mut seen: FnvHashMap<usize, (&SrcPos, ResolvedFormal)> = Default::default();
363-
for (actual_pos, resolved_formal) in result.iter() {
364-
if let Some((prev_pos, prev_formal)) = seen.get(&resolved_formal.idx) {
365-
if !(resolved_formal.is_partial && prev_formal.is_partial) {
366-
let mut diag = Diagnostic::error(
367-
actual_pos,
368-
format!(
369-
"{} has already been associated",
370-
resolved_formal.iface.describe()
371-
),
372-
);
373-
374-
diag.add_related(prev_pos, "Previously associated here");
375-
diagnostics.push(diag);
376-
has_duplicates = true;
377-
}
392+
if let Some(decl_pos) = formal.decl_pos() {
393+
diagnostic.add_related(decl_pos, "Defined here");
378394
}
379-
seen.insert(resolved_formal.idx, (*actual_pos, *resolved_formal));
395+
396+
is_error = true;
397+
diagnostics.push(diagnostic);
380398
}
381399
}
382400

383-
if not_associated.is_empty() && extra_associations.is_empty() && !missing && !has_duplicates
384-
{
385-
Ok(result
386-
.into_iter()
387-
.map(|(_, formal)| formal.type_mark)
388-
.collect())
401+
if !is_error {
402+
Ok(result)
389403
} else {
390-
// Only complain if nothing else is wrong
391-
for idx in not_associated {
392-
if let Some(formal) = formal_region.nth(idx) {
393-
let mut diagnostic = Diagnostic::error(
394-
error_pos,
395-
format!("No association of {}", formal.describe()),
396-
);
397-
398-
if let Some(decl_pos) = formal.decl_pos() {
399-
diagnostic.add_related(decl_pos, "Defined here");
400-
}
401-
402-
diagnostics.push(diagnostic);
403-
}
404-
}
405-
for pos in extra_associations.into_iter() {
406-
diagnostics.error(pos, "Unexpected extra argument")
407-
}
408-
409404
Err(EvalError::Unknown)
410405
}
411406
}
412407

413-
pub fn analyze_assoc_elems_with_formal_region(
408+
pub fn resolve_association_formals<'e>(
409+
&self,
410+
error_pos: &SrcPos, // The position of the instance/call-site
411+
formal_region: &FormalRegion<'a>,
412+
scope: &Scope<'a>,
413+
elems: &'e mut [AssociationElement],
414+
diagnostics: &mut dyn DiagnosticHandler,
415+
) -> EvalResult<Vec<TypeEnt<'a>>> {
416+
let resolved_pairs =
417+
self.combine_formal_with_actuals(formal_region, scope, elems, diagnostics)?;
418+
self.check_missing_and_duplicates(error_pos, &resolved_pairs, formal_region, diagnostics)
419+
}
420+
421+
pub fn check_association<'e>(
414422
&self,
415423
error_pos: &SrcPos, // The position of the instance/call-site
416424
formal_region: &FormalRegion<'a>,
417425
scope: &Scope<'a>,
418-
elems: &mut [AssociationElement],
426+
elems: &'e mut [AssociationElement],
419427
diagnostics: &mut dyn DiagnosticHandler,
420428
) -> FatalResult {
421-
if let Some(formals) = as_fatal(self.resolve_association_formals(
422-
error_pos,
423-
formal_region,
424-
scope,
425-
elems,
426-
diagnostics,
427-
))? {
428-
for (formal_type, actual) in formals
429+
let resolved_pairs =
430+
as_fatal(self.combine_formal_with_actuals(formal_region, scope, elems, diagnostics))?;
431+
432+
if let Some(resolved_pairs) = resolved_pairs {
433+
as_fatal(self.check_missing_and_duplicates(
434+
error_pos,
435+
&resolved_pairs,
436+
formal_region,
437+
diagnostics,
438+
))?;
439+
440+
let resolved_formals = resolved_pairs
441+
.into_iter()
442+
.map(|(_, resolved_formal)| resolved_formal)
443+
.collect_vec();
444+
445+
for (resolved_formal, actual) in resolved_formals
429446
.iter()
430447
.zip(elems.iter_mut().map(|assoc| &mut assoc.actual))
431448
{
432449
match &mut actual.item {
433450
ActualPart::Expression(expr) => {
434-
self.expr_pos_with_ttyp(
435-
scope,
436-
*formal_type,
437-
&actual.pos,
438-
expr,
439-
diagnostics,
440-
)?;
451+
if let Ok(resolved_formal) = resolved_formal {
452+
// Error case is already checked in check_missing_and_duplicates
453+
self.expr_pos_with_ttyp(
454+
scope,
455+
resolved_formal.type_mark,
456+
&actual.pos,
457+
expr,
458+
diagnostics,
459+
)?;
460+
} else {
461+
self.expr_pos_unknown_ttyp(scope, &actual.pos, expr, diagnostics)?;
462+
}
441463
}
442464
ActualPart::Open => {}
443465
}

vhdl_lang/src/analysis/concurrent.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ impl<'a> AnalyzeContext<'a> {
293293

294294
let (generic_region, port_region) = ent_region.to_entity_formal();
295295

296-
self.analyze_assoc_elems_with_formal_region(
296+
self.check_association(
297297
&entity_name.pos,
298298
&generic_region,
299299
scope,
@@ -304,7 +304,7 @@ impl<'a> AnalyzeContext<'a> {
304304
.unwrap_or(&mut []),
305305
diagnostics,
306306
)?;
307-
self.analyze_assoc_elems_with_formal_region(
307+
self.check_association(
308308
&entity_name.pos,
309309
&port_region,
310310
scope,
@@ -339,7 +339,7 @@ impl<'a> AnalyzeContext<'a> {
339339

340340
if let AnyEntKind::Component(ent_region) = ent.kind() {
341341
let (generic_region, port_region) = ent_region.to_entity_formal();
342-
self.analyze_assoc_elems_with_formal_region(
342+
self.check_association(
343343
&component_name.pos,
344344
&generic_region,
345345
scope,
@@ -350,7 +350,7 @@ impl<'a> AnalyzeContext<'a> {
350350
.unwrap_or(&mut []),
351351
diagnostics,
352352
)?;
353-
self.analyze_assoc_elems_with_formal_region(
353+
self.check_association(
354354
&component_name.pos,
355355
&port_region,
356356
scope,

vhdl_lang/src/analysis/overloaded.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,7 @@ impl<'a> AnalyzeContext<'a> {
191191
assocs: &mut [AssociationElement],
192192
diagnostics: &mut dyn DiagnosticHandler,
193193
) -> FatalResult {
194-
self.analyze_assoc_elems_with_formal_region(
195-
error_pos,
196-
ent.formals(),
197-
scope,
198-
assocs,
199-
diagnostics,
200-
)?;
194+
self.check_association(error_pos, ent.formals(), scope, assocs, diagnostics)?;
201195
Ok(())
202196
}
203197

vhdl_lang/src/analysis/semantic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,14 @@ impl<'a> AnalyzeContext<'a> {
204204
if let AnyEntKind::Component(region) = ent.kind() {
205205
name.set_unique_reference(ent);
206206
let (generic_region, port_region) = region.to_entity_formal();
207-
self.analyze_assoc_elems_with_formal_region(
207+
self.check_association(
208208
&fcall.item.name.pos,
209209
&generic_region,
210210
scope,
211211
&mut [],
212212
diagnostics,
213213
)?;
214-
self.analyze_assoc_elems_with_formal_region(
214+
self.check_association(
215215
&fcall.item.name.pos,
216216
&port_region,
217217
scope,

0 commit comments

Comments
 (0)