@@ -32,8 +32,11 @@ pointer_typet select_pointer_typet::convert_pointer_type(
32
32
// a generic parameter, specialize it with concrete types
33
33
if (!generic_parameter_specialization_map.empty ())
34
34
{
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;
37
40
}
38
41
else
39
42
{
@@ -62,13 +65,24 @@ pointer_typet select_pointer_typet::convert_pointer_type(
62
65
pointer_typet select_pointer_typet::specialize_generics (
63
66
const pointer_typet &pointer_type,
64
67
const generic_parameter_specialization_mapt
65
- &generic_parameter_specialization_map) const
68
+ &generic_parameter_specialization_map,
69
+ generic_parameter_recursion_trackingt &visited_nodes) const
66
70
{
67
71
if (is_java_generic_parameter (pointer_type))
68
72
{
69
73
const java_generic_parametert ¶meter =
70
74
to_java_generic_parameter (pointer_type);
71
75
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
+
72
86
if (generic_parameter_specialization_map.count (parameter_name) == 0 )
73
87
{
74
88
// this means that the generic pointer_type has not been specialized
@@ -78,15 +92,20 @@ pointer_typet select_pointer_typet::specialize_generics(
78
92
return pointer_type;
79
93
}
80
94
const pointer_typet &type =
81
- generic_parameter_specialization_map.find (parameter_name)->second .top ();
95
+ generic_parameter_specialization_map.find (parameter_name)->second .back ();
82
96
83
97
// generic parameters can be adopted from outer classes or superclasses so
84
98
// 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;
90
109
}
91
110
else if (pointer_type.subtype ().id () == ID_symbol)
92
111
{
@@ -99,7 +118,8 @@ pointer_typet select_pointer_typet::specialize_generics(
99
118
{
100
119
const pointer_typet &new_array_type = specialize_generics (
101
120
to_pointer_type (array_element_type),
102
- generic_parameter_specialization_map);
121
+ generic_parameter_specialization_map,
122
+ visited_nodes);
103
123
104
124
pointer_typet replacement_array_type = java_array_type (' a' );
105
125
replacement_array_type.subtype ().set (ID_C_element_type, new_array_type);
@@ -109,3 +129,96 @@ pointer_typet select_pointer_typet::specialize_generics(
109
129
}
110
130
return pointer_type;
111
131
}
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