github.com/kidsbmilk/gofronted_all@v0.0.0-20220701224323-6479d5976c5d/go/embed.cc (about) 1 // embed.cc -- Go frontend go:embed handling. 2 3 // Copyright 2021 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 #include "go-system.h" 8 9 #include "operator.h" 10 #include "go-diagnostics.h" 11 #include "lex.h" 12 #include "types.h" 13 #include "expressions.h" 14 #include "gogo.h" 15 16 #ifndef O_BINARY 17 #define O_BINARY 0 18 #endif 19 20 // Read a file into *DATA. Returns false on error. 21 22 static bool 23 read_file(const char* filename, Location loc, std::string* data) 24 { 25 int fd = open(filename, O_RDONLY | O_BINARY); 26 if (fd < 0) 27 { 28 go_error_at(loc, "%s: %m", filename); 29 return false; 30 } 31 32 struct stat st; 33 if (fstat(fd, &st) < 0) 34 { 35 go_error_at(loc, "%s: %m", filename); 36 return false; 37 } 38 off_t want = st.st_size; 39 40 // Most files read here are going to be incorporated into the object file 41 // and then the executable. Set a limit on the size we will accept. 42 if (want > 2000000000) 43 { 44 go_error_at(loc, "%s: file too large", filename); 45 return false; 46 } 47 48 data->resize(want); 49 off_t got = 0; 50 while (want > 0) 51 { 52 // C++11 requires that std::string use contiguous bytes, so this 53 // is safe. 54 ssize_t n = read(fd, &(*data)[got], want); 55 if (n < 0) 56 { 57 close(fd); 58 go_error_at(loc, "%s: %m", filename); 59 return false; 60 } 61 if (n == 0) 62 { 63 data->resize(got); 64 break; 65 } 66 got += n; 67 want -= n; 68 } 69 70 close(fd); 71 return true; 72 } 73 74 // A JSON value as read from an embedcfg file. For our purposes a 75 // JSON value is a string, or a list of strings, or a mapping from 76 // strings to values. We don't expect any numbers. We also don't 77 // expect an array of anything other than strings; that is, we don't 78 // accept an array of general JSON values. 79 80 class Json_value 81 { 82 public: 83 // The types of values. 84 enum Json_value_classification 85 { 86 JSON_VALUE_UNKNOWN, 87 JSON_VALUE_STRING, 88 JSON_VALUE_ARRAY, 89 JSON_VALUE_MAP 90 }; 91 92 Json_value() 93 : classification_(JSON_VALUE_UNKNOWN), string_(), array_(), map_() 94 { } 95 96 ~Json_value(); 97 98 Json_value_classification 99 classification() const 100 { return this->classification_; } 101 102 // Set to a string value. 103 void 104 set_string(const std::string& str) 105 { 106 go_assert(this->classification_ == JSON_VALUE_UNKNOWN); 107 this->classification_ = JSON_VALUE_STRING; 108 this->string_ = str; 109 } 110 111 // Start an array value. 112 void 113 start_array() 114 { 115 go_assert(this->classification_ == JSON_VALUE_UNKNOWN); 116 this->classification_ = JSON_VALUE_ARRAY; 117 } 118 119 // Add an array entry. 120 void 121 add_array_entry(const std::string& s) 122 { 123 go_assert(this->classification_ == JSON_VALUE_ARRAY); 124 this->array_.push_back(s); 125 } 126 127 // Start a map value. 128 void 129 start_map() 130 { 131 go_assert(this->classification_ == JSON_VALUE_UNKNOWN); 132 this->classification_ = JSON_VALUE_MAP; 133 } 134 135 // Add a map entry. 136 void 137 add_map_entry(const std::string& key, Json_value* val) 138 { 139 go_assert(this->classification_ == JSON_VALUE_MAP); 140 this->map_[key] = val; 141 } 142 143 // Return the strings from a string value. 144 const std::string& 145 to_string() const 146 { 147 go_assert(this->classification_ == JSON_VALUE_STRING); 148 return this->string_; 149 } 150 151 // Fetch a vector of strings, and drop them from the JSON value. 152 void 153 get_and_clear_array(std::vector<std::string>* v) 154 { 155 go_assert(this->classification_ == JSON_VALUE_ARRAY); 156 std::swap(*v, this->array_); 157 } 158 159 // Look up a map entry. Returns NULL if not found. 160 Json_value* 161 lookup_map_entry(const std::string& key); 162 163 // Iterate over a map. 164 typedef Unordered_map(std::string, Json_value*)::iterator map_iterator; 165 166 map_iterator 167 map_begin() 168 { 169 go_assert(this->classification_ == JSON_VALUE_MAP); 170 return this->map_.begin(); 171 } 172 173 map_iterator 174 map_end() 175 { return this->map_.end(); } 176 177 private: 178 // Classification. 179 Json_value_classification classification_; 180 // A string, for JSON_VALUE_STRING. 181 std::string string_; 182 // Array, for JSON_VALUE_ARRAY. 183 std::vector<std::string> array_; 184 // Mapping, for JSON_VALUE_MAP. 185 Unordered_map(std::string, Json_value*) map_; 186 }; 187 188 // Delete a JSON value. 189 190 Json_value::~Json_value() 191 { 192 if (this->classification_ == JSON_VALUE_MAP) 193 { 194 for (map_iterator p = this->map_begin(); 195 p != this->map_end(); 196 ++p) 197 delete p->second; 198 } 199 } 200 201 // Look up a map entry in a JSON value. 202 203 Json_value* 204 Json_value::lookup_map_entry(const std::string& key) 205 { 206 go_assert(this->classification_ == JSON_VALUE_MAP); 207 Unordered_map(std::string, Json_value*)::iterator p = this->map_.find(key); 208 if (p == this->map_.end()) 209 return NULL; 210 return p->second; 211 } 212 213 // Manage reading the embedcfg file. 214 215 class Embedcfg_reader 216 { 217 public: 218 Embedcfg_reader(const char* filename) 219 : filename_(filename), data_(), p_(NULL), pend_(NULL) 220 {} 221 222 // Read the contents of FILENAME. Return whether it succeeded. 223 bool 224 initialize_from_file(); 225 226 // Read a JSON object. 227 bool 228 read_object(Json_value*); 229 230 // Report an error if not at EOF. 231 void 232 check_eof(); 233 234 // Report an error for the embedcfg file. 235 void 236 error(const char* msg); 237 238 private: 239 bool 240 read_value(Json_value*); 241 242 bool 243 read_array(Json_value*); 244 245 bool 246 read_string(std::string*); 247 248 bool 249 skip_whitespace(bool eof_ok); 250 251 // File name. 252 const char* filename_; 253 // File contents. 254 std::string data_; 255 // Next character to process. 256 const char *p_; 257 // End of data. 258 const char *pend_; 259 }; 260 261 // Read the embedcfg file. 262 263 void 264 Gogo::read_embedcfg(const char *filename) 265 { 266 class Embedcfg_reader r(filename); 267 if (!r.initialize_from_file()) 268 return; 269 270 Json_value val; 271 if (!r.read_object(&val)) 272 return; 273 274 r.check_eof(); 275 276 if (val.classification() != Json_value::JSON_VALUE_MAP) 277 { 278 r.error("invalid embedcfg: not a JSON object"); 279 return; 280 } 281 282 Json_value* patterns = val.lookup_map_entry("Patterns"); 283 if (patterns == NULL) 284 { 285 r.error("invalid embedcfg: missing Patterns"); 286 return; 287 } 288 if (patterns->classification() != Json_value::JSON_VALUE_MAP) 289 { 290 r.error("invalid embedcfg: Patterns is not a JSON object"); 291 return; 292 } 293 294 Json_value* files = val.lookup_map_entry("Files"); 295 if (files == NULL) 296 { 297 r.error("invalid embedcfg: missing Files"); 298 return; 299 } 300 if (files->classification() != Json_value::JSON_VALUE_MAP) 301 { 302 r.error("invalid embedcfg: Files is not a JSON object"); 303 return; 304 } 305 306 for (Json_value::map_iterator p = patterns->map_begin(); 307 p != patterns->map_end(); 308 ++p) 309 { 310 if (p->second->classification() != Json_value::JSON_VALUE_ARRAY) 311 { 312 r.error("invalid embedcfg: Patterns entry is not an array"); 313 return; 314 } 315 std::vector<std::string> files; 316 p->second->get_and_clear_array(&files); 317 318 std::pair<std::string, std::vector<std::string> > val; 319 val.first = p->first; 320 std::pair<Embed_patterns::iterator, bool> ins = 321 this->embed_patterns_.insert(val); 322 if (!ins.second) 323 { 324 r.error("invalid embedcfg: duplicate Patterns entry"); 325 return; 326 } 327 std::swap(ins.first->second, files); 328 } 329 330 for (Json_value::map_iterator p = files->map_begin(); 331 p != files->map_end(); 332 ++p) 333 { 334 if (p->second->classification() != Json_value::JSON_VALUE_STRING) 335 { 336 r.error("invalid embedcfg: Files entry is not a string"); 337 return; 338 } 339 this->embed_files_[p->first] = p->second->to_string(); 340 } 341 } 342 343 // Read the contents of FILENAME into this->data_. Returns whether it 344 // succeeded. 345 346 bool 347 Embedcfg_reader::initialize_from_file() 348 { 349 if (!read_file(this->filename_, Linemap::unknown_location(), &this->data_)) 350 return false; 351 if (this->data_.empty()) 352 { 353 this->error("empty file"); 354 return false; 355 } 356 this->p_ = this->data_.data(); 357 this->pend_ = this->p_ + this->data_.size(); 358 return true; 359 } 360 361 // Read a JSON object into VAL. Return whether it succeeded. 362 363 bool 364 Embedcfg_reader::read_object(Json_value* val) 365 { 366 if (!this->skip_whitespace(false)) 367 return false; 368 if (*this->p_ != '{') 369 { 370 this->error("expected %<{%>"); 371 return false; 372 } 373 ++this->p_; 374 375 val->start_map(); 376 377 if (!this->skip_whitespace(false)) 378 return false; 379 if (*this->p_ == '}') 380 { 381 ++this->p_; 382 return true; 383 } 384 385 while (true) 386 { 387 if (!this->skip_whitespace(false)) 388 return false; 389 if (*this->p_ != '"') 390 { 391 this->error("expected %<\"%>"); 392 return false; 393 } 394 395 std::string key; 396 if (!this->read_string(&key)) 397 return false; 398 399 if (!this->skip_whitespace(false)) 400 return false; 401 if (*this->p_ != ':') 402 { 403 this->error("expected %<:%>"); 404 return false; 405 } 406 ++this->p_; 407 408 Json_value* subval = new Json_value(); 409 if (!this->read_value(subval)) 410 return false; 411 412 val->add_map_entry(key, subval); 413 414 if (!this->skip_whitespace(false)) 415 return false; 416 if (*this->p_ == '}') 417 { 418 ++this->p_; 419 return true; 420 } 421 if (*this->p_ != ',') 422 { 423 this->error("expected %<,%> or %<}%>"); 424 return false; 425 } 426 ++this->p_; 427 } 428 } 429 430 // Read a JSON array into VAL. Return whether it succeeded. 431 432 bool 433 Embedcfg_reader::read_array(Json_value* val) 434 { 435 if (!this->skip_whitespace(false)) 436 return false; 437 if (*this->p_ != '[') 438 { 439 this->error("expected %<[%>"); 440 return false; 441 } 442 ++this->p_; 443 444 val->start_array(); 445 446 if (!this->skip_whitespace(false)) 447 return false; 448 if (*this->p_ == ']') 449 { 450 ++this->p_; 451 return true; 452 } 453 454 while (true) 455 { 456 // If we were parsing full JSON we would call read_value here, 457 // not read_string. 458 459 std::string s; 460 if (!this->read_string(&s)) 461 return false; 462 463 val->add_array_entry(s); 464 465 if (!this->skip_whitespace(false)) 466 return false; 467 if (*this->p_ == ']') 468 { 469 ++this->p_; 470 return true; 471 } 472 if (*this->p_ != ',') 473 { 474 this->error("expected %<,%> or %<]%>"); 475 return false; 476 } 477 ++this->p_; 478 } 479 } 480 481 // Read a JSON value into VAL. Return whether it succeeded. 482 483 bool 484 Embedcfg_reader::read_value(Json_value* val) 485 { 486 if (!this->skip_whitespace(false)) 487 return false; 488 switch (*this->p_) 489 { 490 case '"': 491 { 492 std::string s; 493 if (!this->read_string(&s)) 494 return false; 495 val->set_string(s); 496 return true; 497 } 498 499 case '{': 500 return this->read_object(val); 501 502 case '[': 503 return this->read_array(val); 504 505 default: 506 this->error("invalid JSON syntax"); 507 return false; 508 } 509 } 510 511 // Read a JSON string. Return whether it succeeded. 512 513 bool 514 Embedcfg_reader::read_string(std::string* str) 515 { 516 if (!this->skip_whitespace(false)) 517 return false; 518 if (*this->p_ != '"') 519 { 520 this->error("expected %<\"%>"); 521 return false; 522 } 523 ++this->p_; 524 525 str->clear(); 526 while (this->p_ < this->pend_ && *this->p_ != '"') 527 { 528 if (*this->p_ != '\\') 529 { 530 str->push_back(*this->p_); 531 ++this->p_; 532 continue; 533 } 534 535 ++this->p_; 536 if (this->p_ >= this->pend_) 537 { 538 this->error("unterminated string"); 539 return false; 540 } 541 switch (*this->p_) 542 { 543 case '"': case '\\': case '/': 544 str->push_back(*this->p_); 545 ++this->p_; 546 break; 547 548 case 'b': 549 str->push_back('\b'); 550 ++this->p_; 551 break; 552 553 case 'f': 554 str->push_back('\f'); 555 ++this->p_; 556 break; 557 558 case 'n': 559 str->push_back('\n'); 560 ++this->p_; 561 break; 562 563 case 'r': 564 str->push_back('\r'); 565 ++this->p_; 566 break; 567 568 case 't': 569 str->push_back('\t'); 570 ++this->p_; 571 break; 572 573 case 'u': 574 { 575 ++this->p_; 576 unsigned int rune = 0; 577 for (int i = 0; i < 4; i++) 578 { 579 if (this->p_ >= this->pend_) 580 { 581 this->error("unterminated string"); 582 return false; 583 } 584 unsigned char c = *this->p_; 585 ++this->p_; 586 rune <<= 4; 587 if (c >= '0' && c <= '9') 588 rune += c - '0'; 589 else if (c >= 'A' && c <= 'F') 590 rune += c - 'A' + 10; 591 else if (c >= 'a' && c <= 'f') 592 rune += c - 'a' + 10; 593 else 594 { 595 this->error("invalid hex digit"); 596 return false; 597 } 598 } 599 Lex::append_char(rune, false, str, Linemap::unknown_location()); 600 } 601 break; 602 603 default: 604 this->error("unrecognized string escape"); 605 return false; 606 } 607 } 608 609 if (*this->p_ == '"') 610 { 611 ++this->p_; 612 return true; 613 } 614 615 this->error("unterminated string"); 616 return false; 617 } 618 619 // Report an error if not at EOF. 620 621 void 622 Embedcfg_reader::check_eof() 623 { 624 if (this->skip_whitespace(true)) 625 this->error("extraneous data at end of file"); 626 } 627 628 // Skip whitespace. Return whether there is more to read. 629 630 bool 631 Embedcfg_reader::skip_whitespace(bool eof_ok) 632 { 633 while (this->p_ < this->pend_) 634 { 635 switch (*this->p_) 636 { 637 case ' ': case '\t': case '\n': case '\r': 638 ++this->p_; 639 break; 640 default: 641 return true; 642 } 643 } 644 if (!eof_ok) 645 this->error("unexpected EOF"); 646 return false; 647 } 648 649 // Report an error. 650 651 void 652 Embedcfg_reader::error(const char* msg) 653 { 654 if (!this->data_.empty() && this->p_ != NULL) 655 go_error_at(Linemap::unknown_location(), 656 "%<-fgo-embedcfg%>: %s: %lu: %s", 657 this->filename_, 658 static_cast<unsigned long>(this->p_ - this->data_.data()), 659 msg); 660 else 661 go_error_at(Linemap::unknown_location(), 662 "%<-fgo-embedcfg%>: %s: %s", 663 this->filename_, msg); 664 } 665 666 // Implement the sort order for a list of embedded files, as discussed 667 // at the docs for embed.FS. 668 669 class Embedfs_sort 670 { 671 public: 672 bool 673 operator()(const std::string& p1, const std::string& p2) const; 674 675 private: 676 void 677 split(const std::string&, size_t*, size_t*, size_t*) const; 678 }; 679 680 bool 681 Embedfs_sort::operator()(const std::string& p1, const std::string& p2) const 682 { 683 size_t dirlen1, elem1, elemlen1; 684 this->split(p1, &dirlen1, &elem1, &elemlen1); 685 size_t dirlen2, elem2, elemlen2; 686 this->split(p2, &dirlen2, &elem2, &elemlen2); 687 688 if (dirlen1 == 0) 689 { 690 if (dirlen2 > 0) 691 { 692 int i = p2.compare(0, dirlen2, "."); 693 if (i != 0) 694 return i > 0; 695 } 696 } 697 else if (dirlen2 == 0) 698 { 699 int i = p1.compare(0, dirlen1, "."); 700 if (i != 0) 701 return i < 0; 702 } 703 else 704 { 705 int i = p1.compare(0, dirlen1, p2, 0, dirlen2); 706 if (i != 0) 707 return i < 0; 708 } 709 710 int i = p1.compare(elem1, elemlen1, p2, elem2, elemlen2); 711 return i < 0; 712 } 713 714 // Pick out the directory and file name components for comparison. 715 716 void 717 Embedfs_sort::split(const std::string& s, size_t* dirlen, size_t* elem, 718 size_t* elemlen) const 719 { 720 size_t len = s.size(); 721 if (len > 0 && s[len - 1] == '/') 722 --len; 723 size_t slash = s.rfind('/', len - 1); 724 if (slash == std::string::npos) 725 { 726 *dirlen = 0; 727 *elem = 0; 728 *elemlen = len; 729 } 730 else 731 { 732 *dirlen = slash; 733 *elem = slash + 1; 734 *elemlen = len - (slash + 1); 735 } 736 } 737 738 // Convert the go:embed directives for a variable into an initializer 739 // for that variable. 740 741 Expression* 742 Gogo::initializer_for_embeds(Type* type, 743 const std::vector<std::string>* embeds, 744 Location loc) 745 { 746 if (this->embed_patterns_.empty()) 747 { 748 go_error_at(loc, 749 ("invalid go:embed: build system did not " 750 "supply embed configuration")); 751 return Expression::make_error(loc); 752 } 753 754 type = type->unalias(); 755 756 enum { 757 EMBED_STRING = 0, 758 EMBED_BYTES = 1, 759 EMBED_FS = 2 760 } embed_kind; 761 762 const Named_type* nt = type->named_type(); 763 if (nt != NULL 764 && nt->named_object()->package() != NULL 765 && nt->named_object()->package()->pkgpath() == "embed" 766 && nt->name() == "FS") 767 embed_kind = EMBED_FS; 768 else if (type->is_string_type()) 769 embed_kind = EMBED_STRING; 770 else if (type->is_slice_type() 771 && type->array_type()->element_type()->integer_type() != NULL 772 && type->array_type()->element_type()->integer_type()->is_byte()) 773 embed_kind = EMBED_BYTES; 774 else 775 { 776 go_error_at(loc, "invalid type for go:embed"); 777 return Expression::make_error(loc); 778 } 779 780 // The patterns in the go:embed directive(s) are in EMBEDS. Find 781 // them in the patterns in the embedcfg file. 782 783 Unordered_set(std::string) have; 784 std::vector<std::string> paths; 785 for (std::vector<std::string>::const_iterator pe = embeds->begin(); 786 pe != embeds->end(); 787 pe++) 788 { 789 Embed_patterns::const_iterator pp = this->embed_patterns_.find(*pe); 790 if (pp == this->embed_patterns_.end()) 791 { 792 go_error_at(loc, 793 ("invalid go:embed: build system did not " 794 "map pattern %<%s%>"), 795 pe->c_str()); 796 continue; 797 } 798 799 // Each pattern in the embedcfg file maps to a list of file 800 // names. Add those file names to PATHS. 801 for (std::vector<std::string>::const_iterator pf = pp->second.begin(); 802 pf != pp->second.end(); 803 pf++) 804 { 805 if (this->embed_files_.find(*pf) == this->embed_files_.end()) 806 { 807 go_error_at(loc, 808 ("invalid go:embed: build system did not " 809 "map file %<%s%>"), 810 pf->c_str()); 811 continue; 812 } 813 814 std::pair<Unordered_set(std::string)::iterator, bool> ins 815 = have.insert(*pf); 816 if (ins.second) 817 { 818 const std::string& path(*pf); 819 paths.push_back(path); 820 821 if (embed_kind == EMBED_FS) 822 { 823 // Add each required directory, with a trailing slash. 824 size_t i = std::string::npos; 825 while (i > 0) 826 { 827 i = path.rfind('/', i); 828 if (i == std::string::npos) 829 break; 830 std::string dir = path.substr(0, i + 1); 831 ins = have.insert(dir); 832 if (ins.second) 833 paths.push_back(dir); 834 --i; 835 } 836 } 837 } 838 } 839 } 840 841 if (embed_kind == EMBED_STRING || embed_kind == EMBED_BYTES) 842 { 843 if (paths.size() > 1) 844 { 845 go_error_at(loc, 846 ("invalid go:embed: multiple files for " 847 "string or byte slice"));; 848 return Expression::make_error(loc); 849 } 850 851 std::string data; 852 if (!read_file(this->embed_files_[paths[0]].c_str(), loc, &data)) 853 return Expression::make_error(loc); 854 855 Expression* e = Expression::make_string(data, loc); 856 if (embed_kind == EMBED_BYTES) 857 e = Expression::make_cast(type, e, loc); 858 return e; 859 } 860 861 std::sort(paths.begin(), paths.end(), Embedfs_sort()); 862 863 if (type->struct_type() == NULL 864 || type->struct_type()->field_count() != 1) 865 { 866 go_error_at(loc, 867 ("internal error: embed.FS should be struct type " 868 "with one field")); 869 return Expression::make_error(loc); 870 } 871 872 Type* ptr_type = type->struct_type()->field(0)->type(); 873 if (ptr_type->points_to() == NULL) 874 { 875 go_error_at(loc, 876 "internal error: embed.FS struct field should be pointer"); 877 return Expression::make_error(loc); 878 } 879 880 Type* slice_type = ptr_type->points_to(); 881 if (!slice_type->is_slice_type()) 882 { 883 go_error_at(loc, 884 ("internal error: embed.FS struct field should be " 885 "pointer to slice")); 886 return Expression::make_error(loc); 887 } 888 889 Type* file_type = slice_type->array_type()->element_type(); 890 if (file_type->struct_type() == NULL 891 || (file_type->struct_type()->find_local_field(".embed.name", NULL) 892 == NULL) 893 || (file_type->struct_type()->find_local_field(".embed.data", NULL) 894 == NULL)) 895 { 896 go_error_at(loc, 897 ("internal error: embed.FS slice element should be struct " 898 "with name and data fields")); 899 return Expression::make_error(loc); 900 } 901 902 const Struct_field_list* file_fields = file_type->struct_type()->fields(); 903 Expression_list* file_vals = new(Expression_list); 904 file_vals->reserve(paths.size()); 905 for (std::vector<std::string>::const_iterator pp = paths.begin(); 906 pp != paths.end(); 907 ++pp) 908 { 909 std::string data; 910 if ((*pp)[pp->size() - 1] != '/') 911 { 912 if (!read_file(this->embed_files_[*pp].c_str(), loc, &data)) 913 return Expression::make_error(loc); 914 } 915 916 Expression_list* field_vals = new(Expression_list); 917 for (Struct_field_list::const_iterator pf = file_fields->begin(); 918 pf != file_fields->end(); 919 ++pf) 920 { 921 if (pf->is_field_name(".embed.name")) 922 field_vals->push_back(Expression::make_string(*pp, loc)); 923 else if (pf->is_field_name(".embed.data")) 924 field_vals->push_back(Expression::make_string(data, loc)); 925 else 926 { 927 // FIXME: The embed.file type has a hash field, which is 928 // currently unused. We should fill it in, but don't. 929 // The hash is a SHA256, and we don't have convenient 930 // SHA256 code. Do this later when the field is 931 // actually used. 932 field_vals->push_back(NULL); 933 } 934 } 935 936 Expression* file_val = 937 Expression::make_struct_composite_literal(file_type, field_vals, loc); 938 file_vals->push_back(file_val); 939 } 940 941 Expression* slice_init = 942 Expression::make_slice_composite_literal(slice_type, file_vals, loc); 943 Expression* fs_init = Expression::make_heap_expression(slice_init, loc); 944 Expression_list* fs_vals = new Expression_list(); 945 fs_vals->push_back(fs_init); 946 return Expression::make_struct_composite_literal(type, fs_vals, loc); 947 }