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> &params,
356 	const std::string &prefix)
357 {
358 	Substitution renamer;
359 	int n = 0;
360 
361 	for (const auto &name : params) {
362 		auto suffix = std::to_string(++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> &params)
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> &params)
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> &params) 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> &params) 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