@@ -14,21 +14,35 @@ use fnv::FnvHashSet;
1414use std:: cell:: RefCell ;
1515use std:: ops:: Deref ;
1616
17- #[ derive( Debug , PartialEq , Eq ) ]
18- pub enum AnalysisError {
19- Fatal ( CircularDependencyError ) ,
20- NotFatal ( Diagnostic ) ,
21- }
22-
23- impl AnalysisError {
24- pub fn not_fatal_error ( pos : impl AsRef < SrcPos > , msg : impl Into < String > ) -> AnalysisError {
25- AnalysisError :: NotFatal ( Diagnostic :: error ( pos, msg) )
26- }
27- }
28-
17+ /// Indicates that a circular dependency is found at the position denoted by `reference`.
18+ ///
19+ /// A circular dependency occurs when module A uses module B, which in turn
20+ /// (either directly or indirectly via other modules) uses module A again.
21+ ///
22+ /// ## Example
23+ ///
24+ /// ```vhdl
25+ /// use work.bar;
26+ ///
27+ /// package foo is
28+ /// end package;
29+ ///
30+ /// use work.foo;
31+ ///
32+ /// package bar is
33+ /// end package;
34+ /// ```
35+ /// In this example, the package `bar` uses the package `foo` which in turn uses package `bar` –
36+ /// making the dependency chain cyclic.
37+ ///
38+ /// Commonly, two or more `CircularDependencyError`s are pushed to indicate what modules
39+ /// the error affects.
2940#[ derive( Clone , Debug , PartialEq , Eq ) ]
3041#[ must_use]
3142pub struct CircularDependencyError {
43+ /// The position where the circular dependency was found.
44+ /// Is `None` when the circular dependency is found in the standard library.
45+ /// This should, in practice, never happen.
3246 reference : Option < SrcPos > ,
3347}
3448
@@ -39,97 +53,84 @@ impl CircularDependencyError {
3953 }
4054 }
4155
56+ /// Pushes this error into a diagnostic handler.
4257 pub fn push_into ( self , diagnostics : & mut dyn DiagnosticHandler ) {
4358 if let Some ( pos) = self . reference {
44- diagnostics. push ( Diagnostic :: error ( pos, "Found circular dependency" ) ) ;
59+ diagnostics. error ( pos, "Found circular dependency" ) ;
4560 }
4661 }
4762}
4863
49- pub type AnalysisResult < T > = Result < T , AnalysisError > ;
64+ /// A `FatalResult` is a result that is either OK or contains a `CircularDependencyError`.
65+ /// If this type contains the error case, most other errors encountered during analysis are ignored
66+ /// as analysis cannot continue (resp. no further analysis is pursued)
5067pub type FatalResult < T = ( ) > = Result < T , CircularDependencyError > ;
5168
52- pub fn as_fatal < T > ( res : EvalResult < T > ) -> FatalResult < Option < T > > {
53- match res {
54- Ok ( val) => Ok ( Some ( val) ) ,
55- Err ( EvalError :: Unknown ) => Ok ( None ) ,
56- Err ( EvalError :: Circular ( circ) ) => Err ( circ) ,
57- }
58- }
59-
60- pub fn catch_diagnostic < T > (
61- res : Result < T , Diagnostic > ,
62- diagnostics : & mut dyn DiagnosticHandler ,
63- ) -> EvalResult < T > {
64- match res {
65- Ok ( val) => Ok ( val) ,
66- Err ( diag) => {
67- diagnostics. push ( diag) ;
68- Err ( EvalError :: Unknown )
69- }
70- }
71- }
72-
73- pub fn catch_analysis_err < T > (
74- res : AnalysisResult < T > ,
75- diagnostics : & mut dyn DiagnosticHandler ,
76- ) -> EvalResult < T > {
77- match res {
78- Ok ( val) => Ok ( val) ,
79- Err ( AnalysisError :: Fatal ( err) ) => Err ( EvalError :: Circular ( err) ) ,
80- Err ( AnalysisError :: NotFatal ( diag) ) => {
81- diagnostics. push ( diag) ;
82- Err ( EvalError :: Unknown )
83- }
84- }
85- }
86-
8769#[ derive( Debug , PartialEq , Eq ) ]
8870pub enum EvalError {
89- // A circular dependency was found
71+ /// A circular dependency was found, see [CircularDependencyError](CircularDependencyError)
9072 Circular ( CircularDependencyError ) ,
91- // Evaluation is no longer possible, for example if encountering illegal code
92- // Typically functions returning Unknown will have published diagnostics on the side-channel
93- // And the unknown is returned to stop further upstream analysis
73+ /// Indicates that evaluation is no longer possible, for example if encountering illegal code.
74+ /// Typically, functions returning Unknown will have published diagnostics on the side-channel
75+ /// and the unknown is returned to stop further upstream analysis
9476 Unknown ,
9577}
9678
97- pub type EvalResult < T = ( ) > = Result < T , EvalError > ;
98-
99- impl From < CircularDependencyError > for AnalysisError {
100- fn from ( err : CircularDependencyError ) -> AnalysisError {
101- AnalysisError :: Fatal ( err)
102- }
103- }
104-
10579impl From < CircularDependencyError > for EvalError {
10680 fn from ( err : CircularDependencyError ) -> EvalError {
10781 EvalError :: Circular ( err)
10882 }
10983}
11084
111- impl From < Diagnostic > for AnalysisError {
112- fn from ( diagnostic : Diagnostic ) -> AnalysisError {
113- AnalysisError :: NotFatal ( diagnostic)
114- }
85+ /// The result of the evaluation of an AST element.
86+ /// The result has either a value of `Ok(T)`, indicating a successful evaluation of
87+ /// the AST and returning the result of that evaluation, or `Err(EvalError)`, indicating
88+ /// an error during evaluation.
89+ ///
90+ /// Most of the time, the error will be `EvalError::Unknown`. This means that the evaluation
91+ /// step has pushed found problems in the code to some side-channel and simply returns an error,
92+ /// signifying that some problem was found without further specifying that problem.
93+ pub type EvalResult < T = ( ) > = Result < T , EvalError > ;
94+
95+ /// Pushes the diagnostic to the provided handler and returns
96+ /// with an `EvalError::Unknown` result.
97+ ///
98+ /// This macro can be used for the common case of encountering an analysis error and
99+ /// immediately returning as the error is not recoverable.
100+ macro_rules! bail {
101+ ( $diagnostics: expr, $error: expr) => {
102+ $diagnostics. push( $error) ;
103+ return Err ( EvalError :: Unknown ) ;
104+ } ;
115105}
116106
117- impl AnalysisError {
118- // Add Non-fatal error to diagnostics or return fatal error
119- pub fn add_to ( self , diagnostics : & mut dyn DiagnosticHandler ) -> FatalResult {
120- let diag = self . into_non_fatal ( ) ? ;
121- diagnostics . push ( diag ) ;
122- Ok ( ( ) )
123- }
107+ pub trait IntoEvalResult < T > {
108+ /// Transforms `Self` into an `EvalResult`.
109+ ///
110+ /// If `Self` has any severe errors, these should be pushed to the provided handler
111+ /// and an `Err(EvalError::Unknown)` should be returned.
112+ fn into_eval_result ( self , diagnostics : & mut dyn DiagnosticHandler ) -> EvalResult < T > ;
113+ }
124114
125- pub fn into_non_fatal ( self ) -> FatalResult < Diagnostic > {
115+ impl < T > IntoEvalResult < T > for Result < T , Diagnostic > {
116+ fn into_eval_result ( self , diagnostics : & mut dyn DiagnosticHandler ) -> EvalResult < T > {
126117 match self {
127- AnalysisError :: Fatal ( err) => Err ( err) ,
128- AnalysisError :: NotFatal ( diag) => Ok ( diag) ,
118+ Ok ( value) => Ok ( value) ,
119+ Err ( diagnostic) => {
120+ bail ! ( diagnostics, diagnostic) ;
121+ }
129122 }
130123 }
131124}
132125
126+ pub fn as_fatal < T > ( res : EvalResult < T > ) -> FatalResult < Option < T > > {
127+ match res {
128+ Ok ( val) => Ok ( Some ( val) ) ,
129+ Err ( EvalError :: Unknown ) => Ok ( None ) ,
130+ Err ( EvalError :: Circular ( circ) ) => Err ( circ) ,
131+ }
132+ }
133+
133134pub ( super ) struct AnalyzeContext < ' a > {
134135 pub ( super ) root : & ' a DesignRoot ,
135136
@@ -138,7 +139,7 @@ pub(super) struct AnalyzeContext<'a> {
138139 standard_sym : Symbol ,
139140 pub ( super ) is_std_logic_1164 : bool ,
140141
141- // Record dependencies and sensitivies when
142+ // Record dependencies and sensitives when
142143 // analyzing design units
143144 //
144145 // Dependencies define the order in which design units must be analyzed
@@ -354,78 +355,104 @@ impl<'a> AnalyzeContext<'a> {
354355 None
355356 }
356357
358+ /// Given an Entity, returns a reference to the architecture denoted by `architecture_name`.
359+ /// If this architecture cannot be found, pushes an appropriate error to the diagnostics-handler
360+ /// and returns `EvalError::Unknown`.
361+ ///
362+ /// # Arguments
363+ ///
364+ /// * `diagnostics` Reference to the diagnostic handler
365+ /// * `library_name` The name of the library where the entity resides
366+ /// * `pos` The position where the architecture name was declared
367+ /// * `entity_name` Name of the entity
368+ /// * `architecture_name` Name of the architecture to be resolved
357369 pub ( super ) fn get_architecture (
358370 & self ,
371+ diagnostics : & mut dyn DiagnosticHandler ,
359372 library_name : & Symbol ,
360373 pos : & SrcPos ,
361374 entity_name : & Symbol ,
362375 architecture_name : & Symbol ,
363- ) -> AnalysisResult < DesignEnt < ' a > > {
376+ ) -> EvalResult < DesignEnt < ' a > > {
364377 if let Some ( unit) = self . get_secondary_unit ( library_name, entity_name, architecture_name) {
365378 let data = self . get_analysis ( Some ( pos) , unit) ?;
366379 if let AnyDesignUnit :: Secondary ( AnySecondaryUnit :: Architecture ( arch) ) = data. deref ( ) {
367380 if let Some ( id) = arch. ident . decl . get ( ) {
368381 let ent = self . arena . get ( id) ;
369- let design = DesignEnt :: from_any ( ent) . ok_or_else ( || {
382+ if let Some ( design) = DesignEnt :: from_any ( ent) {
383+ return Ok ( design) ;
384+ } else {
370385 // Almost impossible but better not fail silently
371- Diagnostic :: error (
372- pos,
373- format ! (
374- "Found non-design {} unit within library {}" ,
375- ent. describe( ) ,
376- library_name
377- ) ,
378- )
379- } ) ?;
380- return Ok ( design) ;
386+ bail ! (
387+ diagnostics,
388+ Diagnostic :: error(
389+ pos,
390+ format!(
391+ "Found non-design {} unit within library {}" ,
392+ ent. describe( ) ,
393+ library_name
394+ ) ,
395+ )
396+ ) ;
397+ }
381398 }
382399 }
383400 }
384401
385- Err ( AnalysisError :: NotFatal ( Diagnostic :: error (
386- pos,
387- format ! (
402+ bail ! (
403+ diagnostics,
404+ Diagnostic :: error(
405+ pos,
406+ format!(
388407 "No architecture '{architecture_name}' for entity '{library_name}.{entity_name}'"
389408 ) ,
390- ) ) )
409+ )
410+ ) ;
391411 }
392412
393413 pub fn lookup_in_library (
394414 & self ,
415+ diagnostics : & mut dyn DiagnosticHandler ,
395416 library_name : & Symbol ,
396417 pos : & SrcPos ,
397418 primary_name : & Designator ,
398- ) -> AnalysisResult < DesignEnt < ' a > > {
419+ ) -> EvalResult < DesignEnt < ' a > > {
399420 if let Designator :: Identifier ( ref primary_name) = primary_name {
400421 if let Some ( unit) = self . get_primary_unit ( library_name, primary_name) {
401422 let data = self . get_analysis ( Some ( pos) , unit) ?;
402423 if let AnyDesignUnit :: Primary ( primary) = data. deref ( ) {
403424 if let Some ( id) = primary. ent_id ( ) {
404425 let ent = self . arena . get ( id) ;
405- let design = DesignEnt :: from_any ( ent) . ok_or_else ( || {
406- // Almost impossible but better not fail silently
407- Diagnostic :: error (
408- pos,
409- format ! (
410- "Found non-design {} unit within library {}" ,
411- ent. describe( ) ,
412- library_name
413- ) ,
414- )
415- } ) ?;
416- return Ok ( design) ;
426+ if let Some ( design) = DesignEnt :: from_any ( ent) {
427+ return Ok ( design) ;
428+ } else {
429+ bail ! (
430+ diagnostics,
431+ Diagnostic :: error(
432+ pos,
433+ format!(
434+ "Found non-design {} unit within library {}" ,
435+ ent. describe( ) ,
436+ library_name
437+ ) ,
438+ )
439+ ) ;
440+ }
417441 }
418442 }
419443 }
420444 }
421445
422- Err ( AnalysisError :: NotFatal ( Diagnostic :: error (
423- pos,
424- format ! ( "No primary unit '{primary_name}' within library '{library_name}'" ) ,
425- ) ) )
446+ bail ! (
447+ diagnostics,
448+ Diagnostic :: error(
449+ pos,
450+ format!( "No primary unit '{primary_name}' within library '{library_name}'" ) ,
451+ )
452+ ) ;
426453 }
427454
428- // Returns None when analyzing the standard package itsel
455+ // Returns None when analyzing the standard package itself
429456 fn standard_package_region ( & self ) -> Option < & ' a Region < ' a > > {
430457 if let Some ( pkg) = self . root . standard_pkg_id . as_ref ( ) {
431458 self . arena . link ( self . root . standard_arena . as_ref ( ) . unwrap ( ) ) ;
0 commit comments