1/* 2 * Copyright 2020 Cerebras Systems. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following 13 * disclaimer in the documentation and/or other materials provided 14 * with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY CEREBRAS SYSTEMS ''AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CEREBRAS SYSTEMS OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * The views and conclusions contained in the software and documentation 29 * are those of the authors and should not be interpreted as 30 * representing official policies, either expressed or implied, of 31 * Cerebras Systems. 32 */ 33 34#include <ctype.h> 35 36#include <algorithm> 37#include <iostream> 38#include <set> 39#include <sstream> 40#include <string> 41#include <unordered_map> 42#include <unordered_set> 43 44#include "template_cpp.h" 45#include "isl_config.h" 46 47/* The textual representation of this tuple kind. 48 * 49 * By default, the textual representation is just the name. 50 */ 51std::string TupleKind::to_string() const 52{ 53 return name; 54} 55 56/* Return the parameters of this tuple kind. 57 * 58 * By default, there are no parameters. 59 */ 60std::vector<std::string> TupleKind::params() const 61{ 62 return { }; 63} 64 65/* Apply the substitution "subs" to this tuple kind and return the result. 66 * "self" is a shared pointer to this. 67 * 68 * If the name of this tuple kind appears in the substitution, 69 * then return the corresponding tuple kind pointer. 70 * Otherwise, return "self". 71 */ 72TupleKindPtr TupleKind::apply(const Substitution &subs, 73 const TupleKindPtr &self) const 74{ 75 if (subs.count(name) != 0) 76 return subs.at(name); 77 return self; 78} 79 80/* Apply the substitution "subs" to "tuple" and return the result. 81 */ 82static TupleKindPtr apply(const TupleKindPtr tuple, const Substitution &subs) 83{ 84 return tuple->apply(subs, tuple); 85} 86 87/* Return the left child of this tuple kind. 88 * 89 * Since this is not a pair, there is no left child. 90 */ 91TupleKindPtr TupleKind::left() const 92{ 93 return TupleKindPtr(); 94} 95 96/* Return the right child of this tuple kind. 97 * 98 * Since this is not a pair, there is no right child. 99 */ 100TupleKindPtr TupleKind::right() const 101{ 102 return TupleKindPtr(); 103} 104 105/* Helper class used to construct a pointer to a tuple kind 106 * that refers to a non-template type. 107 */ 108struct Fixed { 109}; 110 111/* Construct a pointer to a tuple kind that refers to a non-template type. 112 * 113 * Use an empty string as name. Since this is a non-template type, 114 * the kind name will never appear in the generated code. 115 */ 116TupleKindPtr::TupleKindPtr(Fixed) : Base(std::make_shared<TupleKind>("")) 117{ 118} 119 120/* Tuple pointers for non-template types. 121 */ 122static TupleKindPtr Ctx{Fixed()}; 123static TupleKindPtr Integer{Fixed()}; 124static TupleKindPtr Str{Fixed()}; 125static TupleKindPtr Res{Fixed()}; 126 127/* Special tuple pointers. 128 * Anonymous appears in the generated code but cannot be unified 129 * with anything else since it is a predefined template argument. 130 * Leaf can only be unified with something that is not a pair and 131 * does not appear in the generated code. 132 */ 133static TupleKindPtr Anonymous("Anonymous"); 134static TupleKindPtr Leaf("Leaf"); 135 136/* Placeholder tuple pointers that refer to (part of) the domain or range. 137 */ 138static TupleKindPtr Domain("Domain"); 139static TupleKindPtr Domain2("Domain2"); 140static TupleKindPtr Domain3("Domain3"); 141static TupleKindPtr Range("Range"); 142static TupleKindPtr Range2("Range2"); 143static TupleKindPtr Range3("Range3"); 144 145/* A representation of a proper tuple kind that is used as a template 146 * parameter or a template argument. 147 */ 148struct ProperTupleKind : public TupleKind { 149 ProperTupleKind(const std::string &name) : TupleKind(name) {} 150 151 virtual std::vector<std::string> params() const override; 152}; 153 154/* Return the parameters of this tuple kind. 155 * 156 * Return the name of this tuple kind, unless it is the special Anonymous 157 * predefined template argument. 158 */ 159std::vector<std::string> ProperTupleKind::params() const 160{ 161 if (Anonymous.get() == this) 162 return { }; 163 return { name }; 164} 165 166/* Construct a pointer to a tuple kind that refers 167 * to a proper tuple kind with the given name. 168 */ 169TupleKindPtr::TupleKindPtr(const std::string &name) : 170 Base(std::make_shared<ProperTupleKind>(name)) 171{ 172} 173 174/* A tuple kind that represents an anonymous pair of nested tuple kinds. 175 */ 176struct Pair : public TupleKind { 177 Pair(const TupleKindPtr &tuple1, const TupleKindPtr &tuple2) : 178 TupleKind(""), tuple1(tuple1), tuple2(tuple2) {} 179 180 virtual std::string to_string() const override; 181 virtual std::vector<std::string> params() const override; 182 virtual TupleKindPtr apply(const Substitution &match, 183 const TupleKindPtr &self) const override; 184 virtual TupleKindPtr left() const override; 185 virtual TupleKindPtr right() const override; 186 187 const TupleKindPtr tuple1; 188 const TupleKindPtr tuple2; 189}; 190 191/* The textual representation of this tuple kind. 192 * 193 * The textual representation of a pair is of the form "pair<tuple1, tuple2>". 194 */ 195std::string Pair::to_string() const 196{ 197 return std::string("pair<") + tuple1->to_string() + ", " + 198 tuple2->to_string() + ">"; 199} 200 201/* Add the elements of "vec2" that do not already appear in "vec1" 202 * at the end of "vec1". 203 * 204 * The two vectors are assumed not to have any repeated elements. 205 * The updated vector will then also not have repeated elements. 206 */ 207static void combine(std::vector<std::string> &vec1, 208 const std::vector<std::string> &vec2) 209{ 210 for (const auto &s : vec2) 211 if (std::find(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 */ 219std::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 */ 235TupleKindPtr 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 */ 243TupleKindPtr Pair::left() const 244{ 245 return tuple1; 246} 247 248/* Return the right child of this tuple kind. 249 */ 250TupleKindPtr Pair::right() const 251{ 252 return tuple2; 253} 254 255/* Construct a pointer to a tuple kind that refers 256 * to the given pair of nested tuple kinds. 257 */ 258TupleKindPtr::TupleKindPtr(const TupleKindPtr &left, const TupleKindPtr &right) 259 : Base(std::make_shared<Pair>(left, right)) 260{ 261} 262 263/* Is this a kind of object representing an anonymous function? 264 */ 265bool Kind::is_anon() const 266{ 267 return size() != 0 && back() == Anonymous; 268} 269 270/* Is this a kind of object with a single tuple? 271 */ 272bool Kind::is_set() const 273{ 274 return size() == 1; 275} 276 277/* Is this a kind of object with a single, anonymous tuple? 278 */ 279bool Kind::is_anon_set() const 280{ 281 return is_set() && is_anon(); 282} 283 284/* Return the parameters of this kind. 285 * 286 * Collect the parameters of the tuple kinds in the sequence. 287 */ 288std::vector<std::string> Kind::params() const 289{ 290 std::vector<std::string> params; 291 292 for (const auto &tuple : *this) 293 combine(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 */ 302Kind 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 */ 315struct Signature { 316 Kind ret; 317 std::vector<Kind> args; 318 319 std::vector<std::string> params() const; 320 Signature apply(const Substitution &match) const; 321}; 322 323/* Return the parameters of this signature. 324 * 325 * Collect the parameters of the argument kinds and the return kind. 326 */ 327std::vector<std::string> Signature::params() const 328{ 329 std::vector<std::string> params; 330 331 for (const auto &arg : args) 332 combine(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 */ 342Signature Signature::apply(const Substitution &subs) const 343{ 344 std::vector<Kind> applied_args; 345 346 for (const auto &arg : args) 347 applied_args.emplace_back(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 */ 355static Substitution param_renamer(const std::vector<std::string> ¶ms, 356 const std::string &prefix) 357{ 358 Substitution renamer; 359 int n = 0; 360 361 for (const auto &name : params) { 362 auto suffix = std::to_string(++n); 363 auto arg_name = prefix + suffix; 364 auto arg = TupleKindPtr(arg_name); 365 366 if (name == Leaf->name) 367 generator::die("Leaf cannot be renamed"); 368 369 renamer.emplace(name, arg); 370 } 371 372 return renamer; 373} 374 375/* Does the vector "v" contain the element "el"? 376 */ 377static 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 */ 386static std::vector<std::string> intersect(const std::vector<std::string> &v1, 387 const std::vector<std::string> &v2) 388{ 389 std::vector<std::string> intersection; 390 391 for (const auto &el : v1) 392 if (contains(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 */ 401static 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 */ 409static Signature un_params = { { }, { { } } }; 410static Signature un_set = { { Domain }, { { Domain } } }; 411static Signature un_map = { { Domain, Range }, { { Domain, Range } } }; 412static std::vector<Signature> un_op = { un_params, un_set, un_map }; 413static std::vector<Signature> fn_un_op = { un_set, un_map }; 414 415/* Signatures for binary operations, with the second argument 416 * possibly referring to part of the first argument. 417 * Functions have at least one tuple. 418 */ 419static Signature bin_params = { { }, { { }, { } } }; 420static Signature bin_set = { { Domain }, { { Domain }, { Domain } } }; 421static Signature bin_map = 422 { { Domain, Range }, { { Domain, Range }, { Domain, Range } } }; 423static std::vector<Signature> bin_op = { bin_params, bin_set, bin_map }; 424static std::vector<Signature> fn_bin_op = { bin_set, bin_map }; 425static Signature bin_set_params = { { Domain }, { { Domain }, { } } }; 426static Signature bin_map_params = 427 { { Domain, Range }, { { Domain, Range }, { } } }; 428static Signature bin_map_domain = 429 { { Domain, Range }, { { Domain, Range }, { Domain } } }; 430static Signature bin_map_range = 431 { { Domain, Range }, { { Domain, Range }, { Range } } }; 432static Signature bin_map_domain_wrapped_domain = 433 { { { Domain, Domain2 }, Range }, 434 { { { Domain, Domain2 }, Range }, { Domain } } }; 435static 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 */ 442static Signature bin_params_anon = { { }, { { }, { Anonymous } } }; 443static Signature bin_set_anon = { { Domain }, { { Domain }, { Anonymous } } }; 444static Signature bin_map_anon = 445 { { Domain, Range }, { { Domain, Range }, { Anonymous } } }; 446static std::vector<Signature> bin_op_anon = 447 { bin_params_anon, bin_set_anon, bin_map_anon }; 448 449/* Signatures for ternary operations, where the last two arguments are integers. 450 */ 451static Signature ter_params_int_int = 452 { { }, { { }, { Integer }, { Integer } } }; 453static Signature ter_set_int_int = 454 { { Domain }, { { Domain }, { Integer }, { Integer } } }; 455static Signature ter_map_int_int = 456 { { Domain, Range }, { { Domain, Range }, { Integer }, { Integer } } }; 457static std::vector<Signature> ter_int_int = 458 { ter_params_int_int, ter_set_int_int, ter_map_int_int }; 459 460/* Signatures for ternary operations. 461 * Functions have at least one tuple. 462 */ 463static Signature ter_set = 464 { { Domain }, { { Domain }, { Domain }, { Domain } } }; 465static Signature ter_map = 466 { { Domain, Range }, 467 { { Domain, Range }, { Domain, Range }, { Domain, Range } } }; 468static std::vector<Signature> fn_ter_op = { ter_set, ter_map }; 469 470/* Signatures for naming a leaf tuple using an identifier (with an anonymous 471 * tuple). 472 */ 473static Signature update_set = { { Domain2 }, { { Leaf }, { Anonymous } } }; 474static Signature update_domain = 475 { { Domain2, Range }, { { Leaf, Range }, { Anonymous } } }; 476static 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 */ 482static std::vector<Signature> min_max = { un_set, bin_set, un_map, bin_map }; 483 484/* Signatures for adding an unnamed tuple to an object with zero or one tuple. 485 */ 486static Signature to_set = { { Domain }, { { }, { Integer } } }; 487static Signature add_range = { { Domain, Range }, { { Domain }, { Integer } } }; 488/* Signatures for adding a named tuple to an object with zero or one tuple. 489 */ 490static Signature to_set_named = 491 { { Domain }, { { }, { Anonymous }, { Integer } } }; 492static 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 */ 498static Signature set_forward = { { Range }, { { Domain }, { Domain, Range } } }; 499static Signature domain_forward = 500 { { Domain2, Range }, { { Domain, Range }, { Domain, Domain2 } } }; 501static 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 */ 507static Signature set_backward = 508 { { Domain2 }, { { Domain }, { Domain2, Domain } } }; 509static Signature domain_backward = 510 { { Domain2, Range }, { { Domain, Range }, { Domain2, Domain } } }; 511static Signature range_backward = 512 { { Domain, Range2 }, { { Domain, Range }, { Range2, Range } } }; 513static 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 */ 520static Signature bind_set = { { }, { { Domain }, { Domain } } }; 521static Signature bind_domain = { { Range }, { { Domain, Range }, { Domain } } }; 522static Signature bind_range = { { Domain }, { { Domain, Range }, { Range } } }; 523static 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 */ 532static Signature each_params = { { Res }, { { }, { Res }, { } } }; 533static Signature each_set = { { Res }, { { Domain }, { Res }, { Domain } } }; 534static Signature each_map = 535 { { Res }, { { Domain, Range }, { Res }, { Domain, Range } } }; 536static std::vector<Signature> each = { each_params, each_set, each_map }; 537 538/* Signatures for isl_*_list_foreach_scc. 539 * 540 * The first callback takes two elements with the same tuple kinds. 541 * The second callback takes a list with the same tuple kinds. 542 */ 543static Signature each_scc_params = 544 { { Res }, { { }, { Res }, { }, { }, { Res }, { } } }; 545static Signature each_scc_set = 546 { { Res }, { { Domain }, 547 { Res }, { Domain }, { Domain }, 548 { Res }, { Domain } } }; 549static Signature each_scc_map = 550 { { Res }, { { Domain, Range }, 551 { Res }, { Domain, Range }, { Domain, Range }, 552 { Res }, { Domain, Range } } }; 553static std::vector<Signature> each_scc = 554 { each_scc_params, each_scc_set, each_scc_map }; 555 556/* Signature for creating a map from a range, 557 * where the domain is given by an extra argument. 558 */ 559static Signature map_from_range_and_domain = 560 { { 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 */ 567static Signature set_from_params = { { Domain }, { { }, { Domain } } }; 568static Signature map_from_domain_and_range = 569 { { Domain, Range }, { { Domain }, { Range } } }; 570static 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 */ 576static Signature anonymous_set_from_params = { { Anonymous }, { { } } }; 577static Signature anonymous_map_from_domain = 578 { { Domain, Anonymous }, { { Domain } } }; 579static 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 */ 585static Signature anonymous_set_from_params_bin_anon = 586 { { Anonymous }, { { }, { Anonymous } } }; 587static Signature anonymous_map_from_domain_bin_anon = 588 { { Domain, Anonymous }, { { Domain }, { Anonymous } } }; 589static std::vector<Signature> anonymous_from_domain_bin_anon = { 590 anonymous_set_from_params_bin_anon, 591 anonymous_map_from_domain_bin_anon 592 }; 593 594/* Signature for creating a map from a domain, 595 * where the range tuple is equal to the domain tuple. 596 */ 597static Signature set_to_map = { { 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 */ 602static Signature domain = { { Domain }, { { Domain, Range } } }; 603static Signature range = { { Range }, { { Domain, Range } } }; 604static Signature transformation_domain = { { Domain }, { { Domain, Domain } } }; 605 606/* Signatures for obtaining the parameter domain of a set or map. 607 */ 608static Signature set_params = { { }, { { Domain } } }; 609static Signature map_params = { { }, { { Domain, Range } } }; 610 611/* Signatures for obtaining the domain of a function. 612 */ 613static std::vector<Signature> fn_domain = { domain, set_params }; 614 615/* Signatures for interchanging (wrapped) domain and range. 616 */ 617static Signature set_reverse = 618 { { { Range, Domain } }, { { { Domain, Range } } } }; 619static Signature map_reverse = { { Range, Domain }, { { Domain, Range } } }; 620static Signature map_domain_reverse = 621 { { { Domain2, Domain }, Range }, { { { Domain, Domain2 }, Range } } }; 622static Signature map_range_reverse = 623 { { Domain, { Range2, Range } }, { { Domain, { Range, Range2 } } } }; 624 625/* Signatures for constructing products. 626 */ 627static Signature set_product = 628 { { { Domain, Range } }, { { Domain }, { Range } } }; 629static Signature map_product = 630 { { { Domain, Domain2 }, { Range, Range2 } }, 631 { { Domain, Range }, { Domain2, Range2 } } }; 632static Signature domain_product = 633 { { { Domain, Domain2 }, Range }, 634 { { Domain, Range }, { Domain2, Range } } }; 635static Signature range_product = 636 { { Domain, { Range, Range2 } }, 637 { { Domain, Range }, { Domain, Range2 } } }; 638 639/* Signatures for obtaining factors from a product. 640 */ 641static Signature domain_factor_domain = 642 { { Domain, Range }, { { { Domain, Domain2 }, Range } } }; 643static Signature domain_factor_range = 644 { { Domain2, Range }, { { { Domain, Domain2 }, Range } } }; 645static Signature range_factor_domain = 646 { { Domain, Range }, { { Domain, { Range, Range2 } } } }; 647static Signature range_factor_range = 648 { { Domain, Range2 }, { { Domain, { Range, Range2 } } } }; 649 650/* Signatures for (un)currying. 651 */ 652static Signature curry = 653 { { Domain, { Range, Range2 } }, 654 { { { Domain, Range }, Range2 } } }; 655static Signature uncurry = 656 { { { Domain, Range }, Range2 }, 657 { { Domain, { Range, Range2 } } } }; 658 659/* Signatures for (un)wrapping. 660 */ 661static Signature wrap = { { { Domain, Range } }, { { Domain, Range } } }; 662static Signature unwrap = { { Domain, Range }, { { { Domain, Range } } } }; 663 664/* Signatures for constructing objects that map to the domain or range 665 * of a map. 666 */ 667static Signature domain_map = 668 { { { Domain, Range }, Domain }, { { Domain, Range } } }; 669static 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 */ 675static 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 */ 681static 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 */ 687static Signature anonymize_nested_domain = 688 { { Anonymous, Range2 }, { { { Domain, Range }, Range2 } } }; 689static Signature anonymize_nested_range = 690 { { Domain, Anonymous }, { { Domain, { Range, Range2 } } } }; 691static Signature replace_nested_domain = 692 { { Domain2, Range2 }, 693 { { { Domain, Range }, Range2 }, { Anonymous} } }; 694static Signature replace_nested_range = 695 { { Domain, Range3 }, { { Domain, { Range, Range2 } }, { Anonymous} } }; 696static std::vector<Signature> flatten_domain = 697 { anonymize_nested_domain, replace_nested_domain }; 698static std::vector<Signature> flatten_range = 699 { anonymize_nested_range, replace_nested_range }; 700 701/* Signatures for "set_at" methods. 702 */ 703static Signature set_at_set = 704 { { Domain }, { { Domain }, { Integer }, { Anonymous } } }; 705static Signature set_at_map = 706 { { Domain, Range }, 707 { { Domain, Range }, { Integer }, { Domain, Anonymous } } }; 708static 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 */ 713static Signature to_list_set = { { Anonymous }, { { Domain } } }; 714static Signature to_list_map = { { Domain, Anonymous }, { { Domain, Range } } }; 715 716/* Signatures for functions constructing an object from only an isl::ctx. 717 */ 718static Signature ctx_params = { { }, { { Ctx } } }; 719static Signature ctx_set = { { Domain }, { { Ctx } } }; 720static 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 */ 729struct larger_infix { 730 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 */ 739typedef 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 */ 744typedef 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 */ 755static 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 */ 778static std::vector<Signature> range_op = { un_set, range }; 779 780/* Signatures for binary operations where the second argument 781 * is a (multi-)value. 782 */ 783static 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 */ 788static const std::unordered_map<std::string, std::vector<Signature>> 789member_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 */ 928static Signature from_list_set = { { Domain }, { { Domain }, { Anonymous } } }; 929static Signature from_list_map = 930 { { Domain, Range }, { { Domain, Range }, { Domain, Anonymous } } }; 931static 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 */ 966static 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 */ 987static Kind params{}; 988static Kind set_type{ Domain }; 989static Kind set_anon{ Anonymous }; 990static Kind map_type{ Domain, Range }; 991static 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 */ 1008static 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 */ 1021static 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 */ 1031static 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 */ 1050static 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 */ 1067static 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 */ 1075static 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 */ 1093static 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 */ 1107void 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 */ 1128template_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 */ 1148void 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 */ 1161void 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 */ 1184void 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 */ 1196static 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 */ 1204static 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 */ 1212template <typename List> 1213static 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 */ 1225template <typename List> 1226static 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 */ 1235static void print_template(std::ostream &os, 1236 const std::vector<std::string> ¶ms) 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 */ 1246static void print_non_empty_template(std::ostream &os, 1247 const std::vector<std::string> ¶ms) 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 */ 1259static 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 */ 1268struct 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 */ 1279const std::string &specialization::base_name() const 1280{ 1281 return template_class.super_name; 1282} 1283 1284/* The name of the template class. 1285 */ 1286const 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 */ 1297struct specialization_printer { 1298 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 */ 1321void 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 */ 1339struct 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 */ 1386template_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 */ 1400struct template_cpp_type_printer : public cpp_type_printer { 1401 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 */ 1412std::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 */ 1430std::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 */ 1443struct template_cpp_kind_type_printer : public template_cpp_type_printer { 1444 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 */ 1457Kind 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 */ 1466struct template_cpp_arg_type_printer : public template_cpp_type_printer { 1467 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 */ 1480Kind 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 */ 1496struct template_method_type_printer : public template_cpp_arg_type_printer { 1497 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 */ 1512std::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 */ 1523void 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 */ 1533template <typename T> 1534static 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 */ 1549void 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 */ 1573void 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 */ 1591bool 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 */ 1600void 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 */ 1609struct template_cpp_generator::method_decl_printer : 1610 public template_cpp_generator::class_printer { 1611 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 */ 1623void 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 */ 1642static 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 */ 1659static 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 */ 1674void 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 */ 1685struct template_cpp_generator::method_impl_printer : 1686 public template_cpp_generator::class_printer { 1687 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 */ 1705void 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 */ 1726static 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 */ 1751static 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 */ 1788static 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 */ 1814void 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 */ 1848void 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 */ 1878void 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 */ 1902void 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 */ 1910void 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 */ 1918static Signature params_from_str = { { }, { { Ctx }, { Str } } }; 1919static Signature set_from_str = { { Domain }, { { Ctx }, { Str } } }; 1920static Signature map_from_str = { { Domain, Range }, { { Ctx }, { Str } } }; 1921static 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 */ 1926static Signature int_from_si = { { Anonymous }, { { Ctx }, { Integer } } }; 1927 1928/* Signatures for constructors of lists from the initial number 1929 * of elements. 1930 */ 1931static Signature alloc_params = { { }, { { Ctx }, { Integer } } }; 1932static Signature alloc_set = { { Domain }, { { Ctx }, { Integer } } }; 1933static 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 */ 1949static 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 */ 1960static const std::unordered_map<std::string, std::vector<Signature>> 1961special_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 */ 1969void 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 */ 1985bool 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 */ 1995bool 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 */ 2010static 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 */ 2035static 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 */ 2060static 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 */ 2081static 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 */ 2090void 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 */ 2101struct plain_cpp_type_printer : public cpp_type_printer { 2102 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 */ 2114std::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 */ 2125static 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 */ 2132static 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 */ 2144static 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 */ 2158static 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 */ 2174bool 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 */ 2194static 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 */ 2206static 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 */ 2219static 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 2230static 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 */ 2241static 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 */ 2262static 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 */ 2286static 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 */ 2317static 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 */ 2340static 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 */ 2366void 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 */ 2385static 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 */ 2412static 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 */ 2430static 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 */ 2469bool 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 */ 2492static 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 */ 2499static 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 */ 2518bool 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 */ 2541void 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 */ 2557static Signature select_set = { { Anonymous }, { { Domain }, { Integer } } }; 2558static Signature select_map = 2559 { { Domain, Anonymous }, { { Domain, Range }, { Integer } } }; 2560static 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 */ 2565static Signature bin_set_int = { { Domain }, { { Domain }, { Integer } } }; 2566static Signature bin_map_int = 2567 { { Domain, Range }, { { Domain, Range }, { Integer } } }; 2568static 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 */ 2581void 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 */ 2596static 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 */ 2610bool 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 */ 2630bool 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 */ 2639bool 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 */ 2654bool 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 */ 2667static 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 */ 2682static 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 */ 2695const 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 */ 2705const 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 */ 2726void 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 */ 2745void 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 */ 2765void 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 */ 2775void 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 */ 2784struct template_cpp_generator::class_decl_printer : 2785 public specialization_printer 2786{ 2787 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> ¶ms) 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 */ 2817void template_cpp_generator::class_decl_printer::print_arg_subclass_constructor( 2818 const specialization &instance, 2819 const std::vector<std::string> ¶ms) 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 */ 2866void 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 */ 2916void 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 */ 2944struct template_cpp_generator::class_impl_printer : 2945 public specialization_printer 2946{ 2947 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 */ 2962void 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 */ 2975void 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