|
7 | 7 | #![allow(clippy::only_used_in_recursion)] |
8 | 8 |
|
9 | 9 | use fnv::FnvHashMap; |
10 | | -use fnv::FnvHashSet; |
| 10 | +use itertools::Itertools; |
11 | 11 |
|
12 | 12 | use super::analyze::*; |
13 | 13 | use super::names::ResolvedName; |
@@ -297,147 +297,169 @@ impl<'a> AnalyzeContext<'a> { |
297 | 297 | } |
298 | 298 | } |
299 | 299 |
|
300 | | - pub fn resolve_association_formals<'e>( |
| 300 | + fn combine_formal_with_actuals<'e>( |
301 | 301 | &self, |
302 | | - error_pos: &SrcPos, // The position of the instance/call-site |
303 | 302 | formal_region: &FormalRegion<'a>, |
304 | 303 | scope: &Scope<'a>, |
305 | 304 | elems: &'e mut [AssociationElement], |
306 | 305 | diagnostics: &mut dyn DiagnosticHandler, |
307 | | - ) -> EvalResult<Vec<TypeEnt<'a>>> { |
| 306 | + ) -> EvalResult<Vec<(&'e SrcPos, Result<ResolvedFormal<'a>, Diagnostic>)>> { |
308 | 307 | self.check_positional_before_named(elems, diagnostics)?; |
309 | 308 |
|
310 | 309 | // 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(); |
315 | 311 |
|
316 | 312 | for (actual_idx, AssociationElement { formal, actual }) in elems.iter_mut().enumerate() { |
317 | 313 | if let Some(ref mut formal) = formal { |
318 | 314 | // Named argument |
319 | | - match self.resolve_formal( |
| 315 | + let resolved_formal = match self.resolve_formal( |
320 | 316 | formal_region, |
321 | 317 | scope, |
322 | 318 | &formal.pos, |
323 | 319 | &mut formal.item, |
324 | 320 | diagnostics, |
325 | 321 | ) { |
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)); |
334 | 327 | } else if let Some(formal) = formal_region.nth(actual_idx) { |
335 | 328 | // Actual index is same as formal index for positional argument |
336 | 329 | let formal = ResolvedFormal::new_basic(actual_idx, formal); |
337 | | - result.push((&actual.pos, formal)); |
| 330 | + result.push((&actual.pos, Ok(formal))); |
338 | 331 | } else { |
339 | | - extra_associations.push(actual.pos.clone()); |
| 332 | + result.push(( |
| 333 | + &actual.pos, |
| 334 | + Err(Diagnostic::error(&actual.pos, "Unexpected extra argument")), |
| 335 | + )); |
340 | 336 | }; |
341 | 337 | } |
| 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 | + ); |
342 | 364 |
|
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 | + } |
345 | 379 |
|
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) |
351 | 382 | // Default may be unconnected |
352 | 383 | || formal.has_default() |
353 | 384 | // Output ports are allowed to be unconnected |
354 | 385 | || (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 | + ); |
360 | 391 |
|
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"); |
378 | 394 | } |
379 | | - seen.insert(resolved_formal.idx, (*actual_pos, *resolved_formal)); |
| 395 | + |
| 396 | + is_error = true; |
| 397 | + diagnostics.push(diagnostic); |
380 | 398 | } |
381 | 399 | } |
382 | 400 |
|
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) |
389 | 403 | } 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 | | - |
409 | 404 | Err(EvalError::Unknown) |
410 | 405 | } |
411 | 406 | } |
412 | 407 |
|
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>( |
414 | 422 | &self, |
415 | 423 | error_pos: &SrcPos, // The position of the instance/call-site |
416 | 424 | formal_region: &FormalRegion<'a>, |
417 | 425 | scope: &Scope<'a>, |
418 | | - elems: &mut [AssociationElement], |
| 426 | + elems: &'e mut [AssociationElement], |
419 | 427 | diagnostics: &mut dyn DiagnosticHandler, |
420 | 428 | ) -> 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 |
429 | 446 | .iter() |
430 | 447 | .zip(elems.iter_mut().map(|assoc| &mut assoc.actual)) |
431 | 448 | { |
432 | 449 | match &mut actual.item { |
433 | 450 | 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 | + } |
441 | 463 | } |
442 | 464 | ActualPart::Open => {} |
443 | 465 | } |
|
0 commit comments