@@ -76,6 +76,64 @@ pub(crate) struct LookupOperation {
7676 deps_in_order : Vec < SymbolName > ,
7777}
7878
79+ fn final_lookup_helper_body < R : RcAllocator > (
80+ t : & mut SymbolTable ,
81+ name : & SymbolName ,
82+ span : Span ,
83+ target_address : & HereValue ,
84+ rc_allocator : & mut R ,
85+ op : & mut LookupOperation ,
86+ ) -> Result < SymbolValue , SymbolLookupFailure > {
87+ if let Some ( def) = t. get_clone ( name) {
88+ match def {
89+ SymbolDefinition :: DefaultAssigned ( value, _) => Ok ( SymbolValue :: Final ( value) ) ,
90+ SymbolDefinition :: Origin ( addr) => Ok ( SymbolValue :: Final ( addr. into ( ) ) ) ,
91+ SymbolDefinition :: Tag {
92+ block_id,
93+ block_offset,
94+ span : _,
95+ } => match t. finalise_origin ( block_id, rc_allocator, Some ( op) ) {
96+ Ok ( address) => {
97+ let computed_address: Address = address. index_by ( block_offset) ;
98+ Ok ( SymbolValue :: Final ( computed_address. into ( ) ) )
99+ }
100+ Err ( e) => {
101+ panic ! ( "failed to finalise origin of {block_id}: {e}" ) ;
102+ }
103+ } ,
104+ SymbolDefinition :: TagOverride ( address) => Ok ( SymbolValue :: Final ( address. into ( ) ) ) ,
105+ SymbolDefinition :: Equality ( expression) => {
106+ // The target address does not matter below
107+ // since assignments are not allowed to use
108+ // '#' on the right-hand-side of the
109+ // assignment.
110+ expression
111+ . evaluate ( target_address, t, rc_allocator, op)
112+ . map ( SymbolValue :: Final )
113+ }
114+ SymbolDefinition :: Undefined ( context_union) => {
115+ match t. get_default_value ( name, & span, & context_union, rc_allocator) {
116+ Ok ( value) => {
117+ match t. define (
118+ span,
119+ name. clone ( ) ,
120+ SymbolDefinition :: DefaultAssigned ( value, context_union) ,
121+ ) {
122+ Err ( e) => {
123+ panic ! ( "got a bad symbol definition error ({e}) on a previously undefined symbol, which should be impossible" ) ;
124+ }
125+ Ok ( ( ) ) => Ok ( SymbolValue :: Final ( value) ) ,
126+ }
127+ }
128+ Err ( e) => Err ( e) ,
129+ }
130+ }
131+ }
132+ } else {
133+ panic ! ( "final symbol lookup of symbol '{name}' happened in an evaluation context which was not previously returned by SourceFile::global_symbol_references(): {op:?}" ) ;
134+ }
135+ }
136+
79137impl SymbolTable {
80138 pub ( crate ) fn new < I > ( block_sizes : I ) -> SymbolTable
81139 where
@@ -159,93 +217,6 @@ impl SymbolTable {
159217 } ) ;
160218 }
161219
162- fn final_lookup_helper < R : RcAllocator > (
163- & mut self ,
164- name : & SymbolName ,
165- span : Span ,
166- target_address : & HereValue ,
167- rc_allocator : & mut R ,
168- _context : & SymbolContext ,
169- op : & mut LookupOperation ,
170- ) -> Result < SymbolValue , SymbolLookupFailure > {
171- fn body < R : RcAllocator > (
172- t : & mut SymbolTable ,
173- name : & SymbolName ,
174- span : Span ,
175- target_address : & HereValue ,
176- rc_allocator : & mut R ,
177- op : & mut LookupOperation ,
178- ) -> Result < SymbolValue , SymbolLookupFailure > {
179- if let Some ( def) = t. get_clone ( name) {
180- match def {
181- SymbolDefinition :: DefaultAssigned ( value, _) => Ok ( SymbolValue :: Final ( value) ) ,
182- SymbolDefinition :: Origin ( addr) => Ok ( SymbolValue :: Final ( addr. into ( ) ) ) ,
183- SymbolDefinition :: Tag {
184- block_id,
185- block_offset,
186- span : _,
187- } => match t. finalise_origin ( block_id, rc_allocator, Some ( op) ) {
188- Ok ( address) => {
189- let computed_address: Address = address. index_by ( block_offset) ;
190- Ok ( SymbolValue :: Final ( computed_address. into ( ) ) )
191- }
192- Err ( e) => {
193- panic ! ( "failed to finalise origin of {block_id}: {e}" ) ;
194- }
195- } ,
196- SymbolDefinition :: TagOverride ( address) => {
197- Ok ( SymbolValue :: Final ( address. into ( ) ) )
198- }
199- SymbolDefinition :: Equality ( expression) => {
200- // The target address does not matter below
201- // since assignments are not allowed to use
202- // '#' on the right-hand-side of the
203- // assignment.
204- expression
205- . evaluate ( target_address, t, rc_allocator, op)
206- . map ( SymbolValue :: Final )
207- }
208- SymbolDefinition :: Undefined ( context_union) => {
209- match t. get_default_value ( name, & span, & context_union, rc_allocator) {
210- Ok ( value) => {
211- match t. define (
212- span,
213- name. clone ( ) ,
214- SymbolDefinition :: DefaultAssigned ( value, context_union) ,
215- ) {
216- Err ( e) => {
217- panic ! ( "got a bad symbol definition error ({e}) on a previously undefined symbol, which should be impossible" ) ;
218- }
219- Ok ( ( ) ) => Ok ( SymbolValue :: Final ( value) ) ,
220- }
221- }
222- Err ( e) => Err ( e) ,
223- }
224- }
225- }
226- } else {
227- panic ! ( "final symbol lookup of symbol '{name}' happened in an evaluation context which was not previously returned by SourceFile::global_symbol_references(): {op:?}" ) ;
228- }
229- }
230-
231- op. deps_in_order . push ( name. clone ( ) ) ;
232- if !op. depends_on . insert ( name. clone ( ) ) {
233- // `name` was already in `depends_on`; in other words,
234- // we have a loop.
235- return Err ( SymbolLookupFailure :: from ( (
236- name. clone ( ) ,
237- span,
238- SymbolLookupFailureKind :: Loop {
239- deps_in_order : op. deps_in_order . to_vec ( ) ,
240- } ,
241- ) ) ) ;
242- }
243- let result = body ( self , name, span, target_address, rc_allocator, op) ;
244- op. deps_in_order . pop ( ) ;
245- op. depends_on . remove ( name) ;
246- result
247- }
248-
249220 pub ( crate ) fn finalise_origin < R : RcAllocator > (
250221 & mut self ,
251222 block_id : BlockIdentifier ,
@@ -498,10 +469,24 @@ impl SymbolLookup for SymbolTable {
498469 span : Span ,
499470 target_address : & HereValue ,
500471 rc_allocator : & mut R ,
501- context : & SymbolContext ,
502472 op : & mut Self :: Operation < ' _ > ,
503473 ) -> Result < SymbolValue , SymbolLookupFailure > {
504- self . final_lookup_helper ( name, span, target_address, rc_allocator, context, op)
474+ op. deps_in_order . push ( name. clone ( ) ) ;
475+ if !op. depends_on . insert ( name. clone ( ) ) {
476+ // `name` was already in `depends_on`; in other words,
477+ // we have a loop.
478+ return Err ( SymbolLookupFailure :: from ( (
479+ name. clone ( ) ,
480+ span,
481+ SymbolLookupFailureKind :: Loop {
482+ deps_in_order : op. deps_in_order . to_vec ( ) ,
483+ } ,
484+ ) ) ) ;
485+ }
486+ let result = final_lookup_helper_body ( self , name, span, target_address, rc_allocator, op) ;
487+ op. deps_in_order . pop ( ) ;
488+ op. depends_on . remove ( name) ;
489+ result
505490 }
506491}
507492
@@ -723,3 +708,70 @@ impl SymbolDefinition {
723708 }
724709 }
725710}
711+
712+ #[ test]
713+ fn test_loop_detection ( ) {
714+ use super :: ast:: * ;
715+ use super :: eval:: RcBlock ;
716+ use base:: charset:: Script ;
717+ let blocks = [ ( span ( 0 ..10 ) , None , u18 ! ( 2 ) ) ] ;
718+ let mut symtab = SymbolTable :: new ( blocks) ;
719+
720+ // define "A"
721+ symtab
722+ . define (
723+ span ( 0 ..1 ) ,
724+ SymbolName :: from ( "A" ) ,
725+ SymbolDefinition :: Equality ( EqualityValue :: from ( vec ! [ CommaDelimitedInstruction {
726+ leading_commas: None ,
727+ instruction: UntaggedProgramInstruction {
728+ span: span( 0 ..1 ) ,
729+ holdbit: HoldBit :: Unspecified ,
730+ inst: InstructionFragment :: from( ArithmeticExpression :: from(
731+ SymbolOrLiteral :: Symbol ( Script :: Normal , SymbolName :: from( "B" ) , span( 2 ..3 ) ) ,
732+ ) ) ,
733+ } ,
734+ trailing_commas: None ,
735+ } ] ) ) ,
736+ )
737+ . expect ( "definition of A should be correct" ) ;
738+ // define "B"
739+ symtab
740+ . define (
741+ span ( 4 ..5 ) ,
742+ SymbolName :: from ( "B" ) ,
743+ SymbolDefinition :: Equality ( EqualityValue :: from ( vec ! [ CommaDelimitedInstruction {
744+ leading_commas: None ,
745+ instruction: UntaggedProgramInstruction {
746+ span: span( 4 ..5 ) ,
747+ holdbit: HoldBit :: Unspecified ,
748+ inst: InstructionFragment :: from( ArithmeticExpression :: from(
749+ SymbolOrLiteral :: Symbol ( Script :: Normal , SymbolName :: from( "A" ) , span( 6 ..7 ) ) ,
750+ ) ) ,
751+ } ,
752+ trailing_commas: None ,
753+ } ] ) ) ,
754+ )
755+ . expect ( "definition of B should be correct" ) ;
756+
757+ let mut rc_block = RcBlock :: default ( ) ;
758+
759+ // An attempt to look up A should fail because we detect a loop.
760+ let mut op = Default :: default ( ) ;
761+ let r = symtab. lookup_with_op (
762+ & SymbolName :: from ( "A" ) ,
763+ span ( 0 ..1 ) ,
764+ & HereValue :: NotAllowed ,
765+ & mut rc_block,
766+ & mut op,
767+ ) ;
768+ dbg ! ( & r) ;
769+ assert ! ( matches!(
770+ r,
771+ Err ( SymbolLookupFailure {
772+ target: _,
773+ kind: SymbolLookupFailureKind :: Loop { .. }
774+ } )
775+ ) ) ;
776+ assert ! ( false ) ;
777+ }
0 commit comments