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