@@ -32,8 +32,11 @@ pointer_typet select_pointer_typet::convert_pointer_type(
3232 // a generic parameter, specialize it with concrete types
3333 if (!generic_parameter_specialization_map.empty ())
3434 {
35- return specialize_generics (
36- pointer_type, generic_parameter_specialization_map);
35+ generic_parameter_recursion_trackingt visited;
36+ const auto &type = specialize_generics (
37+ pointer_type, generic_parameter_specialization_map, visited);
38+ INVARIANT (visited.empty (), " recursion stack must be empty here" );
39+ return type;
3740 }
3841 else
3942 {
@@ -62,13 +65,24 @@ pointer_typet select_pointer_typet::convert_pointer_type(
6265pointer_typet select_pointer_typet::specialize_generics (
6366 const pointer_typet &pointer_type,
6467 const generic_parameter_specialization_mapt
65- &generic_parameter_specialization_map) const
68+ &generic_parameter_specialization_map,
69+ generic_parameter_recursion_trackingt &visited_nodes) const
6670{
6771 if (is_java_generic_parameter (pointer_type))
6872 {
6973 const java_generic_parametert ¶meter =
7074 to_java_generic_parameter (pointer_type);
7175 const irep_idt ¶meter_name = parameter.get_name ();
76+
77+ // avoid infinite recursion by looking at each generic argument from
78+ // previous assignments
79+ if (visited_nodes.find (parameter_name) != visited_nodes.end ())
80+ {
81+ const optionalt<pointer_typet> result = get_recursively_instantiated_type (
82+ parameter_name, generic_parameter_specialization_map);
83+ return result.has_value () ? result.value () : pointer_type;
84+ }
85+
7286 if (generic_parameter_specialization_map.count (parameter_name) == 0 )
7387 {
7488 // this means that the generic pointer_type has not been specialized
@@ -78,15 +92,20 @@ pointer_typet select_pointer_typet::specialize_generics(
7892 return pointer_type;
7993 }
8094 const pointer_typet &type =
81- generic_parameter_specialization_map.find (parameter_name)->second .top ();
95+ generic_parameter_specialization_map.find (parameter_name)->second .back ();
8296
8397 // generic parameters can be adopted from outer classes or superclasses so
8498 // we may need to search for the concrete type recursively
85- return is_java_generic_parameter (type)
86- ? specialize_generics (
87- to_java_generic_parameter (type),
88- generic_parameter_specialization_map)
89- : type;
99+ if (!is_java_generic_parameter (type))
100+ return type;
101+
102+ visited_nodes.insert (parameter_name);
103+ const auto returned_type = specialize_generics (
104+ to_java_generic_parameter (type),
105+ generic_parameter_specialization_map,
106+ visited_nodes);
107+ visited_nodes.erase (parameter_name);
108+ return returned_type;
90109 }
91110 else if (pointer_type.subtype ().id () == ID_symbol)
92111 {
@@ -99,7 +118,8 @@ pointer_typet select_pointer_typet::specialize_generics(
99118 {
100119 const pointer_typet &new_array_type = specialize_generics (
101120 to_pointer_type (array_element_type),
102- generic_parameter_specialization_map);
121+ generic_parameter_specialization_map,
122+ visited_nodes);
103123
104124 pointer_typet replacement_array_type = java_array_type (' a' );
105125 replacement_array_type.subtype ().set (ID_C_element_type, new_array_type);
@@ -109,3 +129,96 @@ pointer_typet select_pointer_typet::specialize_generics(
109129 }
110130 return pointer_type;
111131}
132+
133+ // / Return the first concrete type instantiation if any such exists. This method
134+ // / is only to be called when the `specialize_generics` cannot find an
135+ // / instantiation due to a loop in its recursion.
136+ // / \param parameter_name The name of the generic parameter type we want to have
137+ // / instantiated
138+ // / \param generic_parameter_specialization_map Map of type names to
139+ // / specialization stack
140+ // / \return The first instantiated type for the generic type or nothing if no
141+ // / such instantiation exists.
142+ optionalt<pointer_typet>
143+ select_pointer_typet::get_recursively_instantiated_type (
144+ const irep_idt ¶meter_name,
145+ const generic_parameter_specialization_mapt
146+ &generic_parameter_specialization_map) const
147+ {
148+ generic_parameter_recursion_trackingt visited;
149+ const size_t max_depth =
150+ generic_parameter_specialization_map.find (parameter_name)->second .size ();
151+
152+ irep_idt current_parameter = parameter_name;
153+ for (size_t depth = 0 ; depth < max_depth; depth++)
154+ {
155+ const auto retval = get_recursively_instantiated_type (
156+ current_parameter, generic_parameter_specialization_map, visited, depth);
157+ if (retval.has_value ())
158+ {
159+ CHECK_RETURN (!is_java_generic_parameter (*retval));
160+ return retval;
161+ }
162+ CHECK_RETURN (visited.empty ());
163+
164+ const auto &entry =
165+ generic_parameter_specialization_map.find (current_parameter)
166+ ->second .back ();
167+ current_parameter = to_java_generic_parameter (entry).get_name ();
168+ }
169+ return {};
170+ }
171+
172+ // / See get_recursively instantiated_type, the additional parameters just track
173+ // / the recursion to prevent visiting the same depth again and specify which
174+ // / stack depth is analyzed.
175+ // / \param parameter_name The name of the generic parameter type we want to have
176+ // / instantiated
177+ // / \param generic_parameter_specialization_map Map of type names to
178+ // / specialization stack
179+ // / \param visited Tracks the visited parameter names
180+ // / \param depth Stack depth to analyze
181+ // / \return if this type is not a generic type, it is returned as a valid
182+ // / instantiation, if nothing can be found at the given depth, en empty
183+ // / optional is returned
184+ optionalt<pointer_typet>
185+ select_pointer_typet::get_recursively_instantiated_type (
186+ const irep_idt ¶meter_name,
187+ const generic_parameter_specialization_mapt
188+ &generic_parameter_specialization_map,
189+ generic_parameter_recursion_trackingt &visited,
190+ const size_t depth) const
191+ {
192+ const auto &val = generic_parameter_specialization_map.find (parameter_name);
193+ INVARIANT (
194+ val != generic_parameter_specialization_map.end (),
195+ " generic parameter must be a key in map" );
196+
197+ const auto &replacements = val->second ;
198+
199+ INVARIANT (
200+ depth < replacements.size (), " cannot access elements outside stack" );
201+
202+ // Check if there is a recursion loop, if yes return with nothing found
203+ if (visited.find (parameter_name) != visited.end ())
204+ {
205+ return {};
206+ }
207+
208+ const size_t index = (replacements.size () - 1 ) - depth;
209+ const auto &type = replacements[index];
210+
211+ if (!is_java_generic_parameter (type))
212+ {
213+ return type;
214+ }
215+
216+ visited.insert (parameter_name);
217+ const auto inst_val = get_recursively_instantiated_type (
218+ to_java_generic_parameter (type).get_name (),
219+ generic_parameter_specialization_map,
220+ visited,
221+ depth);
222+ visited.erase (parameter_name);
223+ return inst_val;
224+ }
0 commit comments