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