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