1/*
2 * Copyright 2020 Cerebras Systems. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials provided
14 * with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY CEREBRAS SYSTEMS ''AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CEREBRAS SYSTEMS OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * The views and conclusions contained in the software and documentation
29 * are those of the authors and should not be interpreted as
30 * representing official policies, either expressed or implied, of
31 * Cerebras Systems.
32 */
33
34#include <ctype.h>
35
36#include <algorithm>
37#include <iostream>
38#include <set>
39#include <sstream>
40#include <string>
41#include <unordered_map>
42#include <unordered_set>
43
44#include "template_cpp.h"
45#include "isl_config.h"
46
47/* The textual representation of this tuple kind.
48 *
49 * By default, the textual representation is just the name.
50 */
51std::string TupleKind::to_string() const
52{
53 return name;
54}
55
56/* Return the parameters of this tuple kind.
57 *
58 * By default, there are no parameters.
59 */
60std::vector<std::string> TupleKind::params() const
61{
62 return { };
63}
64
65/* Apply the substitution "subs" to this tuple kind and return the result.
66 * "self" is a shared pointer to this.
67 *
68 * If the name of this tuple kind appears in the substitution,
69 * then return the corresponding tuple kind pointer.
70 * Otherwise, return "self".
71 */
72TupleKindPtr TupleKind::apply(const Substitution &subs,
73 const TupleKindPtr &self) const
74{
75 if (subs.count(x: name) != 0)
76 return subs.at(k: name);
77 return self;
78}
79
80/* Apply the substitution "subs" to "tuple" and return the result.
81 */
82static TupleKindPtr apply(const TupleKindPtr tuple, const Substitution &subs)
83{
84 return tuple->apply(subs, self: tuple);
85}
86
87/* Return the left child of this tuple kind.
88 *
89 * Since this is not a pair, there is no left child.
90 */
91TupleKindPtr TupleKind::left() const
92{
93 return TupleKindPtr();
94}
95
96/* Return the right child of this tuple kind.
97 *
98 * Since this is not a pair, there is no right child.
99 */
100TupleKindPtr TupleKind::right() const
101{
102 return TupleKindPtr();
103}
104
105/* Helper class used to construct a pointer to a tuple kind
106 * that refers to a non-template type.
107 */
108struct Fixed {
109};
110
111/* Construct a pointer to a tuple kind that refers to a non-template type.
112 *
113 * Use an empty string as name. Since this is a non-template type,
114 * the kind name will never appear in the generated code.
115 */
116TupleKindPtr::TupleKindPtr(Fixed) : Base(std::make_shared<TupleKind>(args: ""))
117{
118}
119
120/* Tuple pointers for non-template types.
121 */
122static TupleKindPtr Ctx{Fixed()};
123static TupleKindPtr Integer{Fixed()};
124static TupleKindPtr Str{Fixed()};
125static TupleKindPtr Res{Fixed()};
126
127/* Special tuple pointers.
128 * Anonymous appears in the generated code but cannot be unified
129 * with anything else since it is a predefined template argument.
130 * Leaf can only be unified with something that is not a pair and
131 * does not appear in the generated code.
132 */
133static TupleKindPtr Anonymous("Anonymous");
134static TupleKindPtr Leaf("Leaf");
135
136/* Placeholder tuple pointers that refer to (part of) the domain or range.
137 */
138static TupleKindPtr Domain("Domain");
139static TupleKindPtr Domain2("Domain2");
140static TupleKindPtr Domain3("Domain3");
141static TupleKindPtr Range("Range");
142static TupleKindPtr Range2("Range2");
143static TupleKindPtr Range3("Range3");
144
145/* A representation of a proper tuple kind that is used as a template
146 * parameter or a template argument.
147 */
148struct ProperTupleKind : public TupleKind {
149 ProperTupleKind(const std::string &name) : TupleKind(name) {}
150
151 virtual std::vector<std::string> params() const override;
152};
153
154/* Return the parameters of this tuple kind.
155 *
156 * Return the name of this tuple kind, unless it is the special Anonymous
157 * predefined template argument.
158 */
159std::vector<std::string> ProperTupleKind::params() const
160{
161 if (Anonymous.get() == this)
162 return { };
163 return { name };
164}
165
166/* Construct a pointer to a tuple kind that refers
167 * to a proper tuple kind with the given name.
168 */
169TupleKindPtr::TupleKindPtr(const std::string &name) :
170 Base(std::make_shared<ProperTupleKind>(args: name))
171{
172}
173
174/* A tuple kind that represents an anonymous pair of nested tuple kinds.
175 */
176struct Pair : public TupleKind {
177 Pair(const TupleKindPtr &tuple1, const TupleKindPtr &tuple2) :
178 TupleKind(""), tuple1(tuple1), tuple2(tuple2) {}
179
180 virtual std::string to_string() const override;
181 virtual std::vector<std::string> params() const override;
182 virtual TupleKindPtr apply(const Substitution &match,
183 const TupleKindPtr &self) const override;
184 virtual TupleKindPtr left() const override;
185 virtual TupleKindPtr right() const override;
186
187 const TupleKindPtr tuple1;
188 const TupleKindPtr tuple2;
189};
190
191/* The textual representation of this tuple kind.
192 *
193 * The textual representation of a pair is of the form "pair<tuple1, tuple2>".
194 */
195std::string Pair::to_string() const
196{
197 return std::string("pair<") + tuple1->to_string() + ", " +
198 tuple2->to_string() + ">";
199}
200
201/* Add the elements of "vec2" that do not already appear in "vec1"
202 * at the end of "vec1".
203 *
204 * The two vectors are assumed not to have any repeated elements.
205 * The updated vector will then also not have repeated elements.
206 */
207static void combine(std::vector<std::string> &vec1,
208 const std::vector<std::string> &vec2)
209{
210 for (const auto &s : vec2)
211 if (std::find(first: vec1.begin(), last: vec1.end(), val: s) == vec1.end())
212 vec1.emplace_back(args: s);
213}
214
215/* Return the parameters of this tuple kind.
216 *
217 * Combine the parameters of the two nested tuple kinds.
218 */
219std::vector<std::string> Pair::params() const
220{
221 auto names1 = tuple1->params();
222 auto names2 = tuple2->params();
223
224 combine(vec1&: names1, vec2: names2);
225
226 return names1;
227}
228
229/* Apply the substitution "subs" to this tuple kind and return the result.
230 * "self" is a shared pointer to this.
231 *
232 * Construct a new tuple kind consisting of the result of applying
233 * the substitution to the two nested tuple kinds.
234 */
235TupleKindPtr Pair::apply(const Substitution &subs, const TupleKindPtr &self)
236 const
237{
238 return TupleKindPtr(::apply(tuple: tuple1, subs), ::apply(tuple: tuple2, subs));
239}
240
241/* Return the left child of this tuple kind.
242 */
243TupleKindPtr Pair::left() const
244{
245 return tuple1;
246}
247
248/* Return the right child of this tuple kind.
249 */
250TupleKindPtr Pair::right() const
251{
252 return tuple2;
253}
254
255/* Construct a pointer to a tuple kind that refers
256 * to the given pair of nested tuple kinds.
257 */
258TupleKindPtr::TupleKindPtr(const TupleKindPtr &left, const TupleKindPtr &right)
259 : Base(std::make_shared<Pair>(args: left, args: right))
260{
261}
262
263/* Is this a kind of object representing an anonymous function?
264 */
265bool Kind::is_anon() const
266{
267 return size() != 0 && back() == Anonymous;
268}
269
270/* Is this a kind of object with a single tuple?
271 */
272bool Kind::is_set() const
273{
274 return size() == 1;
275}
276
277/* Is this a kind of object with a single, anonymous tuple?
278 */
279bool Kind::is_anon_set() const
280{
281 return is_set() && is_anon();
282}
283
284/* Return the parameters of this kind.
285 *
286 * Collect the parameters of the tuple kinds in the sequence.
287 */
288std::vector<std::string> Kind::params() const
289{
290 std::vector<std::string> params;
291
292 for (const auto &tuple : *this)
293 combine(vec1&: params, vec2: tuple->params());
294
295 return params;
296}
297
298/* Apply the substitution "subs" to this kind and return the result.
299 *
300 * Apply the substitution to each of the tuple kinds in the sequence.
301 */
302Kind Kind::apply(const Substitution &subs) const
303{
304 Kind applied;
305
306 for (const auto &tuple : *this)
307 applied.emplace_back(args: ::apply(tuple, subs));
308
309 return applied;
310}
311
312/* A signature of a method in terms of kinds,
313 * consisting of a return kind and a sequence of argument kinds.
314 */
315struct Signature {
316 Kind ret;
317 std::vector<Kind> args;
318
319 std::vector<std::string> params() const;
320 Signature apply(const Substitution &match) const;
321};
322
323/* Return the parameters of this signature.
324 *
325 * Collect the parameters of the argument kinds and the return kind.
326 */
327std::vector<std::string> Signature::params() const
328{
329 std::vector<std::string> params;
330
331 for (const auto &arg : args)
332 combine(vec1&: params, vec2: arg.params());
333 combine(vec1&: params, vec2: ret.params());
334
335 return params;
336}
337
338/* Apply the substitution "subs" to this kind and return the result.
339 *
340 * Apply the substitution to the argument kinds and the return kind.
341 */
342Signature Signature::apply(const Substitution &subs) const
343{
344 std::vector<Kind> applied_args;
345
346 for (const auto &arg : args)
347 applied_args.emplace_back(args: arg.apply(subs));
348
349 return { .ret: ret.apply(subs), .args: applied_args };
350}
351
352/* Return a renaming substitution that renames the elements of "params"
353 * using names starting with "prefix".
354 */
355static Substitution param_renamer(const std::vector<std::string> &params,
356 const std::string &prefix)
357{
358 Substitution renamer;
359 int n = 0;
360
361 for (const auto &name : params) {
362 auto suffix = std::to_string(val: ++n);
363 auto arg_name = prefix + suffix;
364 auto arg = TupleKindPtr(arg_name);
365
366 if (name == Leaf->name)
367 generator::die(msg: "Leaf cannot be renamed");
368
369 renamer.emplace(args: name, args&: arg);
370 }
371
372 return renamer;
373}
374
375/* Does the vector "v" contain the element "el"?
376 */
377static bool contains(const std::vector<std::string> &v, const std::string &el)
378{
379 return find(first: v.begin(), last: v.end(), val: el) != v.end();
380 }
381
382
383/* Return the shared elements of "v1" and "v2", preserving the order
384 * of those elements in "v1".
385 */
386static std::vector<std::string> intersect(const std::vector<std::string> &v1,
387 const std::vector<std::string> &v2)
388{
389 std::vector<std::string> intersection;
390
391 for (const auto &el : v1)
392 if (contains(v: v2, el))
393 intersection.push_back(x: el);
394
395 return intersection;
396}
397
398/* Return a renaming substitution that renames
399 * any parameters that appears in both "sig" and "kind".
400 */
401static Substitution shared_param_renamer(const Signature &sig, const Kind &kind)
402{
403 return param_renamer(params: intersect(v1: sig.params(), v2: kind.params()), prefix: "Arg");
404}
405
406/* Signatures for unary operations.
407 * Functions have at least one tuple.
408 */
409static Signature un_params = { .ret: { }, .args: { { } } };
410static Signature un_set = { .ret: { Domain }, .args: { { Domain } } };
411static Signature un_map = { .ret: { Domain, Range }, .args: { { Domain, Range } } };
412static std::vector<Signature> un_op = { un_params, un_set, un_map };
413static std::vector<Signature> fn_un_op = { un_set, un_map };
414
415/* Signatures for binary operations, with the second argument
416 * possibly referring to part of the first argument.
417 * Functions have at least one tuple.
418 */
419static Signature bin_params = { .ret: { }, .args: { { }, { } } };
420static Signature bin_set = { .ret: { Domain }, .args: { { Domain }, { Domain } } };
421static Signature bin_map =
422 { .ret: { Domain, Range }, .args: { { Domain, Range }, { Domain, Range } } };
423static std::vector<Signature> bin_op = { bin_params, bin_set, bin_map };
424static std::vector<Signature> fn_bin_op = { bin_set, bin_map };
425static Signature bin_set_params = { .ret: { Domain }, .args: { { Domain }, { } } };
426static Signature bin_map_params =
427 { .ret: { Domain, Range }, .args: { { Domain, Range }, { } } };
428static Signature bin_map_domain =
429 { .ret: { Domain, Range }, .args: { { Domain, Range }, { Domain } } };
430static Signature bin_map_range =
431 { .ret: { Domain, Range }, .args: { { Domain, Range }, { Range } } };
432static Signature bin_map_domain_wrapped_domain =
433 { .ret: { { Domain, Domain2 }, Range },
434 .args: { { { Domain, Domain2 }, Range }, { Domain } } };
435static Signature bin_map_range_wrapped_domain =
436 { .ret: { Domain, { Range, Range2 } },
437 .args: { { Domain, { Range, Range2 } }, { Range } } };
438
439/* Signatures for binary operations, where the second argument
440 * is an identifier (with an anonymous tuple).
441 */
442static Signature bin_params_anon = { .ret: { }, .args: { { }, { Anonymous } } };
443static Signature bin_set_anon = { .ret: { Domain }, .args: { { Domain }, { Anonymous } } };
444static Signature bin_map_anon =
445 { .ret: { Domain, Range }, .args: { { Domain, Range }, { Anonymous } } };
446static std::vector<Signature> bin_op_anon =
447 { bin_params_anon, bin_set_anon, bin_map_anon };
448
449/* Signatures for ternary operations, where the last two arguments are integers.
450 */
451static Signature ter_params_int_int =
452 { .ret: { }, .args: { { }, { Integer }, { Integer } } };
453static Signature ter_set_int_int =
454 { .ret: { Domain }, .args: { { Domain }, { Integer }, { Integer } } };
455static Signature ter_map_int_int =
456 { .ret: { Domain, Range }, .args: { { Domain, Range }, { Integer }, { Integer } } };
457static std::vector<Signature> ter_int_int =
458 { ter_params_int_int, ter_set_int_int, ter_map_int_int };
459
460/* Signatures for ternary operations.
461 * Functions have at least one tuple.
462 */
463static Signature ter_set =
464 { .ret: { Domain }, .args: { { Domain }, { Domain }, { Domain } } };
465static Signature ter_map =
466 { .ret: { Domain, Range },
467 .args: { { Domain, Range }, { Domain, Range }, { Domain, Range } } };
468static std::vector<Signature> fn_ter_op = { ter_set, ter_map };
469
470/* Signatures for naming a leaf tuple using an identifier (with an anonymous
471 * tuple).
472 */
473static Signature update_set = { .ret: { Domain2 }, .args: { { Leaf }, { Anonymous } } };
474static Signature update_domain =
475 { .ret: { Domain2, Range }, .args: { { Leaf, Range }, { Anonymous } } };
476static Signature update_range =
477 { .ret: { Domain, Range2 }, .args: { { Domain, Leaf }, { Anonymous } } };
478
479/* Signatures for the functions "min" and "max", which can be either
480 * unary or binary operations.
481 */
482static std::vector<Signature> min_max = { un_set, bin_set, un_map, bin_map };
483
484/* Signatures for adding an unnamed tuple to an object with zero or one tuple.
485 */
486static Signature to_set = { .ret: { Domain }, .args: { { }, { Integer } } };
487static Signature add_range = { .ret: { Domain, Range }, .args: { { Domain }, { Integer } } };
488/* Signatures for adding a named tuple to an object with zero or one tuple.
489 */
490static Signature to_set_named =
491 { .ret: { Domain }, .args: { { }, { Anonymous }, { Integer } } };
492static Signature add_range_named =
493 { .ret: { Domain, Range }, .args: { { Domain }, { Anonymous }, { Integer } } };
494
495/* Signatures for methods applying a map to a set, a function or
496 * part of a map.
497 */
498static Signature set_forward = { .ret: { Range }, .args: { { Domain }, { Domain, Range } } };
499static Signature domain_forward =
500 { .ret: { Domain2, Range }, .args: { { Domain, Range }, { Domain, Domain2 } } };
501static Signature range_forward =
502 { .ret: { Domain, Range2 }, .args: { { Domain, Range }, { Range, Range2 } } };
503
504/* Signatures for methods plugging in a function into a set, a function or
505 * part of a map.
506 */
507static Signature set_backward =
508 { .ret: { Domain2 }, .args: { { Domain }, { Domain2, Domain } } };
509static Signature domain_backward =
510 { .ret: { Domain2, Range }, .args: { { Domain, Range }, { Domain2, Domain } } };
511static Signature range_backward =
512 { .ret: { Domain, Range2 }, .args: { { Domain, Range }, { Range2, Range } } };
513static Signature domain_wrapped_domain_backward =
514 { .ret: { { Domain3, Domain2 }, Range },
515 .args: { { { Domain, Domain2 }, Range }, { Domain3, Domain } } };
516
517/* Signatures for methods binding a set, a function,
518 * or (part of) a map to parameters or an object of the same kind.
519 */
520static Signature bind_set = { .ret: { }, .args: { { Domain }, { Domain } } };
521static Signature bind_domain = { .ret: { Range }, .args: { { Domain, Range }, { Domain } } };
522static Signature bind_range = { .ret: { Domain }, .args: { { Domain, Range }, { Range } } };
523static Signature bind_domain_wrapped_domain =
524 { .ret: { Range2, Range }, .args: { { { Domain2, Range2 }, Range }, { Domain2 } } };
525
526/* Signatures for functions that take a callback accepting
527 * objects of the same kind (but a different type).
528 *
529 * The return and argument kinds of the callback appear
530 * at the position of the callback.
531 */
532static Signature each_params = { .ret: { Res }, .args: { { }, { Res }, { } } };
533static Signature each_set = { .ret: { Res }, .args: { { Domain }, { Res }, { Domain } } };
534static Signature each_map =
535 { .ret: { Res }, .args: { { Domain, Range }, { Res }, { Domain, Range } } };
536static std::vector<Signature> each = { each_params, each_set, each_map };
537
538/* Signatures for isl_*_list_foreach_scc.
539 *
540 * The first callback takes two elements with the same tuple kinds.
541 * The second callback takes a list with the same tuple kinds.
542 */
543static Signature each_scc_params =
544 { .ret: { Res }, .args: { { }, { Res }, { }, { }, { Res }, { } } };
545static Signature each_scc_set =
546 { .ret: { Res }, .args: { { Domain },
547 { Res }, { Domain }, { Domain },
548 { Res }, { Domain } } };
549static Signature each_scc_map =
550 { .ret: { Res }, .args: { { Domain, Range },
551 { Res }, { Domain, Range }, { Domain, Range },
552 { Res }, { Domain, Range } } };
553static std::vector<Signature> each_scc =
554 { each_scc_params, each_scc_set, each_scc_map };
555
556/* Signature for creating a map from a range,
557 * where the domain is given by an extra argument.
558 */
559static Signature map_from_range_and_domain =
560 { .ret: { Domain, Range }, .args: { { Range }, { Domain } } };
561
562/* Signature for creating a map from a domain,
563 * where the range is given by an extra argument.
564 */
565static Signature map_from_domain_and_range =
566 { .ret: { Domain, Range }, .args: { { Domain }, { Range } } };
567
568/* Signatures for creating an anonymous set from a parameter set
569 * or a map from a domain, where the range is anonymous.
570 */
571static Signature anonymous_set_from_params = { .ret: { Anonymous }, .args: { { } } };
572static Signature anonymous_map_from_domain =
573 { .ret: { Domain, Anonymous }, .args: { { Domain } } };
574static std::vector<Signature> anonymous_from_domain =
575 { anonymous_set_from_params, anonymous_map_from_domain };
576
577/* Signature for creating a set from a parameter set,
578 * where the domain is given by an extra argument.
579 */
580static Signature set_from_params = { .ret: { Domain }, .args: { { }, { Domain } } };
581
582/* Signatures for creating an anonymous function from a domain,
583 * where the second argument is an identifier (with an anonymous tuple).
584 */
585static Signature anonymous_set_from_params_bin_anon =
586 { .ret: { Anonymous }, .args: { { }, { Anonymous } } };
587static Signature anonymous_map_from_domain_bin_anon =
588 { .ret: { Domain, Anonymous }, .args: { { Domain }, { Anonymous } } };
589static std::vector<Signature> anonymous_from_domain_bin_anon = {
590 anonymous_set_from_params_bin_anon,
591 anonymous_map_from_domain_bin_anon
592 };
593
594/* Signature for creating a map from a domain,
595 * where the range tuple is equal to the domain tuple.
596 */
597static Signature set_to_map = { .ret: { Domain, Domain }, .args: { { Domain } } };
598
599/* Signatures for obtaining the range or the domain of a map.
600 * In case of a transformation, the domain and range are the same.
601 */
602static Signature domain = { .ret: { Domain }, .args: { { Domain, Range } } };
603static Signature range = { .ret: { Range }, .args: { { Domain, Range } } };
604static Signature transformation_domain = { .ret: { Domain }, .args: { { Domain, Domain } } };
605
606/* Signatures for obtaining the parameter domain of a set or map.
607 */
608static Signature set_params = { .ret: { }, .args: { { Domain } } };
609static Signature map_params = { .ret: { }, .args: { { Domain, Range } } };
610
611/* Signatures for obtaining the domain of a function.
612 */
613static std::vector<Signature> fn_domain = { domain, set_params };
614
615/* Signatures for interchanging (wrapped) domain and range.
616 */
617static Signature map_reverse = { .ret: { Range, Domain }, .args: { { Domain, Range } } };
618static Signature map_range_reverse =
619 { .ret: { Domain, { Range2, Range } }, .args: { { Domain, { Range, Range2 } } } };
620
621/* Signatures for constructing products.
622 */
623static Signature set_product =
624 { .ret: { { Domain, Range } }, .args: { { Domain }, { Range } } };
625static Signature map_product =
626 { .ret: { { Domain, Domain2 }, { Range, Range2 } },
627 .args: { { Domain, Range }, { Domain2, Range2 } } };
628static Signature domain_product =
629 { .ret: { { Domain, Domain2 }, Range },
630 .args: { { Domain, Range }, { Domain2, Range } } };
631static Signature range_product =
632 { .ret: { Domain, { Range, Range2 } },
633 .args: { { Domain, Range }, { Domain, Range2 } } };
634
635/* Signatures for obtaining factors from a product.
636 */
637static Signature domain_factor_domain =
638 { .ret: { Domain, Range }, .args: { { { Domain, Domain2 }, Range } } };
639static Signature domain_factor_range =
640 { .ret: { Domain2, Range }, .args: { { { Domain, Domain2 }, Range } } };
641static Signature range_factor_domain =
642 { .ret: { Domain, Range }, .args: { { Domain, { Range, Range2 } } } };
643static Signature range_factor_range =
644 { .ret: { Domain, Range2 }, .args: { { Domain, { Range, Range2 } } } };
645
646/* Signatures for (un)currying.
647 */
648static Signature curry =
649 { .ret: { Domain, { Range, Range2 } },
650 .args: { { { Domain, Range }, Range2 } } };
651static Signature uncurry =
652 { .ret: { { Domain, Range }, Range2 },
653 .args: { { Domain, { Range, Range2 } } } };
654
655/* Signatures for (un)wrapping.
656 */
657static Signature wrap = { .ret: { { Domain, Range } }, .args: { { Domain, Range } } };
658static Signature unwrap = { .ret: { Domain, Range }, .args: { { { Domain, Range } } } };
659
660/* Signatures for constructing objects that map to the domain or range
661 * of a map.
662 */
663static Signature domain_map =
664 { .ret: { { Domain, Range }, Domain }, .args: { { Domain, Range } } };
665static Signature range_map =
666 { .ret: { { Domain, Range }, Range }, .args: { { Domain, Range } } };
667
668/* Signature for applying a comparison between the domain and the range
669 * of a map.
670 */
671static Signature map_cmp =
672 { .ret: { Domain, Domain }, .args: { { Domain, Domain }, { Domain, Range } } };
673
674/* Signature for creating a set corresponding to the domains
675 * of two functions.
676 */
677static Signature set_join =
678 { .ret: { Domain }, .args: { { Domain, Range }, { Domain, Range } } };
679
680/* Signatures for flattening the domain or range of a map,
681 * replacing it with either an anonymous tuple or a tuple with a given name.
682 */
683static Signature anonymize_nested_domain =
684 { .ret: { Anonymous, Range2 }, .args: { { { Domain, Range }, Range2 } } };
685static Signature anonymize_nested_range =
686 { .ret: { Domain, Anonymous }, .args: { { Domain, { Range, Range2 } } } };
687static Signature replace_nested_domain =
688 { .ret: { Domain2, Range2 },
689 .args: { { { Domain, Range }, Range2 }, { Anonymous} } };
690static Signature replace_nested_range =
691 { .ret: { Domain, Range3 }, .args: { { Domain, { Range, Range2 } }, { Anonymous} } };
692static std::vector<Signature> flatten_domain =
693 { anonymize_nested_domain, replace_nested_domain };
694static std::vector<Signature> flatten_range =
695 { anonymize_nested_range, replace_nested_range };
696
697/* Signatures for "set_at" methods.
698 */
699static Signature set_at_set =
700 { .ret: { Domain }, .args: { { Domain }, { Integer }, { Anonymous } } };
701static Signature set_at_map =
702 { .ret: { Domain, Range },
703 .args: { { Domain, Range }, { Integer }, { Domain, Anonymous } } };
704static std::vector<Signature> set_at = { set_at_set, set_at_map };
705
706/* Signatures for "list" methods, extracting a list
707 * from a multi-expression.
708 */
709static Signature to_list_set = { .ret: { Anonymous }, .args: { { Domain } } };
710static Signature to_list_map = { .ret: { Domain, Anonymous }, .args: { { Domain, Range } } };
711
712/* Signatures for functions constructing an object from only an isl::ctx.
713 */
714static Signature ctx_params = { .ret: { }, .args: { { Ctx } } };
715static Signature ctx_set = { .ret: { Domain }, .args: { { Ctx } } };
716static Signature ctx_map = { .ret: { Domain, Range }, .args: { { Ctx } } };
717
718/* Helper structure for sorting the keys of static_methods and
719 * special_member_methods such that the larger keys appear first.
720 * In particular, a key should appear before any key that appears
721 * as a substring in the key.
722 * Note that this sorting is currently only important
723 * for special_member_methods.
724 */
725struct larger_infix {
726 bool operator()(const std::string &x, const std::string &y) const {
727 if (x.length() > y. length())
728 return true;
729 return x < y;
730 }
731};
732
733/* A map from part of a type name to a sequence of signatures.
734 */
735typedef std::map<std::string, std::vector<Signature>, larger_infix> infix_map;
736
737/* A map from a method name to a map from part of a type name
738 * to a sequence of signatures.
739 */
740typedef std::map<std::string, infix_map> infix_map_map;
741
742/* Signatures for static methods.
743 *
744 * The "unit" static method is only available in a 0-tuple space.
745 *
746 * The "empty" static method creates union objects with the relevant
747 * number of tuples.
748 *
749 * The "universe" static methods create objects from the corresponding spaces.
750 */
751static const infix_map_map static_methods {
752 { "unit",
753 { { "space", { ctx_params } } }
754 },
755 { "empty",
756 {
757 { "union_set", { ctx_params, ctx_set } },
758 { "union_map", { ctx_map } },
759 { "union_pw_multi_aff", { ctx_set, ctx_map } },
760 }
761 },
762 { "universe",
763 {
764 { "set", { un_params, un_set } },
765 { "map", { un_map } },
766 }
767 },
768};
769
770/* Signatures for unary operations that either take something in a set space
771 * and return something in the same space or take something in a map space
772 * and return something in the range of that space.
773 */
774static std::vector<Signature> range_op = { un_set, range };
775
776/* Signatures for binary operations where the second argument
777 * is a (multi-)value.
778 */
779static std::vector<Signature> bin_val = { bin_set, bin_map_range };
780
781/* The (default) signatures for methods with a given name.
782 * Some of these are overridden by special_member_methods.
783 */
784static const std::unordered_map<std::string, std::vector<Signature>>
785member_methods {
786 { "add", bin_op },
787 { "add_constant", bin_val },
788 { "add_named_tuple", { to_set_named, add_range_named } },
789 { "add_param", bin_op_anon },
790 { "add_unnamed_tuple", { to_set, add_range } },
791 { "apply", { set_forward, range_forward } },
792 { "apply_domain", { domain_forward } },
793 { "apply_range", { range_forward } },
794 { "as", un_op },
795 { "as_map", { un_map } },
796 { "as_union_map", { un_map } },
797 { "as_set", { un_set } },
798 { "bind", { bind_set, bind_range } },
799 { "bind_domain", { bind_domain } },
800 { "bind_range", { bind_range } },
801 { "bind_domain_wrapped_domain",
802 { bind_domain_wrapped_domain } },
803 { "ceil", fn_un_op },
804 { "coalesce", un_op },
805 { "cond", fn_ter_op },
806 { "constant", range_op },
807 { "curry", { curry } },
808 { "deltas", { transformation_domain } },
809 { "detect_equalities", un_op },
810 { "domain", fn_domain },
811 { "domain_factor_domain",
812 { domain_factor_domain } },
813 { "domain_factor_range",
814 { domain_factor_range } },
815 { "domain_map", { domain_map } },
816 { "domain_product", { domain_product } },
817 { "drop", ter_int_int },
818 { "eq_at", { map_cmp } },
819 { "every", each },
820 { "extract", bin_op },
821 { "flatten_domain", flatten_domain },
822 { "flatten_range", flatten_range },
823 { "floor", fn_un_op },
824 { "foreach", each },
825 { "foreach_scc", each_scc },
826 { "ge_set", { set_join } },
827 { "gt_set", { set_join } },
828 { "gist", bin_op },
829 { "gist_domain", { bin_map_domain } },
830 { "gist_params", { bin_set_params, bin_map_params } },
831 { "identity", { un_map, set_to_map } },
832 { "identity_on_domain", { set_to_map } },
833 { "indicator_function", anonymous_from_domain },
834 { "insert_domain", { map_from_range_and_domain } },
835 { "intersect", bin_op },
836 { "intersect_params", { bin_set_params, bin_map_params } },
837 { "intersect_domain", { bin_map_domain } },
838 { "intersect_domain_wrapped_domain",
839 { bin_map_domain_wrapped_domain } },
840 { "intersect_range", { bin_map_range } },
841 { "intersect_range_wrapped_domain",
842 { bin_map_range_wrapped_domain } },
843 { "lattice_tile", { un_set } },
844 { "le_set", { set_join } },
845 { "lt_set", { set_join } },
846 { "lex_le_at", { map_cmp } },
847 { "lex_lt_at", { map_cmp } },
848 { "lex_ge_at", { map_cmp } },
849 { "lex_gt_at", { map_cmp } },
850 { "lexmin", fn_un_op },
851 { "lexmax", fn_un_op },
852 { "list", { to_list_set, to_list_map } },
853 { "lower_bound", fn_bin_op },
854 { "map_from_set", { set_to_map } },
855 { "max", min_max },
856 { "max_val", range_op },
857 { "max_multi_val", range_op },
858 { "min", min_max },
859 { "min_val", range_op },
860 { "min_multi_val", range_op },
861 { "mod", bin_val },
862 { "on_domain", { map_from_domain_and_range } },
863 { "neg", fn_un_op },
864 { "offset", fn_un_op },
865 { "param_on_domain", anonymous_from_domain_bin_anon },
866 { "params", { set_params, map_params } },
867 { "plain_multi_val_if_fixed",
868 { un_set } },
869 { "preimage", { set_backward } },
870 { "preimage_domain", { domain_backward } },
871 { "preimage_domain_wrapped_domain",
872 { domain_wrapped_domain_backward } },
873 { "preimage_range", { range_backward } },
874 { "product", { set_product, map_product } },
875 { "project_out_param", bin_op_anon },
876 { "project_out_all_params",
877 un_op },
878 { "pullback", { domain_backward, bind_domain } },
879 { "range", { range } },
880 { "range_factor_domain",
881 { range_factor_domain } },
882 { "range_factor_range", { range_factor_range } },
883 { "range_lattice_tile", { un_map } },
884 { "range_map", { range_map } },
885 { "range_product", { range_product } },
886 { "range_reverse", { map_range_reverse } },
887 { "range_simple_fixed_box_hull",
888 { un_map } },
889 { "reverse", { map_reverse } },
890 { "scale", bin_val },
891 { "scale_down", bin_val },
892 { "set_at", set_at },
893 { "set_domain_tuple", { update_domain } },
894 { "set_range_tuple", { update_set, update_range } },
895 { "simple_fixed_box_hull",
896 { un_set } },
897 { "sub", fn_bin_op },
898 { "subtract", bin_op },
899 { "subtract_domain", { bin_map_domain } },
900 { "subtract_range", { bin_map_range } },
901 { "translation", { set_to_map } },
902 { "to", un_op },
903 { "unbind_params", { set_from_params } },
904 { "unbind_params_insert_domain",
905 { map_from_range_and_domain } },
906 { "uncurry", { uncurry } },
907 { "union_add", fn_bin_op },
908 { "unite", bin_op },
909 { "universe", un_op },
910 { "unwrap", { unwrap } },
911 { "upper_bound", fn_bin_op },
912 { "wrap", { wrap } },
913 { "zero", fn_un_op },
914 { "zero_on_domain", { anonymous_map_from_domain } },
915};
916
917/* Signatures for methods of types containing a given substring
918 * that override the default signatures, where larger substrings
919 * appear first.
920 *
921 * In particular, "gist" is usually a regular binary operation,
922 * but for any type derived from "aff", the argument refers
923 * to the domain of the function.
924 *
925 * The "size" method can usually simply be inherited from
926 * the corresponding plain C++ type, but for a "fixed_box",
927 * the size lives in the space of the box or its range.
928 *
929 * The "space" method is usually a regular unary operation
930 * that returns the single space of the elements in the object,
931 * with the same number of tuples.
932 * However, a "union" object may contain elements from many spaces and
933 * therefore its space only refers to the symbolic constants and
934 * has zero tuples, except if it is also a "multi_union" object,
935 * in which case it has a fixed range space and the space of the object
936 * has a single tuple.
937 * Note that since "space' is also the name of a template class,
938 * the default space method is handled by print_type_named_member_method.
939 */
940static const infix_map_map special_member_methods {
941 { "gist",
942 { { "aff", { bin_set_params, bin_map_domain } } }
943 },
944 { "size",
945 { { "fixed_box", range_op } },
946 },
947 { "space",
948 {
949 { "multi_union", range_op },
950 { "union", { un_params, set_params, map_params } },
951 }
952 },
953};
954
955/* Generic kinds for objects with zero, one or two tuples,
956 * the last of which may be anonymous.
957 */
958static Kind params{};
959static Kind set_type{ Domain };
960static Kind set_anon{ Anonymous };
961static Kind map_type{ Domain, Range };
962static Kind map_anon{ Domain, Anonymous };
963
964/* The initial sequence of specialization kinds for base types.
965 * The specialization kinds for other types are derived
966 * from the corresponding base types.
967 *
968 * In particular, this sequence specifies how many tuples
969 * a given type can have and whether it is anonymous.
970 *
971 * "space" can have any number of tuples.
972 * "set" and "point" can have zero or one tuple.
973 * "map" can only have two tuples.
974 * "aff" can have one or two tuples, the last of which is anonymous.
975 * "fixed_box" can represent a (proper) set) or a map.
976 * "val" and "id" are treated as anonymous sets so that
977 * they can form the basis of "multi_val" and "multi_id".
978 */
979static const std::unordered_map<std::string, std::vector<Kind>> base_kinds {
980 { "space", { params, set_type, map_type } },
981 { "set", { params, set_type } },
982 { "point", { params, set_type } },
983 { "map", { map_type } },
984 { "aff", { set_anon, map_anon } },
985 { "fixed_box", { set_type, map_type } },
986 { "val", { set_anon } },
987 { "id", { set_anon } },
988};
989
990/* Prefixes introduced by type constructors.
991 */
992static const std::unordered_set<std::string> type_prefixes {
993 "basic",
994 "multi",
995 "pw",
996 "union",
997};
998
999/* If "type" has a "_list" suffix, then return "type" with this suffix removed.
1000 * Otherwise, simply return "type".
1001 */
1002static std::string drop_list(const std::string &type)
1003{
1004 size_t pos = type.rfind(c: '_');
1005
1006 if (pos == std::string::npos)
1007 return type;
1008 if (type.substr(pos: pos + 1) == "list")
1009 return type.substr(pos: 0, n: pos);
1010 return type;
1011}
1012
1013/* Given the name of a plain C++ type, return the base type
1014 * from which it was derived using type constructors.
1015 *
1016 * In particular, drop any "list" suffix and
1017 * drop any prefixes from type_prefixes, stopping
1018 * as soon as a base type is found for which kinds have been registered
1019 * in base_kinds.
1020 */
1021static std::string base_type(const std::string &type)
1022{
1023 auto base = type;
1024 size_t pos;
1025
1026 base = drop_list(type: base);
1027 while (base_kinds.count(x: base) == 0 &&
1028 (pos = base.find(c: '_')) != std::string::npos &&
1029 type_prefixes.count(x: base.substr(pos: 0, n: pos)) != 0) {
1030 base = base.substr(pos: pos + 1);
1031 }
1032
1033 return base;
1034}
1035
1036/* A mapping from anonymous kinds to named kinds.
1037 */
1038static std::map<Kind, Kind> anon_to_named {
1039 { set_anon, set_type },
1040 { map_anon, map_type },
1041};
1042
1043/* Given a sequence of anonymous kinds, replace them
1044 * by the corresponding named kinds.
1045 */
1046static std::vector<Kind> add_name(const std::vector<Kind> &tuples)
1047{
1048 std::vector<Kind> named;
1049
1050 for (const auto &tuple : tuples)
1051 named.emplace_back(args&: anon_to_named.at(k: tuple));
1052
1053 return named;
1054}
1055
1056/* Look up the (initial) specializations of the class called "name".
1057 * If no specializations have been defined, then return an empty vector.
1058 *
1059 * Start from the initial specializations of the corresponding base type.
1060 * If this template class is a multi-expression, then it was derived
1061 * from an anonymous function type. Replace the final Anonymous
1062 * tuple kind by a placeholder in this case.
1063 */
1064static std::vector<Kind> lookup_class_tuples(const std::string &name)
1065{
1066 std::string base = base_type(type: name);
1067
1068 if (base_kinds.count(x: base) == 0)
1069 return { };
1070 if (name.find(s: "multi_") != std::string::npos)
1071 return add_name(tuples: base_kinds.at(k: base));
1072 return base_kinds.at(k: base);
1073}
1074
1075/* Add a template class called "name", of which the methods are described
1076 * by "clazz" and the initial specializations by "class_tuples".
1077 */
1078void template_cpp_generator::add_template_class(const isl_class &clazz,
1079 const std::string &name, const std::vector<Kind> &class_tuples)
1080{
1081 auto isl_namespace = cpp_type_printer().isl_namespace();
1082 auto super = isl_namespace + name;
1083
1084 template_classes.emplace(args: name,
1085 args: template_class{.class_name: name, .super_name: super, .clazz: clazz, .class_tuples: class_tuples});
1086}
1087
1088/* Construct a templated C++ bindings generator from
1089 * the exported types and functions and the set of all declared functions.
1090 *
1091 * On top of the initialization of the shared parts
1092 * of C++ bindings generators, add a template class
1093 * for each plain C++ class for which template kinds
1094 * have been defined.
1095 * In particular, determine the base type from which the plain C++ class
1096 * was derived using type constructors and check if any template kinds
1097 * have been registered for this base type.
1098 */
1099template_cpp_generator::template_cpp_generator(clang::SourceManager &SM,
1100 std::set<clang::RecordDecl *> &exported_types,
1101 std::set<clang::FunctionDecl *> exported_functions,
1102 std::set<clang::FunctionDecl *> functions) :
1103 cpp_generator(SM, exported_types, exported_functions,
1104 functions)
1105{
1106 for (const auto &kvp : classes) {
1107 const auto &clazz = kvp.second;
1108 std::string name = type2cpp(clazz);
1109 const auto &class_tuples = lookup_class_tuples(name);
1110
1111 if (class_tuples.empty())
1112 continue;
1113 add_template_class(clazz: clazz, name, class_tuples: class_tuples);
1114 }
1115}
1116
1117/* Call "fn" on each template class.
1118 */
1119void template_cpp_generator::foreach_template_class(
1120 const std::function<void(const template_class &)> &fn) const
1121{
1122 for (const auto &kvp : template_classes)
1123 fn(kvp.second);
1124}
1125
1126/* Print forward declarations for all template classes to "os".
1127 *
1128 * For template classes that represent an anonymous function
1129 * that can also have a domain tuple, provide an <name>_on alias
1130 * that adds the fixed Anonymous tuple kind.
1131 */
1132void template_cpp_generator::print_forward_declarations(std::ostream &os)
1133{
1134 foreach_template_class(fn: [&os] (const template_class &template_class) {
1135 auto name = template_class.class_name;
1136
1137 os << "\n";
1138 os << "template <typename...>\n";
1139 os << "struct " << name << ";\n";
1140
1141 if (!template_class.is_anon())
1142 return;
1143 if (template_class.is_anon_set())
1144 return;
1145
1146 os << "\n";
1147 os << "template <typename...Ts>\n";
1148 os << "using " << name << "_on = "
1149 << name << "<Ts..., Anonymous>;\n";
1150 });
1151}
1152
1153/* Print friend declarations for all template classes to "os".
1154 */
1155void template_cpp_generator::print_friends(std::ostream &os)
1156{
1157 foreach_template_class(fn: [&os] (const template_class &template_class) {
1158 os << " template <typename...>\n";
1159 os << " friend struct " << template_class.class_name << ";\n";
1160 });
1161}
1162
1163/* Print a template parameter or argument.
1164 * In case of a std::string, it's a template parameter
1165 * that needs to be declared.
1166 */
1167static void print_template_arg(std::ostream &os, const std::string &arg)
1168{
1169 os << "typename " << arg;
1170}
1171
1172/* Print a template parameter or argument.
1173 * In case of a TupleKindPtr, it's a template argument.
1174 */
1175static void print_template_arg(std::ostream &os, const TupleKindPtr &kind)
1176{
1177 os << kind->to_string();
1178}
1179
1180/* Print a sequence of template parameters (std::string) or
1181 * arguments (TupleKindPtr) "args", without the enclosing angle brackets.
1182 */
1183template <typename List>
1184static void print_pure_template_args(std::ostream &os, const List &args)
1185{
1186 for (size_t i = 0; i < args.size(); ++i) {
1187 if (i != 0)
1188 os << ", ";
1189 print_template_arg(os, args[i]);
1190 }
1191}
1192
1193/* Print a sequence of template parameters (std::string) or
1194 * arguments (TupleKindPtr) "args".
1195 */
1196template <typename List>
1197static void print_template_args(std::ostream &os, const List &args)
1198{
1199 os << "<";
1200 print_pure_template_args(os, args);
1201 os << ">";
1202}
1203
1204/* Print a declaration of the template parameters "params".
1205 */
1206static void print_template(std::ostream &os,
1207 const std::vector<std::string> &params)
1208{
1209 os << "template ";
1210 print_template_args(os, args: params);
1211 os << "\n";
1212}
1213
1214/* Print a declaration of the template parameters "params",
1215 * if there are any.
1216 */
1217static void print_non_empty_template(std::ostream &os,
1218 const std::vector<std::string> &params)
1219{
1220 if (params.size() > 0)
1221 print_template(os, params);
1222}
1223
1224/* Print a bare template type, i.e., without namespace,
1225 * consisting of the type "type" and the kind "kind" to "os".
1226 *
1227 * In particular, print "type" followed by the template arguments
1228 * as specified by "kind".
1229 */
1230static void print_bare_template_type(std::ostream &os, const std::string &type,
1231 const Kind &kind)
1232{
1233 os << type;
1234 print_template_args(os, args: kind);
1235}
1236
1237/* A specific instance of "template_class", with tuple kinds given by "kind".
1238 */
1239struct specialization {
1240 struct template_class &template_class;
1241 Kind kind;
1242
1243 const std::string &base_name() const;
1244 const std::string &class_name() const;
1245};
1246
1247/* The name of the plain C++ interface class
1248 * from which this template class (instance) derives.
1249 */
1250const std::string &specialization::base_name() const
1251{
1252 return template_class.super_name;
1253}
1254
1255/* The name of the template class.
1256 */
1257const std::string &specialization::class_name() const
1258{
1259 return template_class.class_name;
1260}
1261
1262/* Helper class for printing the specializations of template classes
1263 * that is used to print both the class declarations and the class definitions.
1264 *
1265 * "os" is the stream onto which the classes should be printed.
1266 * "generator" is the templated C++ interface generator printing the classes.
1267 */
1268struct specialization_printer {
1269 specialization_printer(std::ostream &os,
1270 template_cpp_generator &generator) :
1271 os(os), generator(generator) {}
1272
1273 virtual void print_class(const specialization &instance) const = 0;
1274 void print_classes() const;
1275
1276 std::ostream &os;
1277 template_cpp_generator &generator;
1278};
1279
1280/* Print all specializations of all template classes.
1281 *
1282 * Each class has a predefined set of initial specializations,
1283 * but while such a specialization is being printed,
1284 * the need for other specializations may arise and
1285 * these are added at the end of the list of specializations.
1286 * That is, class_tuples.size() may change during the execution
1287 * of the loop.
1288 *
1289 * For each specialization of a template class, call
1290 * the print_class virtual method.
1291 */
1292void specialization_printer::print_classes() const
1293{
1294 for (auto &kvp : generator.template_classes) {
1295 auto &template_class = kvp.second;
1296 const auto &class_tuples = template_class.class_tuples;
1297
1298 for (size_t i = 0; i < class_tuples.size(); ++i)
1299 print_class(instance: { .template_class: template_class, .kind: class_tuples[i] });
1300 }
1301}
1302
1303/* A helper class for printing method declarations and definitions
1304 * of a template class specialization.
1305 *
1306 * "instance" is the template class specialization for which methods
1307 * are printed.
1308 * "generator" is the templated C++ interface generator printing the classes.
1309 */
1310struct template_cpp_generator::class_printer :
1311 public cpp_generator::class_printer {
1312 class_printer(const specialization &instance,
1313 const specialization_printer &instance_printer,
1314 bool is_declaration);
1315
1316 void print_return_type(const Method &method, const Kind &kind)
1317 const;
1318 void print_method_template_arguments(const Signature &sig);
1319 void print_method_header(const Method &method, const Signature &sig);
1320 bool print_special_method(const Method &method,
1321 const infix_map_map &special_methods);
1322 void print_static_method(const Method &method);
1323 void print_constructor(const Method &method);
1324 bool is_return_kind(const Method &method, const Kind &return_kind);
1325 void add_specialization(const Kind &kind);
1326 bool print_matching_method(const Method &method, const Signature &sig,
1327 const Kind &match_arg);
1328 bool print_matching_method(const Method &method, const Signature &sig);
1329 void print_matching_method(const Method &method,
1330 const std::vector<Signature> &signatures);
1331 void print_at_method(const Method &method);
1332 bool print_special_member_method(const Method &method);
1333 bool print_type_named_member_method(const Method &method);
1334 bool print_member_method_with_name(const Method &method,
1335 const std::string &name);
1336 void print_member_method(const Method &method);
1337 void print_any_method(const Method &method);
1338 virtual void print_method(const Method &method) override;
1339 virtual void print_method(const ConversionMethod &method) override;
1340 virtual void print_method_sig(const Method &method,
1341 const Signature &sig, bool deleted) = 0;
1342 virtual bool want_descendent_overloads(const function_set &methods)
1343 override;
1344 void print_all_methods();
1345
1346 const specialization &instance;
1347 template_cpp_generator &generator;
1348};
1349
1350/* Construct a class_printer from the template class specialization
1351 * for which methods are printed and
1352 * the printer of the template class.
1353 *
1354 * The template class printer is only used to obtain the output stream and
1355 * the templated C++ interface generator printing the classes.
1356 */
1357template_cpp_generator::class_printer::class_printer(
1358 const specialization &instance,
1359 const specialization_printer &instance_printer,
1360 bool is_declaration) :
1361 cpp_generator::class_printer(instance_printer.os,
1362 instance.template_class.clazz, instance_printer.generator,
1363 is_declaration),
1364 instance(instance), generator(instance_printer.generator)
1365{
1366}
1367
1368/* An abstract template type printer, where the way of obtaining
1369 * the argument kind is specified by the subclasses.
1370 */
1371struct template_cpp_type_printer : public cpp_type_printer {
1372 template_cpp_type_printer() {}
1373
1374 std::string base(const std::string &type, const Kind &kind) const;
1375 virtual Kind kind(int arg) const = 0;
1376 virtual std::string qualified(int arg, const std::string &cpp_type)
1377 const override;
1378};
1379
1380/* Print a template type consisting of the type "type" and the kind "kind",
1381 * including the "typed::" namespace specifier.
1382 */
1383std::string template_cpp_type_printer::base(const std::string &type,
1384 const Kind &kind) const
1385{
1386 std::ostringstream ss;
1387
1388 ss << "typed::";
1389 print_bare_template_type(os&: ss, type, kind);
1390 return ss.str();
1391}
1392
1393/* Return the qualified form of the given C++ isl type name appearing
1394 * in argument position "arg" (-1 for return type).
1395 *
1396 * isl::ctx is not templated, so if "cpp_type" is "ctx",
1397 * then print a non-templated version.
1398 * Otherwise, look up the kind of the argument and print
1399 * the corresponding template type.
1400 */
1401std::string template_cpp_type_printer::qualified(int arg,
1402 const std::string &cpp_type) const
1403{
1404 if (cpp_type == "ctx")
1405 return cpp_type_printer::qualified(arg, cpp_type);
1406
1407 return base(type: cpp_type, kind: kind(arg));
1408}
1409
1410/* A template type printer for printing types with a fixed kind.
1411 *
1412 * "fixed_kind" is the fixed kind.
1413 */
1414struct template_cpp_kind_type_printer : public template_cpp_type_printer {
1415 template_cpp_kind_type_printer(const Kind &kind) :
1416 template_cpp_type_printer(), fixed_kind(kind) {}
1417
1418 virtual Kind kind(int arg) const override;
1419
1420 const Kind &fixed_kind;
1421};
1422
1423/* Return the kind of the argument at position "arg",
1424 * where position -1 refers to the return type.
1425 *
1426 * Always use the fixed kind.
1427 */
1428Kind template_cpp_kind_type_printer::kind(int arg) const
1429{
1430 return fixed_kind;
1431}
1432
1433/* A template type printer for printing a method with a given signature.
1434 *
1435 * "sig" is the signature of the method being printed.
1436 */
1437struct template_cpp_arg_type_printer : public template_cpp_type_printer {
1438 template_cpp_arg_type_printer(const Signature &sig) :
1439 template_cpp_type_printer(), sig(sig) {}
1440
1441 virtual Kind kind(int arg) const override;
1442
1443 const Signature &sig;
1444};
1445
1446/* Return the kind of the argument at position "arg",
1447 * where position -1 refers to the return type.
1448 *
1449 * Look up the kind in the signature.
1450 */
1451Kind template_cpp_arg_type_printer::kind(int arg) const
1452{
1453 int n_args = sig.args.size();
1454
1455 if (arg < 0)
1456 return sig.ret;
1457 if (arg >= n_args)
1458 generator::die(msg: "argument out of bounds");
1459 return sig.args[arg];
1460}
1461
1462/* A template type printer for printing a method with a given signature
1463 * as part of a template class specialization of a given kind.
1464 *
1465 * "class_kind" is the template class specialization kind.
1466 */
1467struct template_method_type_printer : public template_cpp_arg_type_printer {
1468 template_method_type_printer(const Signature &sig,
1469 const Kind &class_kind) :
1470 template_cpp_arg_type_printer(sig),
1471 class_kind(class_kind) {}
1472
1473 virtual std::string class_type(const std::string &cpp_name)
1474 const override;
1475
1476 const Kind &class_kind;
1477};
1478
1479/* Print the class type "cpp_name".
1480 *
1481 * Print the templated version using the template class specialization kind.
1482 */
1483std::string template_method_type_printer::class_type(
1484 const std::string &cpp_name) const
1485{
1486 return base(type: cpp_name, kind: class_kind);
1487}
1488
1489/* Print the templated return type of "method" of the kind "return_kind".
1490 *
1491 * Construct a type printer with "return_kind" as fixed kind and
1492 * use it to print the return type.
1493 */
1494void template_cpp_generator::class_printer::print_return_type(
1495 const Method &method, const Kind &return_kind) const
1496{
1497 template_cpp_kind_type_printer printer(return_kind);
1498
1499 os << printer.return_type(method);
1500}
1501
1502/* Remove the initial "n" elements from "v".
1503 */
1504template <typename T>
1505static void drop_initial(std::vector<T> &v, size_t n)
1506{
1507 v.erase(v.begin(), v.begin() + n);
1508}
1509
1510/* If a method with signature "sig" requires additional template parameters
1511 * compared to those of the class, then print a declaration for them.
1512 * If this->declarations is set, then this will be part of a method declaration,
1513 * requiring extra indentation.
1514 *
1515 * Construct the sequence of all required template parameters
1516 * with those of the template class appearing first.
1517 * If this sequence has any parameters not induced by the template class itself,
1518 * then print a declaration for these extra parameters.
1519 */
1520void template_cpp_generator::class_printer::print_method_template_arguments(
1521 const Signature &sig)
1522{
1523 std::vector<std::string> class_params, method_params;
1524
1525 class_params = instance.kind.params();
1526 method_params = class_params;
1527 combine(vec1&: method_params, vec2: sig.params());
1528
1529 if (class_params.size() == method_params.size())
1530 return;
1531
1532 drop_initial(v&: method_params, n: class_params.size());
1533
1534 if (declarations)
1535 os << " ";
1536 print_template(os, params: method_params);
1537}
1538
1539/* Print the header for "method" with signature "sig".
1540 *
1541 * First print any additional template parameters that may be required and
1542 * then print a regular method header, using a template type printer.
1543 */
1544void template_cpp_generator::class_printer::print_method_header(
1545 const Method &method, const Signature &sig)
1546{
1547 template_method_type_printer type_printer(sig, instance.kind);
1548
1549 print_method_template_arguments(sig);
1550 cpp_generator::class_printer::print_method_header(method,
1551 type_printer);
1552}
1553
1554/* Given a group of methods with the same name,
1555 * should extra methods be added that take as arguments
1556 * those types that can be converted to the original argument type
1557 * through a unary constructor?
1558 *
1559 * Since type deduction does not consider implicit conversions,
1560 * these extra methods should always be printed.
1561 */
1562bool template_cpp_generator::class_printer::want_descendent_overloads(
1563 const function_set &methods)
1564{
1565 return true;
1566}
1567
1568/* Print all constructors and methods that forward
1569 * to the corresponding methods in the plain C++ interface class.
1570 */
1571void template_cpp_generator::class_printer::print_all_methods()
1572{
1573 print_constructors();
1574 print_methods();
1575}
1576
1577/* A helper class for printing method declarations
1578 * of a template class specialization.
1579 */
1580struct template_cpp_generator::method_decl_printer :
1581 public template_cpp_generator::class_printer {
1582 method_decl_printer(const specialization &instance,
1583 const struct specialization_printer &instance_printer) :
1584 class_printer(instance, instance_printer, true) {}
1585
1586 virtual void print_method_sig(const Method &method,
1587 const Signature &sig, bool deleted) override;
1588 virtual void print_get_method(FunctionDecl *fd) override;
1589};
1590
1591/* Print a declaration of the method "method" with signature "sig".
1592 * Mark is "delete" if "deleted" is set.
1593 */
1594void template_cpp_generator::method_decl_printer::print_method_sig(
1595 const Method &method, const Signature &sig, bool deleted)
1596{
1597 print_method_header(method, sig);
1598 if (deleted)
1599 os << " = delete";
1600 os << ";\n";
1601}
1602
1603/* Return the total number of arguments in the signature for "method",
1604 * taking into account any possible callback arguments.
1605 *
1606 * In particular, if the method has a callback argument,
1607 * then the return kind of the callback appears at the position
1608 * of the callback and the kinds of the arguments (except
1609 * the user pointer argument) appear in the following positions.
1610 * The user pointer argument that follows the callback argument
1611 * is also removed.
1612 */
1613static int total_params(const Method &method)
1614{
1615 int n = method.num_params();
1616
1617 for (const auto &callback : method.callbacks) {
1618 auto callback_type = callback->getType();
1619 auto proto = generator::extract_prototype(callback_type);
1620
1621 n += proto->getNumArgs() - 1;
1622 n -= 1;
1623 }
1624
1625 return n;
1626}
1627
1628/* Return a signature for "method" that matches "instance".
1629 */
1630static Signature instance_sig(const Method &method,
1631 const specialization &instance)
1632{
1633 std::vector<Kind> args(total_params(method));
1634
1635 args[0] = instance.kind;
1636 return { .ret: instance.kind, .args: args };
1637}
1638
1639/* Print a declaration for the "get" method "fd",
1640 * using a name that includes the "get_" prefix.
1641 *
1642 * These methods are only included in the plain interface.
1643 * Explicitly delete them from the templated interface.
1644 */
1645void template_cpp_generator::method_decl_printer::print_get_method(
1646 FunctionDecl *fd)
1647{
1648 Method method(clazz, fd, clazz.base_method_name(fd));
1649
1650 print_method_sig(method, sig: instance_sig(method, instance), deleted: true);
1651}
1652
1653/* A helper class for printing method definitions
1654 * of a template class specialization.
1655 */
1656struct template_cpp_generator::method_impl_printer :
1657 public template_cpp_generator::class_printer {
1658 method_impl_printer(const specialization &instance,
1659 const struct specialization_printer &instance_printer) :
1660 class_printer(instance, instance_printer, false) {}
1661
1662 void print_callback_method_body(const Method &method,
1663 const Signature &sig);
1664 void print_method_body(const Method &method, const Signature &sig);
1665 void print_constructor_body(const Method &method, const Signature &sig);
1666 virtual void print_method_sig(const Method &method,
1667 const Signature &sig, bool deleted) override;
1668 virtual void print_get_method(FunctionDecl *fd) override;
1669};
1670
1671/* Print a definition of the constructor "method" with signature "sig".
1672 *
1673 * Simply pass all arguments to the constructor of the corresponding
1674 * plain type.
1675 */
1676void template_cpp_generator::method_impl_printer::print_constructor_body(
1677 const Method &method, const Signature &sig)
1678{
1679 const auto &base_name = instance.base_name();
1680
1681 os << " : " << base_name;
1682 method.print_cpp_arg_list(os, print_arg: [&] (int i, int arg) {
1683 os << method.fd->getParamDecl(i)->getName().str();
1684 });
1685 os << "\n";
1686
1687 os << "{\n";
1688 os << "}\n";
1689}
1690
1691/* Print the arguments of the callback function "callback" to "os",
1692 * calling "print_arg" with the type and the name of the arguments,
1693 * where the type is obtained from "type_printer" with argument positions
1694 * shifted by "shift".
1695 * None of the arguments should be skipped.
1696 */
1697static void print_callback_args(std::ostream &os,
1698 const FunctionProtoType *callback, const cpp_type_printer &type_printer,
1699 int shift,
1700 const std::function<void(const std::string &type,
1701 const std::string &name)> &print_arg)
1702{
1703 auto n_arg = callback->getNumArgs() - 1;
1704
1705 Method::print_arg_list(os, start: 0, end: n_arg, print_arg_skip_next: [&] (int i) {
1706 auto type = callback->getArgType(i);
1707 auto name = "arg" + std::to_string(val: i);
1708 auto cpptype = type_printer.param(shift + i, type);
1709
1710 print_arg(cpptype, name);
1711
1712 return false;
1713 });
1714}
1715
1716/* Print a lambda corresponding to "callback"
1717 * with signature "sig" and argument positions shifted by "shift".
1718 *
1719 * The lambda takes arguments with plain isl types and
1720 * calls the callback of "method" with templated arguments.
1721 */
1722static void print_callback_lambda(std::ostream &os, ParmVarDecl *callback,
1723 const Signature &sig, int shift)
1724{
1725 auto callback_type = callback->getType();
1726 auto callback_name = callback->getName().str();
1727 auto proto = generator::extract_prototype(callback_type);
1728
1729 os << " auto lambda_" << callback_name << " = [&] ";
1730 print_callback_args(os, proto, cpp_type_printer(), shift,
1731 [&] (const std::string &type, const std::string &name) {
1732 os << type << " " << name;
1733 });
1734 os << " {\n";
1735
1736 os << " return " << callback_name;
1737 print_callback_args(os, proto, template_cpp_arg_type_printer(sig),
1738 shift,
1739 [&] (const std::string &type, const std::string &name) {
1740 os << type << "(" << name << ")";
1741 });
1742 os << ";\n";
1743
1744 os << " };\n";
1745}
1746
1747/* Print lambdas for passing to the plain method corresponding to "method"
1748 * with signature "sig".
1749 *
1750 * The method is assumed to have only callbacks as argument,
1751 * which means the arguments of the first callback are shifted by 2
1752 * with respect to the arguments of the signature
1753 * (one for the position of the callback argument plus
1754 * one for the return kind of the callback).
1755 * The arguments of a subsequent callback are shifted by
1756 * the number of arguments of the previous callback minus one
1757 * for the user pointer plus one for the return kind.
1758 */
1759static void print_callback_lambdas(std::ostream &os, const Method &method,
1760 const Signature &sig)
1761{
1762 int shift;
1763
1764 if (method.num_params() != 1 + 2 * method.callbacks.size())
1765 generator::die(msg: "callbacks are assumed to be only arguments");
1766
1767 shift = 2;
1768 for (const auto &callback : method.callbacks) {
1769 print_callback_lambda(os, callback, sig, shift);
1770 shift += generator::prototype_n_args(callback->getType());
1771 }
1772}
1773
1774/* Print a definition of the member method "method", which is known
1775 * to have a callback argument, with signature "sig".
1776 *
1777 * First print lambdas for passing to the corresponding plain method and
1778 * calling the callback of "method" with templated arguments.
1779 * Then call the plain method, replacing the original callbacks
1780 * by the lambdas.
1781 *
1782 * The return value is assumed to be isl_bool or isl_stat
1783 * so that no conversion to a template type is required.
1784 */
1785void template_cpp_generator::method_impl_printer::print_callback_method_body(
1786 const Method &method, const Signature &sig)
1787{
1788 const auto &base_name = instance.base_name();
1789 auto return_type = method.fd->getReturnType();
1790
1791 if (!is_isl_bool(return_type) && !is_isl_stat(return_type))
1792 die(msg: "only isl_bool and isl_stat return types are supported");
1793
1794 os << "{\n";
1795
1796 print_callback_lambdas(os, method, sig);
1797
1798 os << " return ";
1799 os << base_name << "::" << method.name;
1800 method.print_cpp_arg_list(os, print_arg: [&] (int i, int arg) {
1801 auto param = method.fd->getParamDecl(i);
1802
1803 if (generator::is_callback(param->getType()))
1804 os << "lambda_";
1805 os << param->getName().str();
1806 });
1807 os << ";\n";
1808
1809 os << "}\n";
1810}
1811
1812/* Print a definition of the member or static method "method"
1813 * with signature "sig".
1814 *
1815 * The body calls the corresponding method of the base class
1816 * in the plain interface and
1817 * then casts the result to the templated result type.
1818 */
1819void template_cpp_generator::method_impl_printer::print_method_body(
1820 const Method &method, const Signature &sig)
1821{
1822 const auto &base_name = instance.base_name();
1823
1824 os << "{\n";
1825 os << " auto res = ";
1826 os << base_name << "::" << method.name;
1827 method.print_cpp_arg_list(os, print_arg: [&] (int i, int arg) {
1828 os << method.fd->getParamDecl(i)->getName().str();
1829 });
1830 os << ";\n";
1831
1832 os << " return ";
1833 print_return_type(method, return_kind: sig.ret);
1834 os << "(res);\n";
1835 os << "}\n";
1836}
1837
1838/* Print a definition of the method "method" with signature "sig",
1839 * if "deleted" is not set.
1840 *
1841 * If "deleted" is set, then the corresponding declaration
1842 * is marked "delete" and no definition needs to be printed.
1843 *
1844 * Otherwise print the method header, preceded by the template parameters,
1845 * if needed.
1846 * The body depends on whether the method is a constructor or
1847 * takes any callbacks.
1848 */
1849void template_cpp_generator::method_impl_printer::print_method_sig(
1850 const Method &method, const Signature &sig, bool deleted)
1851{
1852 if (deleted)
1853 return;
1854
1855 os << "\n";
1856 print_non_empty_template(os, params: instance.kind.params());
1857 print_method_header(method, sig);
1858 os << "\n";
1859 if (method.kind == Method::Kind::constructor)
1860 print_constructor_body(method, sig);
1861 else if (method.callbacks.size() != 0)
1862 print_callback_method_body(method, sig);
1863 else
1864 print_method_body(method, sig);
1865}
1866
1867/* Print a definition for the "get" method "fd" in class "clazz",
1868 * using a name that includes the "get_" prefix, to "os".
1869 *
1870 * The declarations of these methods are explicitly delete'd
1871 * so no definition needs to be printed.
1872 */
1873void template_cpp_generator::method_impl_printer::print_get_method(
1874 FunctionDecl *fd)
1875{
1876}
1877
1878/* Print a declaration or definition of the static method "method",
1879 * if it has a signature specified by static_methods.
1880 */
1881void template_cpp_generator::class_printer::print_static_method(
1882 const Method &method)
1883{
1884 print_special_method(method, special_methods: static_methods);
1885}
1886
1887/* Signatures for constructors of multi-expressions
1888 * from a space and a list.
1889 */
1890static Signature from_list_set = { .ret: { Domain }, .args: { { Domain }, { Anonymous } } };
1891static Signature from_list_map =
1892 { .ret: { Domain, Range }, .args: { { Domain, Range }, { Domain, Anonymous } } };
1893
1894/* Signatures for constructors from a string.
1895 */
1896static Signature params_from_str = { .ret: { }, .args: { { Ctx }, { Str } } };
1897static Signature set_from_str = { .ret: { Domain }, .args: { { Ctx }, { Str } } };
1898static Signature map_from_str = { .ret: { Domain, Range }, .args: { { Ctx }, { Str } } };
1899static std::vector<Signature> from_str =
1900 { params_from_str, set_from_str, map_from_str };
1901
1902/* Signature for a constructor from an integer.
1903 */
1904static Signature int_from_si = { .ret: { Anonymous }, .args: { { Ctx }, { Integer } } };
1905
1906/* Signatures for constructors of lists from the initial number
1907 * of elements.
1908 */
1909static Signature alloc_params = { .ret: { }, .args: { { Ctx }, { Integer } } };
1910static Signature alloc_set = { .ret: { Domain }, .args: { { Ctx }, { Integer } } };
1911static Signature alloc_map = { .ret: { Domain, Range }, .args: { { Ctx }, { Integer } } };
1912
1913/* Signatures for constructors and methods named after some other class.
1914 *
1915 * Two forms of constructors are handled
1916 * - conversion from another object
1917 * - construction of a multi-expression from a space and a list
1918 *
1919 * Methods named after some other class also come in two forms
1920 * - extraction of information such as the space or a list
1921 * - construction of a multi-expression from a space and a list
1922 *
1923 * In both cases, the first form is a unary operation and
1924 * the second has an extra argument with a kind that is equal
1925 * to that of the first argument, except that the final tuple is anonymous.
1926 */
1927static std::vector<Signature> constructor_sig = {
1928 un_params,
1929 un_set,
1930 un_map,
1931 from_list_set,
1932 from_list_map,
1933};
1934
1935/* Signatures for constructors derived from methods
1936 * with the given names that override the default signatures.
1937 */
1938static const std::unordered_map<std::string, std::vector<Signature>>
1939special_constructors {
1940 { "alloc", { alloc_params, alloc_set, alloc_map } },
1941 { "int_from_si", { int_from_si } },
1942 { "read_from_str", from_str },
1943};
1944
1945/* Print a declaration or definition of the constructor "method".
1946 */
1947void template_cpp_generator::class_printer::print_constructor(
1948 const Method &method)
1949{
1950 if (special_constructors.count(x: method.name) != 0) {
1951 const auto &sigs = special_constructors.at(k: method.name);
1952 return print_matching_method(method, signatures: sigs);
1953 }
1954 print_matching_method(method, signatures: constructor_sig);
1955}
1956
1957/* Does this template class represent an anonymous function?
1958 *
1959 * If any specialization represents an anonymous function,
1960 * then every specialization does, so simply check
1961 * the first specialization.
1962 */
1963bool template_class::is_anon() const
1964{
1965 return class_tuples[0].is_anon();
1966}
1967
1968/* Does this template class represent an anonymous value?
1969 *
1970 * That is, is there only a single specialization that moreover
1971 * has a single, anonymous tuple?
1972 */
1973bool template_class::is_anon_set() const
1974{
1975 return class_tuples.size() == 1 && class_tuples[0].is_anon_set();
1976}
1977
1978/* Update the substitution "sub" to map "general" to "specific"
1979 * if "specific" is a special case of "general" consistent with "sub",
1980 * given that "general" is not a pair and can be assigned "specific".
1981 * Return true if successful.
1982 * Otherwise, return false.
1983 *
1984 * Check whether "general" is already assigned something in "sub".
1985 * If so, it must be assigned "specific".
1986 * Otherwise, there is a conflict.
1987 */
1988static bool update_sub_base(Substitution &sub, const TupleKindPtr &general,
1989 const TupleKindPtr &specific)
1990{
1991 auto name = general->name;
1992
1993 if (sub.count(x: name) != 0 && sub.at(k: name) != specific)
1994 return false;
1995 sub.emplace(args&: name, args: specific);
1996 return true;
1997}
1998
1999/* Update the substitution "sub" to map "general" to "specific"
2000 * if "specific" is a special case of "general" consistent with "sub".
2001 * Return true if successful.
2002 * Otherwise, return false.
2003 *
2004 * If "general" is a pair and "specific" is not,
2005 * then "specific" cannot be a special case.
2006 * If both are pairs, then update the substitution based
2007 * on both sides.
2008 * If "general" is Anonymous, then "specific" must be Anonymous as well.
2009 * If "general" is Leaf, then "specific" cannot be a pair.
2010 *
2011 * Otherwise, assign "specific" to "general", if possible.
2012 */
2013static bool update_sub(Substitution &sub, const TupleKindPtr &general,
2014 const TupleKindPtr &specific)
2015{
2016 if (general->left() && !specific->left())
2017 return false;
2018 if (general->left())
2019 return update_sub(sub, general: general->left(), specific: specific->left()) &&
2020 update_sub(sub, general: general->right(), specific: specific->right());
2021 if (general == Anonymous && specific != Anonymous)
2022 return false;
2023 if (general == Leaf && specific->left())
2024 return false;
2025
2026 return update_sub_base(sub, general, specific);
2027}
2028
2029/* Check if "specific" is a special case of "general" and,
2030 * if so, return true along with a substitution
2031 * that maps "general" to "specific".
2032 * Otherwise return false.
2033 *
2034 * This can only happen if the number of tuple kinds is the same.
2035 * If so, start with an empty substitution and update it
2036 * for each pair of tuple kinds, checking that each update succeeds.
2037 */
2038static std::pair<bool, Substitution> specializer(const Kind &general,
2039 const Kind &specific)
2040{
2041 Substitution specializer;
2042
2043 if (general.size() != specific.size())
2044 return { false, Substitution() };
2045
2046 for (size_t i = 0; i < general.size(); ++i) {
2047 auto general_tuple = general[i];
2048
2049 if (!update_sub(sub&: specializer, general: general[i], specific: specific[i]))
2050 return { false, Substitution() };
2051 }
2052
2053 return { true, specializer };
2054}
2055
2056/* Is "kind1" equivalent to "kind2"?
2057 * That is, is each a special case of the other?
2058 */
2059static bool equivalent(const Kind &kind1, const Kind &kind2)
2060{
2061 return specializer(general: kind1, specific: kind2).first &&
2062 specializer(general: kind2, specific: kind1).first;
2063}
2064
2065/* Add the specialization "kind" to the sequence of specializations,
2066 * provided there is no equivalent specialization already in there.
2067 */
2068void template_class::add_specialization(const Kind &kind)
2069{
2070 for (const auto &special : class_tuples)
2071 if (equivalent(kind1: special, kind2: kind))
2072 return;
2073 class_tuples.emplace_back(args: kind);
2074}
2075
2076/* A type printer that prints the plain interface type,
2077 * without namespace.
2078 */
2079struct plain_cpp_type_printer : public cpp_type_printer {
2080 plain_cpp_type_printer() {}
2081
2082 virtual std::string qualified(int arg, const std::string &cpp_type)
2083 const override;
2084};
2085
2086/* Return the qualified form of the given C++ isl type name appearing
2087 * in argument position "arg" (-1 for return type).
2088 *
2089 * For printing the plain type without namespace, no modifications
2090 * are required.
2091 */
2092std::string plain_cpp_type_printer::qualified(int arg,
2093 const std::string &cpp_type) const
2094{
2095 return cpp_type;
2096}
2097
2098/* Return a string representation of the plain type "type".
2099 *
2100 * For the plain printer, the argument position is irrelevant,
2101 * so simply pass in -1.
2102 */
2103static std::string plain_type(QualType type)
2104{
2105 return plain_cpp_type_printer().param(-1, type);
2106}
2107
2108/* Return a string representation of the plain return type of "method".
2109 */
2110static std::string plain_return_type(const Method &method)
2111{
2112 return plain_type(method.fd->getReturnType());
2113}
2114
2115/* Return that part of the signature "sig" that should match
2116 * the template class specialization for the given method.
2117 *
2118 * In particular, if the method is a regular member method,
2119 * then the instance should match the first argument.
2120 * Otherwise, it should match the return kind.
2121 */
2122static const Kind &matching_kind(const Method &method, const Signature &sig)
2123{
2124 if (method.kind == Method::Kind::member_method)
2125 return sig.args[0];
2126 else
2127 return sig.ret;
2128}
2129
2130/* Is it possible for "template_class" to have the given kind?
2131 *
2132 * If the template class represents an anonymous function,
2133 * then so must the given kind.
2134 * There should also be specialization with the same number of tuple kinds.
2135 */
2136static bool has_kind(const template_class &template_class, const Kind &kind)
2137{
2138 if (template_class.is_anon() && !kind.is_anon())
2139 return false;
2140 for (const auto &class_tuple : template_class.class_tuples)
2141 if (class_tuple.size() == kind.size())
2142 return true;
2143 return false;
2144}
2145
2146/* Is "return_kind" a possible kind for the return type of "method"?
2147 *
2148 * If the return type is not a template class,
2149 * then "return_kind" should not have any template parameters.
2150 * Otherwise, "return_kind" should be a valid kind for the template class.
2151 */
2152bool template_cpp_generator::class_printer::is_return_kind(
2153 const Method &method, const Kind &return_kind)
2154{
2155 const auto &template_classes = generator.template_classes;
2156 auto return_type = plain_return_type(method);
2157
2158 if (template_classes.count(x: return_type) == 0)
2159 return return_kind.params().size() == 0;
2160 return has_kind(template_class: template_classes.at(k: return_type), kind: return_kind);
2161}
2162
2163/* Is "kind" a placeholder that can be assigned something else
2164 * in a substitution?
2165 *
2166 * Anonymous can only be mapped to itself. This is taken care of
2167 * by assign().
2168 * Leaf can only be assigned a placeholder, but there is no need
2169 * to handle this specifically since Leaf can still be assigned
2170 * to the placeholder.
2171 */
2172static bool assignable(const TupleKindPtr &kind)
2173{
2174 return kind != Anonymous && kind != Leaf;
2175}
2176
2177/* Return a substitution that maps "kind1" to "kind2", if possible.
2178 * Otherwise return an empty substitution.
2179 *
2180 * Check if "kind1" can be assigned anything or
2181 * if "kind1" and "kind2" are identical.
2182 * The latter case handles mapping Anonymous to itself.
2183 */
2184static Substitution assign(const TupleKindPtr &kind1, const TupleKindPtr &kind2)
2185{
2186 Substitution res;
2187
2188 if (assignable(kind: kind1) || kind1 == kind2)
2189 res.emplace(args: kind1->name, args: kind2);
2190 return res;
2191}
2192
2193/* Return a substitution that first applies "first" and then "second".
2194 *
2195 * The result consists of "second" and of "second" applied to "first".
2196 */
2197static Substitution compose(const Substitution &first,
2198 const Substitution &second)
2199{
2200 Substitution res = second;
2201
2202 for (const auto &kvp : first)
2203 res.emplace(args: kvp.first, args: apply(tuple: kvp.second, subs: second));
2204
2205 return res;
2206}
2207
2208static Substitution compute_unifier(const TupleKindPtr &kind1,
2209 const TupleKindPtr &kind2);
2210
2211/* Try and extend "unifier" with a unifier for "kind1" and "kind2".
2212 * Return the resulting unifier if successful.
2213 * Otherwise, return an empty substitution.
2214 *
2215 * First apply "unifier" to "kind1" and "kind2".
2216 * Then compute a unifier for the resulting tuple kinds and
2217 * combine it with "unifier".
2218 */
2219static Substitution combine_unifiers(const TupleKindPtr &kind1,
2220 const TupleKindPtr &kind2, const Substitution &unifier)
2221{
2222 auto k1 = apply(tuple: kind1, subs: unifier);
2223 auto k2 = apply(tuple: kind2, subs: unifier);
2224 auto u = compute_unifier(kind1: k1, kind2: k2);
2225 if (u.size() == 0)
2226 return Substitution();
2227 return compose(first: unifier, second: u);
2228}
2229
2230/* Try and compute a unifier of "kind1" and "kind2",
2231 * i.e., a substitution that produces the same result when
2232 * applied to both "kind1" and "kind2",
2233 * for the case where both "kind1" and "kind2" are pairs.
2234 * Return this unifier if it was found.
2235 * Return an empty substitution if no unifier can be found.
2236 *
2237 * First compute a unifier for the left parts of the pairs and,
2238 * if successful, combine it with a unifier for the right parts.
2239 */
2240static Substitution compute_pair_unifier(const TupleKindPtr &kind1,
2241 const TupleKindPtr &kind2)
2242{
2243 auto unifier_left = compute_unifier(kind1: kind1->left(), kind2: kind2->left());
2244 if (unifier_left.size() == 0)
2245 return Substitution();
2246 return combine_unifiers(kind1: kind1->right(), kind2: kind2->right(), unifier: unifier_left);
2247}
2248
2249/* Try and compute a unifier of "kind1" and "kind2",
2250 * i.e., a substitution that produces the same result when
2251 * applied to both "kind1" and "kind2".
2252 * Return this unifier if it was found.
2253 * Return an empty substitution if no unifier can be found.
2254 *
2255 * If one of the tuple kinds is a pair then assign it
2256 * to the other tuple kind, if possible.
2257 * If neither is a pair, then try and assign one to the other.
2258 * Otherwise, let compute_pair_unifier compute a unifier.
2259 *
2260 * Note that an assignment is added to the unifier even
2261 * if "kind1" and "kind2" are identical.
2262 * This ensures that a successful substitution is never empty.
2263 */
2264static Substitution compute_unifier(const TupleKindPtr &kind1,
2265 const TupleKindPtr &kind2)
2266{
2267 if (kind1->left() && !kind2->left())
2268 return assign(kind1: kind2, kind2: kind1);
2269 if (!kind1->left() && kind2->left())
2270 return assign(kind1, kind2);
2271 if (!kind1->left() && !kind2->left()) {
2272 if (assignable(kind: kind1))
2273 return assign(kind1, kind2);
2274 else
2275 return assign(kind1: kind2, kind2: kind1);
2276 }
2277
2278 return compute_pair_unifier(kind1, kind2);
2279}
2280
2281/* Try and compute a unifier of "kind1" and "kind2",
2282 * i.e., a substitution that produces the same result when
2283 * applied to both "kind1" and "kind2".
2284 * Return this unifier if it was found.
2285 * Return an empty substitution if no unifier can be found.
2286 *
2287 * Start with an empty substitution and compute a unifier for
2288 * each pair of tuple kinds, combining the results.
2289 * If no combined unifier can be found or
2290 * if the numbers of tuple kinds are different, then return
2291 * an empty substitution.
2292 * This assumes that the number of tuples is greater than zero,
2293 * as otherwise an empty substitution would be returned as well.
2294 */
2295static Substitution compute_unifier(const Kind &kind1, const Kind &kind2)
2296{
2297 Substitution unifier;
2298
2299 if (kind1.size() != kind2.size())
2300 return Substitution();
2301
2302 for (size_t i = 0; i < kind1.size(); ++i)
2303 unifier = combine_unifiers(kind1: kind1[i], kind2: kind2[i], unifier);
2304
2305 return unifier;
2306}
2307
2308/* Try and construct a Kind that is a specialization of both "general" and
2309 * "specific", where "specific" is known _not_ to be a specialization
2310 * of "general" and not to contain any Leaf.
2311 *
2312 * First check whether "general" is a specialization of "specific".
2313 * If so, simply return "general".
2314 * Otherwise, rename the placeholders in the two kinds apart and
2315 * try and compute a unifier.
2316 * If this succeeds, then return the result of applying the unifier.
2317 */
2318static std::pair<bool, Kind> unify(const Kind &general, const Kind &specific)
2319{
2320 if (specializer(general: specific, specific: general).first) {
2321 return { true, general };
2322 } else {
2323 auto rename = param_renamer(params: specific.params(), prefix: "T");
2324 auto renamed = specific.apply(subs: rename);
2325 auto unifier = compute_unifier(kind1: general, kind2: renamed);
2326
2327 if (unifier.size() == 0)
2328 return { false, { } };
2329
2330 return { true, general.apply(subs: unifier) };
2331 }
2332}
2333
2334/* Try and add a template class specialization corresponding to "kind".
2335 * The new specialization needs to be a specialization of both
2336 * the current specialization and "kind".
2337 *
2338 * The current template class specialization is known not to be a special case
2339 * of "kind".
2340 *
2341 * Try and unify the two kinds and, if this succeeds, add the result
2342 * to this list of template class specializations.
2343 */
2344void template_cpp_generator::class_printer::add_specialization(
2345 const Kind &kind)
2346{
2347 auto maybe_unified = unify(general: kind, specific: instance.kind);
2348
2349 if (!maybe_unified.first)
2350 return;
2351 instance.template_class.add_specialization(kind: maybe_unified.second);
2352}
2353
2354/* Does the type of the parameter at position "i" of "method" necessarily
2355 * have a final Anonymous tuple?
2356 *
2357 * If the parameter is not of an isl type or if no specializations
2358 * have been defined for the type, then it can be considered anonymous.
2359 * Otherwise, if any specialization represents an anonymous function,
2360 * then every specialization does, so simply check
2361 * the first specialization.
2362 */
2363static bool param_is_anon(const Method &method, int i)
2364{
2365 ParmVarDecl *param = method.get_param(i);
2366 QualType type = param->getOriginalType();
2367
2368 if (cpp_generator::is_isl_type(type)) {
2369 const auto &name = type->getPointeeType().getAsString();
2370 const auto &cpp = cpp_generator::type2cpp(name);
2371 const auto &tuples = lookup_class_tuples(cpp);
2372
2373 if (tuples.empty())
2374 return true;
2375 return tuples[0].is_anon();
2376 }
2377
2378 return true;
2379}
2380
2381/* Replace the final tuple of "arg_kind" by Anonymous in "sig" and
2382 * return the update signature,
2383 * unless this would affect the class instance "instance_kind".
2384 *
2385 * If the original "instance_kind" is a special case
2386 * of the result of the substitution, then "instance_kind"
2387 * is not affected and the substitution can be applied
2388 * to the entire signature.
2389 */
2390static Signature specialize_anonymous_arg(const Signature &sig,
2391 const Kind &arg_kind, const Kind &instance_kind)
2392{
2393 const auto &subs = compute_unifier(kind1: arg_kind.back(), kind2: Anonymous);
2394 const auto &specialized_instance = instance_kind.apply(subs);
2395
2396 if (!specializer(general: specialized_instance, specific: instance_kind).first)
2397 return sig;
2398
2399 return sig.apply(subs);
2400}
2401
2402/* If any of the arguments of "method" is of a type that necessarily
2403 * has a final Anonymous tuple, but the corresponding entry
2404 * in the signature "sig" is not Anonymous, then replace
2405 * that entry by Anonymous and return the updated signature,
2406 * unless this would affect the class instance "instance_kind".
2407 */
2408static Signature specialize_anonymous_args(const Signature &sig,
2409 const Method &method, const Kind &instance_kind)
2410{
2411 auto specialized_sig = sig;
2412
2413 method.on_cpp_arg_list(on_arg: [&] (int i, int arg) {
2414 const auto &arg_kind = sig.args[arg];
2415
2416 if (arg_kind.is_anon())
2417 return;
2418 if (!param_is_anon(method, i))
2419 return;
2420 specialized_sig = specialize_anonymous_arg(sig: specialized_sig,
2421 arg_kind, instance_kind);
2422 });
2423
2424 return specialized_sig;
2425}
2426
2427/* Print a declaration or definition of the method "method"
2428 * if the template class specialization matches "match_arg".
2429 * Return true if so.
2430 * "sig" is the complete signature, of which "match_arg" refers
2431 * to the first argument or the return type.
2432 *
2433 * Since "sig" may have parameters with the same names as
2434 * those in instance.kind, rename them apart first.
2435 *
2436 * If the template class specialization is a special case of
2437 * (the renamed) "match_arg"
2438 * then apply the specializer to the complete (renamed) signature,
2439 * specialize any anonymous arguments,
2440 * check that the return kind is allowed and, if so,
2441 * print the declaration or definition using the specialized signature.
2442 *
2443 * If the template class specialization is not a special case of "match_arg"
2444 * then add a further specialization to the list of specializations
2445 * of the template class.
2446 */
2447bool template_cpp_generator::class_printer::print_matching_method(
2448 const Method &method, const Signature &sig, const Kind &match_arg)
2449{
2450 auto rename = shared_param_renamer(sig, kind: instance.kind);
2451 auto renamed_arg = match_arg.apply(subs: rename);
2452 auto maybe_specializer = specializer(general: renamed_arg, specific: instance.kind);
2453 if (maybe_specializer.first) {
2454 const auto &specializer = maybe_specializer.second;
2455 auto specialized_sig = sig.apply(subs: rename).apply(subs: specializer);
2456 specialized_sig = specialize_anonymous_args(sig: specialized_sig,
2457 method, instance_kind: instance.kind);
2458 if (!is_return_kind(method, return_kind: specialized_sig.ret))
2459 return false;
2460
2461 print_method_sig(method, sig: specialized_sig, deleted: false);
2462 } else {
2463 add_specialization(kind: match_arg);
2464 }
2465 return maybe_specializer.first;
2466}
2467
2468/* Is the first argument of "method" of type "isl_ctx *"?
2469 */
2470static bool first_arg_is_ctx(const Method &method)
2471{
2472 return generator::first_arg_is_isl_ctx(method.fd);
2473}
2474
2475/* Is the first signature argument set to { Ctx }?
2476 */
2477static bool first_kind_is_ctx(const Signature &sig)
2478{
2479 return sig.args[0].size() > 0 && sig.args[0][0] == Ctx;
2480}
2481
2482/* Print a declaration or definition of the member method "method"
2483 * if it matches the signature "sig".
2484 * Return true if so.
2485 *
2486 * First determine the part of the signature that needs to match
2487 * the template class specialization and
2488 * check that it has the same number of template arguments.
2489 * Also check that the number of arguments of the signature
2490 * matches that of the method.
2491 * If there is at least one argument, then check that the first method argument
2492 * is an isl_ctx if and only if the first signature argument is Ctx.
2493 *
2494 * If these tests succeed, proceed with the actual matching.
2495 */
2496bool template_cpp_generator::class_printer::print_matching_method(
2497 const Method &method, const Signature &sig)
2498{
2499 auto match_arg = matching_kind(method, sig);
2500 int n_args = sig.args.size();
2501
2502 if (match_arg.size() != instance.kind.size())
2503 return false;
2504 if (n_args != total_params(method))
2505 return false;
2506 if (n_args > 0 && first_arg_is_ctx(method) != first_kind_is_ctx(sig))
2507 return false;
2508
2509 return print_matching_method(method, sig, match_arg);
2510}
2511
2512/* Print a declaration or definition of the member method "method"
2513 * for each matching signature in "signatures".
2514 *
2515 * If there is no matching signature in "signatures",
2516 * then explicitly delete the method (using a signature based on
2517 * the specialization) so that it is not inherited from the base class.
2518 */
2519void template_cpp_generator::class_printer::print_matching_method(
2520 const Method &method, const std::vector<Signature> &signatures)
2521{
2522 auto any = false;
2523
2524 for (const auto &sig : signatures)
2525 if (print_matching_method(method, sig))
2526 any = true;
2527
2528 if (!any)
2529 print_method_sig(method, sig: instance_sig(method, instance), deleted: true);
2530}
2531
2532/* Signatures for "at" methods applied to a multi-expression,
2533 * which make the final tuple anonymous.
2534 */
2535static Signature select_set = { .ret: { Anonymous }, .args: { { Domain }, { Integer } } };
2536static Signature select_map =
2537 { .ret: { Domain, Anonymous }, .args: { { Domain, Range }, { Integer } } };
2538static std::vector<Signature> at_select = { select_set, select_map };
2539
2540/* Signatures for other "at" methods applied to a list,
2541 * which do not modify the tuple kind.
2542 */
2543static Signature bin_set_int = { .ret: { Domain }, .args: { { Domain }, { Integer } } };
2544static Signature bin_map_int =
2545 { .ret: { Domain, Range }, .args: { { Domain, Range }, { Integer } } };
2546static std::vector<Signature> at_keep = { bin_set_int, bin_map_int };
2547
2548/* Print a declaration or definition of the "at" member method "method".
2549 *
2550 * There are two types of methods called "at".
2551 * One type extracts an element from a multi-expression and
2552 * the other extracts an element from a list.
2553 *
2554 * In the first case, the return type is an anonymous function
2555 * while the object type is not. In this case, the return kind
2556 * should have a final Anonymous tuple.
2557 * Otherwise, the return kind should be the same as the object kind.
2558 */
2559void template_cpp_generator::class_printer::print_at_method(
2560 const Method &method)
2561{
2562 auto anon = instance.template_class.is_anon();
2563 auto return_type = plain_return_type(method);
2564 auto return_class = generator.template_classes.at(k: return_type);
2565
2566 if (!anon && return_class.is_anon())
2567 return print_matching_method(method, signatures: at_select);
2568 else
2569 return print_matching_method(method, signatures: at_keep);
2570}
2571
2572/* Does the string "s" contain "sub" as a substring?
2573 */
2574static bool contains(const std::string &s, const std::string &sub)
2575{
2576 return s.find(str: sub) != std::string::npos;
2577}
2578
2579/* Print a declaration or definition of the member method "method",
2580 * if it has a special signature in "special_methods".
2581 * Return true if this is the case.
2582 *
2583 * Check if any special signatures are specified for this method and
2584 * if the class name matches any of those with special signatures.
2585 * If so, pick the one with the best match, i.e., the first match
2586 * since the largest keys appear first.
2587 */
2588bool template_cpp_generator::class_printer::print_special_method(
2589 const Method &method, const infix_map_map &special_methods)
2590{
2591 if (special_methods.count(x: method.name) == 0)
2592 return false;
2593
2594 for (const auto &kvp : special_methods.at(k: method.name)) {
2595 if (!contains(s: instance.template_class.class_name, sub: kvp.first))
2596 continue;
2597 print_matching_method(method, signatures: kvp.second);
2598 return true;
2599 }
2600
2601 return false;
2602}
2603
2604/* Print a declaration or definition of the member method "method",
2605 * if it has a special signature specified by special_member_methods.
2606 * Return true if this is the case.
2607 */
2608bool template_cpp_generator::class_printer::print_special_member_method(
2609 const Method &method)
2610{
2611 return print_special_method(method, special_methods: special_member_methods);
2612}
2613
2614/* Print a declaration or definition of the member method "method",
2615 * if it is named after a template class. Return true if this is the case.
2616 */
2617bool template_cpp_generator::class_printer::print_type_named_member_method(
2618 const Method &method)
2619{
2620 if (generator.template_classes.count(x: method.name) == 0)
2621 return false;
2622
2623 print_matching_method(method, signatures: constructor_sig);
2624
2625 return true;
2626}
2627
2628/* Print a declaration or definition of the member method "method"
2629 * using a signature associated to method name "name", if there is any.
2630 * Return true if this is the case.
2631 */
2632bool template_cpp_generator::class_printer::print_member_method_with_name(
2633 const Method &method, const std::string &name)
2634{
2635 if (member_methods.count(x: name) == 0)
2636 return false;
2637
2638 print_matching_method(method, signatures: member_methods.at(k: name));
2639 return true;
2640}
2641
2642/* If "sub" appears inside "str", then remove the first occurrence and
2643 * return the result. Otherwise, simply return "str".
2644 */
2645static std::string drop_occurrence(const std::string &str,
2646 const std::string &sub)
2647{
2648 auto res = str;
2649 auto pos = str.find(str: sub);
2650
2651 if (pos != std::string::npos)
2652 res.erase(pos: pos, n: sub.length());
2653
2654 return res;
2655}
2656
2657/* If "sub" appears in "str" next to an underscore, then remove the combination.
2658 * Otherwise, simply return "str".
2659 */
2660static std::string drop_underscore_occurrence(const std::string &str,
2661 const std::string &sub)
2662{
2663 auto res = drop_occurrence(str, sub: sub + "_");
2664 if (res != str)
2665 return res;
2666 return drop_occurrence(str: res, sub: std::string("_") + sub);
2667}
2668
2669/* Return the name of "method", with the name of the return type,
2670 * along with an underscore, removed, if this combination appears in the name.
2671 * Otherwise, simply return the name.
2672 */
2673const std::string name_without_return(const Method &method)
2674{
2675 auto return_infix = plain_return_type(method);
2676 return drop_underscore_occurrence(str: method.name, sub: return_infix);
2677}
2678
2679/* If this method has a callback, then remove the type
2680 * of the first argument of the first callback from the name of the method.
2681 * Otherwise, simply return the name of the method.
2682 */
2683const std::string callback_name(const Method &method)
2684{
2685 if (method.callbacks.size() == 0)
2686 return method.name;
2687
2688 auto type = method.callbacks.at(0)->getType();
2689 auto callback = cpp_generator::extract_prototype(type);
2690 auto arg_type = plain_type(callback->getArgType(0));
2691 return generator::drop_suffix(s: method.name, suffix: "_" + arg_type);
2692}
2693
2694/* Print a declaration or definition of the member method "method".
2695 *
2696 * If the method is called "at", then it requires special treatment.
2697 * Otherwise, check if the signature is overridden for this class or
2698 * if the method is named after some other type.
2699 * Otherwise look for an appropriate signature using different variations
2700 * of the method name. First try the method name itself,
2701 * then the method name with the return type removed and
2702 * finally the method name with the callback argument type removed.
2703 */
2704void template_cpp_generator::class_printer::print_member_method(
2705 const Method &method)
2706{
2707 if (method.name == "at")
2708 return print_at_method(method);
2709 if (print_special_member_method(method))
2710 return;
2711 if (print_type_named_member_method(method))
2712 return;
2713 if (print_member_method_with_name(method, name: method.name))
2714 return;
2715 if (print_member_method_with_name(method, name: name_without_return(method)))
2716 return;
2717 if (print_member_method_with_name(method, name: callback_name(method)))
2718 return;
2719}
2720
2721/* Print a declaration or definition of "method" based on its type.
2722 */
2723void template_cpp_generator::class_printer::print_any_method(
2724 const Method &method)
2725{
2726 switch (method.kind) {
2727 case Method::Kind::static_method:
2728 print_static_method(method);
2729 break;
2730 case Method::Kind::constructor:
2731 print_constructor(method);
2732 break;
2733 case Method::Kind::member_method:
2734 print_member_method(method);
2735 break;
2736 }
2737}
2738
2739/* Print a declaration or definition of "method".
2740 *
2741 * Mark the method as not requiring copies of the arguments.
2742 */
2743void template_cpp_generator::class_printer::print_method(const Method &method)
2744{
2745 print_any_method(NoCopyMethod(method));
2746}
2747
2748/* Print a declaration or definition of "method".
2749 *
2750 * Note that a ConversionMethod is already marked
2751 * as not requiring copies of the arguments.
2752 */
2753void template_cpp_generator::class_printer::print_method(
2754 const ConversionMethod &method)
2755{
2756 print_any_method(method);
2757}
2758
2759/* Helper class for printing the declarations for
2760 * template class specializations.
2761 */
2762struct template_cpp_generator::class_decl_printer :
2763 public specialization_printer
2764{
2765 class_decl_printer(std::ostream &os,
2766 template_cpp_generator &generator) :
2767 specialization_printer(os, generator) {}
2768
2769 void print_arg_subclass_constructor(const specialization &instance,
2770 const std::vector<std::string> &params) const;
2771 void print_super_constructor(const specialization &instance) const;
2772 virtual void print_class(const specialization &instance) const override;
2773};
2774
2775/* Print the declaration and definition of a constructor
2776 * for the template class specialization "instance" taking
2777 * an instance with more specialized template arguments,
2778 * where "params" holds the template parameters of "instance".
2779 * It is assumed that there is at least one template parameter as otherwise
2780 * there are no template arguments to be specialized and
2781 * no constructor needs to be printed.
2782 *
2783 * In particular, the constructor takes an object of the same instance where
2784 * for each template parameter, the corresponding template argument
2785 * of the input object is a subclass of the template argument
2786 * of the constructed object.
2787 *
2788 * Pick fresh names for all template parameters and
2789 * add a constructor with these fresh names as extra template parameters and
2790 * a constraint requiring that each of them is a subclass
2791 * of the corresponding class template parameter.
2792 * The plain C++ interface object of the constructed object is initialized with
2793 * the plain C++ interface object of the constructor argument.
2794 */
2795void template_cpp_generator::class_decl_printer::print_arg_subclass_constructor(
2796 const specialization &instance,
2797 const std::vector<std::string> &params) const
2798{
2799 const auto &class_name = instance.class_name();
2800 auto rename = param_renamer(params, prefix: "Arg");
2801 auto derived = instance.kind.apply(subs: rename);
2802
2803 os << " template ";
2804 os << "<";
2805 print_pure_template_args(os, args: derived.params());
2806 os << ",\n";
2807 os << " typename std::enable_if<\n";
2808 for (size_t i = 0; i < params.size(); ++i) {
2809 if (i != 0)
2810 os << " &&\n";
2811 os << " std::is_base_of<"
2812 << params[i] << ", "
2813 << rename.at(k: params[i])->params()[0] << ">{}";
2814 }
2815 os << ",\n";
2816 os << " bool>::type = true>";
2817 os << "\n";
2818 os << " " << class_name << "(const ";
2819 print_bare_template_type(os, type: class_name, kind: derived);
2820 os << " &obj) : " << instance.base_name() << "(obj) {}\n";
2821}
2822
2823/* Print the declaration and definition of a constructor
2824 * for the template class specialization "instance" taking
2825 * an instance of the base class.
2826 *
2827 * If the instance kind is that of an anonymous set
2828 * (i.e., it has a single tuple that is set to Anonymous),
2829 * then allow the constructor to be called externally.
2830 * This is mostly useful for being able to use isl::val and
2831 * isl::typed::val<Anonymous> interchangeably and similarly for isl::id.
2832 *
2833 * If the instance is of any other kind, then make this constructor private
2834 * to avoid objects of the plain interface being converted automatically.
2835 * Also make sure that it does not apply to any type derived
2836 * from the base class. In particular, this makes sure it does
2837 * not apply to any other specializations of this template class as
2838 * otherwise any conflict in specializations would simply point
2839 * to the private constructor.
2840 *
2841 * A factory method is added to be able to perform the conversion explicitly,
2842 * with an explicit specification of the template arguments.
2843 */
2844void template_cpp_generator::class_decl_printer::print_super_constructor(
2845 const specialization &instance) const
2846{
2847 bool hide = !instance.kind.is_anon_set();
2848 const auto &base_name = instance.base_name();
2849 const auto &arg_name = hide ? "base" : base_name;
2850
2851 if (hide) {
2852 os << " private:\n";
2853 os << " template <typename base,\n";
2854 os << " typename std::enable_if<\n";
2855 os << " std::is_same<base, " << base_name
2856 << ">{}, bool>::type = true>\n";
2857 }
2858 os << " " << instance.class_name()
2859 << "(const " << arg_name << " &obj) : "
2860 << base_name << "(obj) {}\n";
2861 if (hide)
2862 os << " public:\n";
2863 os << " static " << instance.class_name() << " from"
2864 << "(const " << base_name << " &obj) {\n";
2865 os << " return " << instance.class_name() << "(obj);\n";
2866 os << " }\n";
2867}
2868
2869/* Print a "declaration" for the given template class specialization.
2870 * In particular, print the class definition and the method declarations.
2871 *
2872 * The template parameters are the distinct variable names
2873 * in the instance kind.
2874 *
2875 * Each instance of the template class derives from the corresponding
2876 * plain C++ interface class.
2877 *
2878 * All (other) template classes are made friends of this template class
2879 * to allow them to call the private constructor taking an object
2880 * of the plain interface.
2881 *
2882 * Besides the constructors and methods that forward
2883 * to the corresponding methods in the plain C++ interface class,
2884 * some extra constructors are defined.
2885 * The default zero-argument constructor is useful for declaring
2886 * a variable that only gets assigned a value at a later stage.
2887 * The constructor taking an instance with more specialized
2888 * template arguments is useful for lifting the class hierarchy
2889 * of the template arguments to the template class.
2890 * The constructor taking an instance of the base class
2891 * is useful for (explicitly) constructing a template type
2892 * from a plain type.
2893 */
2894void template_cpp_generator::class_decl_printer::print_class(
2895 const specialization &instance) const
2896{
2897 const auto &class_name = instance.class_name();
2898 auto params = instance.kind.params();
2899
2900 os << "\n";
2901
2902 print_template(os, params);
2903
2904 os << "struct ";
2905 print_bare_template_type(os, type: class_name, kind: instance.kind);
2906 os << " : public " << instance.base_name() << " {\n";
2907
2908 generator.print_friends(os);
2909 os << "\n";
2910
2911 os << " " << class_name << "() = default;\n";
2912 if (params.size() != 0)
2913 print_arg_subclass_constructor(instance, params);
2914 print_super_constructor(instance);
2915 method_decl_printer(instance, *this).print_all_methods();
2916
2917 os << "};\n";
2918}
2919
2920/* Helper class for printing the definitions of template class specializations.
2921 */
2922struct template_cpp_generator::class_impl_printer :
2923 public specialization_printer
2924{
2925 class_impl_printer(std::ostream &os,
2926 template_cpp_generator &generator) :
2927 specialization_printer(os, generator) {}
2928
2929 virtual void print_class(const specialization &instance) const override;
2930};
2931
2932/* Print a definition for the given template class specialization.
2933 *
2934 * In particular, print definitions
2935 * for the constructors and methods that forward
2936 * to the corresponding methods in the plain C++ interface class.
2937 * The extra constructors declared in the class definition
2938 * are defined inline.
2939 */
2940void template_cpp_generator::class_impl_printer::print_class(
2941 const specialization &instance) const
2942{
2943 method_impl_printer(instance, *this).print_all_methods();
2944}
2945
2946/* Generate a templated cpp interface
2947 * based on the extracted types and functions.
2948 *
2949 * First print forward declarations for all template classes,
2950 * then the declarations of the classes, and at the end all
2951 * method implementations.
2952 */
2953void template_cpp_generator::generate()
2954{
2955 ostream &os = std::cout;
2956
2957 os << "\n";
2958
2959 print_forward_declarations(os);
2960 class_decl_printer(os, *this).print_classes();
2961 class_impl_printer(os, *this).print_classes();
2962}
2963

source code of polly/lib/External/isl/interface/template_cpp.cc