Codemod asserts to assertxs in the runtime
[hiphop-php.git] / hphp / runtime / base / type-structure.cpp
blob22e6b25363aa207c19e79f2bea539c2dd21cf53d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/type-structure.h"
19 #include "hphp/runtime/base/array-data.h"
20 #include "hphp/runtime/base/array-data-defs.h"
21 #include "hphp/runtime/base/array-init.h"
22 #include "hphp/runtime/base/exceptions.h"
23 #include "hphp/runtime/base/string-data.h"
24 #include "hphp/runtime/base/typed-value.h"
25 #include "hphp/runtime/base/type-array.h"
26 #include "hphp/runtime/base/type-variant.h"
27 #include "hphp/runtime/vm/named-entity.h"
28 #include "hphp/runtime/vm/unit.h"
29 #include "hphp/util/text-util.h"
31 namespace HPHP {
33 struct String;
34 struct StaticString;
36 namespace {
39 * These static strings are the same as the ones in
40 * hphp/compiler/type_annotation.cpp, where the typeAnnotArrays are
41 * originally generated. See TypeAnnotation::getScalarArrayRep().
43 const StaticString
44 s_nullable("nullable"),
45 s_name("name"),
46 s_classname("classname"),
47 s_kind("kind"),
48 s_elem_types("elem_types"),
49 s_return_type("return_type"),
50 s_param_types("param_types"),
51 s_generic_types("generic_types"),
52 s_root_name("root_name"),
53 s_access_list("access_list"),
54 s_fields("fields"),
55 s_allows_unknown_fields("allows_unknown_fields"),
56 s_is_cls_cns("is_cls_cns"),
57 s_optional_shape_field("optional_shape_field"),
58 s_value("value"),
59 s_this("HH\\this"),
60 s_self("self"),
61 s_parent("parent"),
62 s_callable("callable"),
63 s_alias("alias"),
64 s_typevars("typevars"),
65 s_typevar_types("typevar_types")
68 const std::string
69 s_void("HH\\void"),
70 s_int("HH\\int"),
71 s_bool("HH\\bool"),
72 s_float("HH\\float"),
73 s_string("HH\\string"),
74 s_resource("HH\\resource"),
75 s_num("HH\\num"),
76 s_arraykey("HH\\arraykey"),
77 s_noreturn("HH\\noreturn"),
78 s_mixed("HH\\mixed"),
79 s_array("array"),
80 s_shape("HH\\shape"),
81 s_dict("HH\\dict"),
82 s_vec("HH\\vec"),
83 s_keyset("HH\\keyset"),
84 s_vec_or_dict("HH\\vec_or_dict")
87 std::string fullName(const Array& arr);
89 void functionTypeName(const Array& arr, std::string& name) {
90 name += "(function (";
92 assertx(arr.exists(s_return_type));
93 auto const retType = arr[s_return_type].toCArrRef();
95 assertx(arr.exists(s_param_types));
96 auto const params = arr[s_param_types].toCArrRef();
98 auto sep = "";
99 auto const sz = params.size();
100 for (auto i = 0; i < sz; i++) {
101 auto const param = params[i].toCArrRef();
102 folly::toAppend(sep, fullName(param), &name);
103 sep = ", ";
106 // add funciton return type
107 folly::toAppend("): ", fullName(retType), ")", &name);
110 void accessTypeName(const Array& arr, std::string& name) {
111 assertx(arr.exists(s_root_name));
112 auto const rootName = arr[s_root_name].toCStrRef();
113 name += rootName.toCppString();
115 assertx(arr.exists(s_access_list));
116 auto const accessList = arr[s_access_list].toCArrRef();
117 auto const sz = accessList.size();
118 for (auto i = 0; i < sz; i++) {
119 folly::toAppend("::",
120 accessList[i].toCStrRef().toCppString(),
121 &name);
125 // xhp names are mangled so we get them back to their original definition
126 // see the mangling in ScannerToken::xhpLabel
127 void xhpTypeName(const Array& arr, std::string& name) {
128 assertx(arr.exists(s_classname));
129 std::string clsName = arr[s_classname].toCStrRef().toCppString();
130 // remove prefix if any
131 if (clsName.compare(0, sizeof("xhp_") - 1, "xhp_") == 0) {
132 name += clsName.replace(0, sizeof("xhp_") - 1, ":");
133 } else {
134 name += clsName;
136 // un-mangle back
137 replaceAll(name, "__", ":");
138 replaceAll(name, "_", "-");
141 void tupleTypeName(const Array& arr, std::string& name) {
142 name += "(";
143 assertx(arr.exists(s_elem_types));
144 auto const elems = arr[s_elem_types].toCArrRef();
145 auto const sz = elems.size();
146 auto sep = "";
147 for (auto i = 0; i < sz; i++) {
148 auto const elem = elems[i].toCArrRef();
149 folly::toAppend(sep, fullName(elem), &name);
150 sep = ", ";
153 name += ")";
156 void genericTypeName(const Array& arr, std::string& name) {
157 name += "<";
158 assertx(arr.exists(s_generic_types));
159 auto const args = arr[s_generic_types].toCArrRef();
160 auto const sz = args.size();
161 auto sep = "";
162 for (auto i = 0; i < sz; i++) {
163 auto const arg = args[i].toCArrRef();
164 folly::toAppend(sep, fullName(arg), &name);
165 sep = ", ";
167 name += ">";
170 void shapeTypeName(const Array& arr, std::string& name) {
171 // works for both resolved and unresolved TypeStructures
172 name += "(";
173 assertx(arr.exists(s_fields));
174 auto const fields = arr[s_fields].toCArrRef();
175 auto const sz = fields.size();
176 auto sep = "";
177 for (auto i = 0; i < sz; i++) {
178 name += sep;
179 auto const field = fields->getKey(i);
180 auto value = fields->getValue(i).toCArrRef();
181 auto quote = "'";
182 if (value.exists(s_optional_shape_field)) {
183 name += "?";
185 if (value.exists(s_value)) {
186 // if unresolved, ignore wrapper
187 if (value.exists(s_is_cls_cns)) quote = "";
188 value = value[s_value].toCArrRef();
190 auto const fieldType = field.getType();
191 if (isStringType(fieldType)) {
192 folly::toAppend(quote, field.toCStrRef().data(), quote, &name);
193 } else if (isIntType(fieldType)) {
194 folly::toAppend(field.toInt64Val(), &name);
197 folly::toAppend("=>", fullName(value), &name);
198 sep = ", ";
201 if (arr.exists(s_allows_unknown_fields)) {
202 folly::toAppend(sep, "...", &name);
205 name += ")";
208 std::string fullName(const Array& arr) {
209 std::string name;
210 if (arr.exists(s_nullable)) {
211 assertx(arr[s_nullable].toBoolean());
212 name += '?';
215 assertx(arr.exists(s_kind));
217 TypeStructure::Kind kind =
218 TypeStructure::Kind(arr[s_kind].toInt64Val());
219 switch (kind) {
220 case TypeStructure::Kind::T_void:
221 name += s_void;
222 break;
223 case TypeStructure::Kind::T_int:
224 name += s_int;
225 break;
226 case TypeStructure::Kind::T_bool:
227 name += s_bool;
228 break;
229 case TypeStructure::Kind::T_float:
230 name += s_float;
231 break;
232 case TypeStructure::Kind::T_string:
233 name += s_string;
234 break;
235 case TypeStructure::Kind::T_resource:
236 name += s_resource;
237 break;
238 case TypeStructure::Kind::T_num:
239 name += s_num;
240 break;
241 case TypeStructure::Kind::T_arraykey:
242 name += s_arraykey;
243 break;
244 case TypeStructure::Kind::T_noreturn:
245 name += s_noreturn;
246 break;
247 case TypeStructure::Kind::T_mixed:
248 name += s_mixed;
249 break;
250 case TypeStructure::Kind::T_tuple:
251 tupleTypeName(arr, name);
252 break;
253 case TypeStructure::Kind::T_fun:
254 functionTypeName(arr, name);
255 break;
256 case TypeStructure::Kind::T_array:
257 name += s_array;
258 if (arr.exists(s_generic_types)) {
259 genericTypeName(arr, name);
261 break;
262 case TypeStructure::Kind::T_shape:
263 name += s_shape;
264 shapeTypeName(arr, name);
265 break;
266 case TypeStructure::Kind::T_dict:
267 name += s_dict;
268 if (arr.exists(s_generic_types)) {
269 genericTypeName(arr, name);
271 break;
272 case TypeStructure::Kind::T_vec:
273 name += s_vec;
274 if (arr.exists(s_generic_types)) {
275 genericTypeName(arr, name);
277 break;
278 case TypeStructure::Kind::T_keyset:
279 name += s_keyset;
280 if (arr.exists(s_generic_types)) {
281 genericTypeName(arr, name);
283 break;
284 case TypeStructure::Kind::T_vec_or_dict:
285 name += s_vec_or_dict;
286 if (arr.exists(s_generic_types)) {
287 genericTypeName(arr, name);
289 break;
290 case TypeStructure::Kind::T_typevar:
291 assertx(arr.exists(s_name));
292 name += arr[s_name].toCStrRef().toCppString();
293 break;
294 case TypeStructure::Kind::T_typeaccess:
295 accessTypeName(arr, name);
296 break;
297 case TypeStructure::Kind::T_xhp:
298 xhpTypeName(arr, name);
299 break;
300 case TypeStructure::Kind::T_class:
301 case TypeStructure::Kind::T_interface:
302 case TypeStructure::Kind::T_trait:
303 case TypeStructure::Kind::T_enum:
304 case TypeStructure::Kind::T_unresolved:
305 assertx(arr.exists(s_classname));
306 name += arr[s_classname].toCStrRef().toCppString();
307 if (arr.exists(s_generic_types)) genericTypeName(arr, name);
308 break;
311 return name;
314 Array resolveTS(const Array& arr,
315 const Class::Const& typeCns,
316 const Class* typeCnsCls,
317 const Array& generics,
318 bool& persistent);
320 Array resolveList(const Array& arr,
321 const Class::Const& typeCns,
322 const Class* typeCnsCls,
323 const Array& generics,
324 bool& persistent) {
325 auto const sz = arr.size();
327 VArrayInit newarr(sz);
328 for (auto i = 0; i < sz; i++) {
329 auto elemArr = arr[i].toArray();
330 auto elem = resolveTS(elemArr, typeCns, typeCnsCls, generics, persistent);
331 newarr.append(Variant(elem));
334 return newarr.toArray();
337 std::string resolveContextMsg(const Class::Const& typeCns,
338 const Class* typeCnsCls) {
339 std::string msg("when resolving ");
340 if (typeCnsCls) {
341 folly::toAppend("type constant ", typeCnsCls->name()->data(),
342 "::", typeCns.name->data(),
343 &msg);
344 } else {
345 folly::toAppend("type alias ", typeCns.name->data(), &msg);
347 return msg;
350 /* returns the unresolved TypeStructure; if aliasName is not an alias,
351 * return an empty Array. */
352 Array getAlias(const String& aliasName, bool& persistent) {
353 if (aliasName.same(s_this) || Unit::lookupClass(aliasName.get())) {
354 return Array::CreateDArray();
357 auto persistentTA = true;
358 auto typeAliasReq = Unit::loadTypeAlias(aliasName.get(), &persistentTA);
359 if (!typeAliasReq) return Array::CreateDArray();
361 // this returned type structure is unresolved.
362 assertx(typeAliasReq->typeStructure.isDictOrDArray());
363 persistent &= persistentTA;
364 return typeAliasReq->typeStructure;
367 const Class* getClass(const String& clsName,
368 const Class::Const& typeCns,
369 const Class* typeCnsCls,
370 bool& persistent) {
371 auto checkPersistent = [&persistent](const Class* cls) {
372 persistent &= classHasPersistentRDS(cls);
373 return cls;
376 // the original unresolved type structure came from a type constant
377 // (instead of a type alias), and may have this/self/parent.
378 if (typeCnsCls) {
379 // HH\this: late static binding
380 if (clsName.same(s_this)) {
381 return checkPersistent(typeCnsCls);
384 auto declCls = typeCns.cls;
385 // self
386 if (clsName.same(s_self)) {
387 return checkPersistent(declCls);
389 // parent
390 if (clsName.same(s_parent)) {
391 auto parent = declCls->parent();
392 if (!parent) {
393 throw Exception(
394 "%s, class %s does not have a parent",
395 resolveContextMsg(typeCns, typeCnsCls).c_str(),
396 declCls->name()->data());
398 return checkPersistent(parent);
402 auto name = clsName;
403 auto ts = getAlias(name, persistent);
404 while (!ts.empty()) {
405 assertx(ts.exists(s_kind));
406 if (!ts.exists(s_classname)) {
407 // not a class, interface, trait, enum, or alias
408 throw Exception(
409 "%s, alias %s does not resolve to a class",
410 resolveContextMsg(typeCns, typeCnsCls).c_str(),
411 name.data());
413 name = ts[s_classname].toCStrRef();
414 ts = getAlias(name, persistent);
417 auto const cls = Unit::loadClass(name.get());
418 if (!cls) {
419 throw Exception(
420 "%s, class %s not found",
421 resolveContextMsg(typeCns, typeCnsCls).c_str(),
422 name.data());
425 return checkPersistent(cls);
428 /* Given an unresolved T_shape TypeStructure, returns the __fields__
429 * portion of the array with all the field names resolved to string
430 * literals. */
431 Array resolveShape(const Array& arr,
432 const Class::Const& typeCns,
433 const Class* typeCnsCls,
434 const Array& generics,
435 bool& persistent) {
436 assertx(arr.exists(s_kind));
437 assertx(static_cast<TypeStructure::Kind>(arr[s_kind].toInt64Val())
438 == TypeStructure::Kind::T_shape);
439 assertx(arr.exists(s_fields));
441 auto newfields = Array::CreateDArray();
442 auto const fields = arr[s_fields].toCArrRef();
443 auto const sz = fields.size();
444 for (auto i = 0; i < sz; i++) {
445 Variant key = fields->getKey(i);
446 auto const wrapper = fields->getValue(i).toCArrRef();
447 if (wrapper.exists(s_is_cls_cns)) {
448 // if the shape field name is a class constant, its name is
449 // double colon delimited clsName::cnsName
450 auto const clsCns = key.toCStrRef().toCppString();
451 std::string clsName, cnsName;
452 folly::split("::", clsCns, clsName, cnsName);
454 // look up clsName::cnsName
455 auto cls = getClass(String(clsName), typeCns, typeCnsCls, persistent);
456 auto cnsValue = cls->clsCnsGet(String(cnsName).get());
458 if (isStringType(cnsValue.m_type) || isIntType(cnsValue.m_type)) {
459 key = tvAsVariant(&cnsValue);
460 } else {
461 throw Exception(
462 "class constant %s::%s is not a string or an int "
463 "and cannot be used as shape field names",
464 clsName.c_str(), cnsName.c_str());
467 assertx(wrapper.exists(s_value));
468 auto valueArr = wrapper[s_value].toArray();
469 auto value =
470 resolveTS(valueArr, typeCns, typeCnsCls, generics, persistent);
472 if (wrapper.exists(s_optional_shape_field)) {
473 value.add(s_optional_shape_field, true_varNR.tv());
476 newfields.add(key, Variant(value));
479 return newfields;
482 void resolveClass(Array& ret,
483 const String& clsName,
484 const Class::Const& typeCns,
485 const Class* typeCnsCls,
486 bool& persistent) {
487 auto const cls = getClass(clsName, typeCns, typeCnsCls, persistent);
489 TypeStructure::Kind resolvedKind;
490 if (isNormalClass(cls)) {
491 resolvedKind = TypeStructure::Kind::T_class;
492 } else if (isInterface(cls)) {
493 resolvedKind = TypeStructure::Kind::T_interface;
494 } else if (isTrait(cls)) {
495 resolvedKind = TypeStructure::Kind::T_trait;
496 } else if (isEnum(cls)) {
497 resolvedKind = TypeStructure::Kind::T_enum;
498 } else {
499 not_reached();
502 ret.set(s_kind, Variant(static_cast<uint8_t>(resolvedKind)));
503 ret.add(s_classname, Variant(makeStaticString(cls->name())));
506 Array resolveGenerics(const Array& arr,
507 const Class::Const& typeCns,
508 const Class* typeCnsCls,
509 const Array& generics,
510 bool& persistent) {
511 auto genericsArr = arr[s_generic_types].toArray();
512 return resolveList(genericsArr, typeCns, typeCnsCls, generics, persistent);
515 Array resolveTS(const Array& arr,
516 const Class::Const& typeCns,
517 const Class* typeCnsCls,
518 const Array& generics,
519 bool& persistent) {
520 assertx(arr.exists(s_kind));
521 auto const kind = static_cast<TypeStructure::Kind>(
522 arr[s_kind].toInt64Val());
524 auto newarr = Array::CreateDArray();
525 if (arr.exists(s_nullable)) newarr.add(s_nullable, true_varNR.tv());
526 newarr.add(s_kind, Variant(static_cast<uint8_t>(kind)));
528 if (arr.exists(s_allows_unknown_fields)) {
529 newarr.add(s_allows_unknown_fields, true_varNR.tv());
532 switch (kind) {
533 case TypeStructure::Kind::T_tuple: {
534 assertx(arr.exists(s_elem_types));
535 auto const elemsArr = arr[s_elem_types].toCArrRef();
536 auto const elemTypes =
537 resolveList(elemsArr, typeCns, typeCnsCls, generics, persistent);
538 newarr.add(s_elem_types, Variant(elemTypes));
539 break;
541 case TypeStructure::Kind::T_fun: {
542 assertx(arr.exists(s_return_type));
543 auto const returnArr = arr[s_return_type].toCArrRef();
544 auto const returnType =
545 resolveTS(returnArr, typeCns, typeCnsCls, generics, persistent);
546 newarr.add(s_return_type, Variant(returnType));
548 assertx(arr.exists(s_param_types));
549 auto const paramsArr = arr[s_param_types].toCArrRef();
550 auto const paramTypes =
551 resolveList(paramsArr, typeCns, typeCnsCls, generics, persistent);
552 newarr.add(s_param_types, Variant(paramTypes));
553 break;
555 case TypeStructure::Kind::T_array:
556 case TypeStructure::Kind::T_dict:
557 case TypeStructure::Kind::T_vec:
558 case TypeStructure::Kind::T_keyset:
559 case TypeStructure::Kind::T_vec_or_dict: {
560 if (arr.exists(s_generic_types)) {
561 newarr.add(s_generic_types,
562 Variant(resolveGenerics(arr, typeCns, typeCnsCls,
563 generics, persistent)));
565 break;
567 case TypeStructure::Kind::T_shape: {
568 auto const fields =
569 resolveShape(arr, typeCns, typeCnsCls, generics, persistent);
570 newarr.add(s_fields, Variant(fields));
571 break;
573 case TypeStructure::Kind::T_unresolved: {
574 assertx(arr.exists(s_classname));
575 auto const clsName = arr[s_classname].toCStrRef();
576 auto ts = getAlias(clsName, persistent);
577 if (!ts.empty()) {
578 if (ts.exists(s_typevars) && arr.exists(s_generic_types)) {
579 std::vector<std::string> typevars;
580 folly::split(",", ts[s_typevars].toCStrRef().data(), typevars);
581 ts.remove(s_typevars);
583 auto generic_types =
584 resolveGenerics(arr, typeCns, typeCnsCls, generics, persistent);
586 auto const sz = std::min(static_cast<ssize_t>(typevars.size()),
587 generic_types.size());
588 DArrayInit newarr(sz);
589 for (auto i = 0; i < sz; i++) {
590 newarr.add(String(typevars[i]), generic_types[i]);
592 auto generics = newarr.toArray();
593 ts = TypeStructure::resolve(clsName, ts, persistent, generics);
594 ts.add(s_typevar_types, Variant(generics));
595 } else {
596 ts = TypeStructure::resolve(clsName, ts, persistent);
598 if (arr.exists(s_nullable)) {
599 ts.add(s_nullable, true_varNR.tv());
602 return ts;
605 /* Special cases for 'callable': Hack typechecker throws a naming error
606 * (unbound name), however, hhvm still supports this type hint to be
607 * compatible with php. We simply return as a OF_CLASS with class name
608 * set to 'callable'. */
609 if (clsName.same(s_callable)) {
610 newarr.add(s_kind,
611 Variant(static_cast<uint8_t>(TypeStructure::Kind::T_class)));
612 newarr.add(s_classname, Variant(clsName));
613 break;
615 resolveClass(newarr, clsName, typeCns, typeCnsCls, persistent);
616 if (arr.exists(s_generic_types)) {
617 newarr.add(s_generic_types,
618 Variant(resolveGenerics(arr, typeCns, typeCnsCls,
619 generics, persistent)));
621 break;
623 case TypeStructure::Kind::T_typeaccess: {
624 /* type access is a root class (may be HH\this) followed by a series of
625 * type constants, i.e., cls::TC1::TC2::TC3. Each type constant other
626 * than the last one in the chain must refer to a class or an
627 * interface. */
628 assertx(arr.exists(s_root_name));
629 auto clsName = arr[s_root_name].toCStrRef();
630 assertx(arr.exists(s_access_list));
631 auto const accList = arr[s_access_list].toCArrRef();
632 auto const sz = accList.size();
633 Array typeCnsVal;
634 for (auto i = 0; i < sz; i++) {
635 auto const cls = getClass(clsName, typeCns, typeCnsCls, persistent);
636 auto const cnsName = accList[i].toCStrRef();
637 if (!cls->hasTypeConstant(cnsName.get())) {
638 throw Exception(
639 "%s, class %s does not have non-abstract "
640 "type constant %s",
641 resolveContextMsg(typeCns, typeCnsCls).c_str(),
642 clsName.data(),
643 cnsName.data());
645 auto tv = cls->clsCnsGet(cnsName.get(), /* includeTypeCns = */ true);
646 assertx(isArrayLikeType(tv.m_type));
647 typeCnsVal = Array(tv.m_data.parr);
648 assertx(typeCnsVal.isDictOrDArray());
649 if (i == sz - 1) break;
651 // if there are more accesses, keep resolving
652 assertx(typeCnsVal.exists(s_kind));
653 auto kind = static_cast<TypeStructure::Kind>
654 (typeCnsVal[s_kind].toInt64Val());
655 if (kind != TypeStructure::Kind::T_class
656 && kind != TypeStructure::Kind::T_interface) {
657 throw Exception(
658 "%s, %s::%s does not resolve to a class or "
659 "an interface and cannot contain type constant %s",
660 resolveContextMsg(typeCns, typeCnsCls).c_str(),
661 clsName.data(),
662 cnsName.data(),
663 accList[i+1].toCStrRef().data());
665 assertx(typeCnsVal.exists(s_classname));
666 clsName = typeCnsVal[s_classname].toCStrRef();
669 if (arr.exists(s_nullable)) {
670 typeCnsVal.add(s_nullable, true_varNR.tv());
673 return typeCnsVal;
675 case TypeStructure::Kind::T_typevar: {
676 assertx(arr.exists(s_name));
677 auto const name = arr[s_name].toCStrRef();
678 return generics.exists(name) ? generics[name].toDArray() : arr.toDArray();
680 case TypeStructure::Kind::T_xhp:
681 default:
682 return arr.toDArray();
685 if(arr.exists(s_typevars)) newarr.add(s_typevars, arr[s_typevars]);
687 return newarr;
690 } // anonymous namespace
692 String TypeStructure::toString(const Array& arr) {
693 if (arr.empty()) return String();
695 return String(fullName(arr));
699 * Constructs a scalar array with all the shape field names, this/self/parent,
700 * classes, type accesses, and type aliases resolved.
702 Array TypeStructure::resolve(const Class::Const& typeCns,
703 const Class* typeCnsCls,
704 bool& persistent) {
705 assertx(typeCns.isType());
706 assertx(isArrayLikeType(typeCns.val.m_type));
707 assertx(typeCns.name);
708 assertx(typeCnsCls);
710 Array arr(typeCns.val.m_data.parr);
711 return resolveTS(arr, typeCns, typeCnsCls, Array(), persistent);
715 * Called by TypeAliasReq to get resolved TypeStructure for type aliases.
717 Array TypeStructure::resolve(const String& aliasName,
718 const Array& arr,
719 bool& persistent,
720 const Array& generics) {
721 // use a bogus constant to store the name
722 Class::Const cns;
723 cns.name = aliasName.get();
725 auto newarr = resolveTS(arr, cns, nullptr, generics, persistent);
726 newarr.add(s_alias, Variant(aliasName));
727 return newarr;
730 } // namespace HPHP