2 +----------------------------------------------------------------------+
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/vm/type-constraint.h"
19 #include <folly/Format.h>
20 #include <folly/MapUtil.h>
22 #include "hphp/util/trace.h"
24 #include "hphp/runtime/base/autoload-handler.h"
25 #include "hphp/runtime/base/execution-context.h"
26 #include "hphp/runtime/base/runtime-error.h"
27 #include "hphp/runtime/vm/act-rec.h"
28 #include "hphp/runtime/vm/class.h"
29 #include "hphp/runtime/vm/func.h"
30 #include "hphp/runtime/vm/hhbc.h"
31 #include "hphp/runtime/vm/jit/translator-inline.h"
32 #include "hphp/runtime/vm/repo-global-data.h"
33 #include "hphp/runtime/vm/repo.h"
34 #include "hphp/runtime/vm/runtime.h"
35 #include "hphp/runtime/vm/unit.h"
36 #include "hphp/runtime/vm/vm-regs.h"
38 #include "hphp/runtime/ext/std/ext_std_function.h"
42 TRACE_SET_MOD(runtime
);
44 //////////////////////////////////////////////////////////////////////
46 const StaticString
s___invoke("__invoke"),
48 s_varray
{"HH\\varray"},
49 s_darray
{"HH\\darray"},
50 s_varray_or_darray
{"HH\\varray_or_darray"},
53 s_vec_or_dict
{"HH\\vec_or_dict"};
55 void TypeConstraint::init() {
56 if (m_typeName
== nullptr || isTypeVar() || isTypeConstant()) {
60 TRACE(5, "TypeConstraint: this %p type %s, nullable %d\n",
61 this, m_typeName
->data(), isNullable());
62 auto const mptr
= nameToAnnotType(m_typeName
);
65 assertx(getAnnotDataType(m_type
) != KindOfPersistentString
);
66 if (RuntimeOption::EvalHackArrDVArrs
) {
67 assertx(m_type
!= AnnotType::VArray
&&
68 m_type
!= AnnotType::DArray
&&
69 m_type
!= AnnotType::VArrOrDArr
);
70 if (m_typeName
->isame(s_varray
.get())) {
71 m_typeName
= s_vec
.get();
72 } else if (m_typeName
->isame(s_darray
.get())) {
73 m_typeName
= s_dict
.get();
74 } else if (m_typeName
->isame(s_varray_or_darray
.get())) {
75 m_typeName
= s_vec_or_dict
.get();
80 if (m_flags
& Flags::Resolved
) {
81 TRACE(5, "TypeConstraint: this %p pre-resolved type %s, treating as %s\n",
82 this, m_typeName
->data(), tname(getAnnotDataType(m_type
)).c_str());
84 TRACE(5, "TypeConstraint: this %p no such type %s, treating as object\n",
85 this, m_typeName
->data());
86 m_type
= Type::Object
;
88 m_namedEntity
= NamedEntity::get(m_typeName
);
89 TRACE(5, "TypeConstraint: this %p NamedEntity: %p\n",
90 this, m_namedEntity
.get());
93 std::string
TypeConstraint::displayName(const Func
* func
/*= nullptr*/,
94 bool extra
/* = false */) const {
95 const StringData
* tn
= typeName();
100 if (isNullable() && isExtended()) {
103 if (func
&& isSelf()) {
104 selfToTypeName(func
, &tn
);
106 } else if (func
&& isParent()) {
107 parentToTypeName(func
, &tn
);
110 const char* str
= tn
->data();
111 auto len
= tn
->size();
112 if (len
> 3 && tolower(str
[0]) == 'h' && tolower(str
[1]) == 'h' &&
115 const char* stripped
= str
+ 3;
118 strip
= (!strcasecmp(stripped
, "int") ||
119 !strcasecmp(stripped
, "num"));
122 strip
= (!strcasecmp(stripped
, "bool") ||
123 !strcasecmp(stripped
, "this"));
125 case 5: strip
= !strcasecmp(stripped
, "float"); break;
126 case 6: strip
= !strcasecmp(stripped
, "string"); break;
128 strip
= (!strcasecmp(stripped
, "resource") ||
129 !strcasecmp(stripped
, "noreturn") ||
130 !strcasecmp(stripped
, "arraykey"));
141 if (extra
&& m_flags
& Flags::Resolved
&& m_type
!= AnnotType::Object
) {
142 const char* str
= nullptr;
144 case AnnotType::Uninit
: str
= "uninit"; break;
145 case AnnotType::Null
: str
= "null"; break;
146 case AnnotType::Bool
: str
= "bool"; break;
147 case AnnotType::Int
: str
= "int"; break;
148 case AnnotType::Float
: str
= "float"; break;
149 case AnnotType::String
: str
= "string"; break;
150 case AnnotType::Array
: str
= "array"; break;
151 case AnnotType::Resource
: str
= "resource"; break;
152 case AnnotType::Dict
: str
= "dict"; break;
153 case AnnotType::Vec
: str
= "vec"; break;
154 case AnnotType::Keyset
: str
= "keyset"; break;
155 case AnnotType::Number
: str
= "num"; break;
156 case AnnotType::ArrayKey
: str
= "arraykey"; break;
157 case AnnotType::VArray
: str
= "varray"; break;
158 case AnnotType::DArray
: str
= "darray"; break;
159 case AnnotType::VArrOrDArr
: str
= "varray_or_darray"; break;
160 case AnnotType::VecOrDict
: str
= "vec_or_dict"; break;
161 case AnnotType::Self
:
162 case AnnotType::This
:
163 case AnnotType::Parent
:
164 case AnnotType::Object
:
165 case AnnotType::Mixed
:
166 case AnnotType::Callable
:
169 if (str
) folly::format(&name
, " ({})", str
);
174 bool TypeConstraint::compat(const TypeConstraint
& other
) const {
175 if (other
.isExtended() || isExtended()) {
177 * Rely on the ahead of time typechecker---checking here can
178 * make it harder to convert a base class or interface to <?hh,
179 * because derived classes that are still <?php would all need
185 if (m_typeName
== other
.m_typeName
) {
189 if (m_typeName
&& other
.m_typeName
) {
190 if (m_typeName
->isame(other
.m_typeName
)) {
194 auto const is_array
= [](const StringData
* str
) {
195 return str
->isame(s_array
.get()) ||
196 str
->isame(s_varray
.get()) ||
197 str
->isame(s_darray
.get()) ||
198 str
->isame(s_varray_or_darray
.get());
200 if (is_array(m_typeName
) && is_array(other
.m_typeName
)) {
204 const Class
* cls
= Unit::lookupClass(m_typeName
);
205 const Class
* otherCls
= Unit::lookupClass(other
.m_typeName
);
207 return cls
&& otherCls
&& cls
== otherCls
;
216 * Look up a TypeAliasReq for the supplied NamedEntity (which must be the
217 * NamedEntity for `name'), invoking autoload if necessary for types but not
220 * We don't need to autoload classes because it is impossible to have an
221 * instance of a class if it's not defined. However, we need to autoload
222 * typedefs because they can affect whether VerifyParamType would succeed.
224 const TypeAliasReq
* getTypeAliasWithAutoload(const NamedEntity
* ne
,
225 const StringData
* name
) {
226 auto def
= ne
->getCachedTypeAlias();
229 String
nameStr(const_cast<StringData
*>(name
));
230 if (!AutoloadHandler::s_instance
->autoloadType(nameStr
)) {
233 def
= ne
->getCachedTypeAlias();
239 * Look up a TypeAliasReq or a Class for the supplied NamedEntity
240 * (which must be the NamedEntity for `name'), invoking autoload if
243 * This is useful when looking up a type annotation that could be either a
244 * type alias or an enum class; enum classes are strange in that it
245 * *is* possible to have an instance of them even if they are not defined.
248 std::pair
<const TypeAliasReq
*, Class
*> getTypeAliasOrClassWithAutoload(
249 const NamedEntity
* ne
,
250 const StringData
* name
) {
252 auto def
= ne
->getCachedTypeAlias();
253 Class
*klass
= nullptr;
255 klass
= Unit::lookupClass(ne
);
256 // We don't have the class or the typedef, so autoload.
258 String
nameStr(const_cast<StringData
*>(name
));
259 if (AutoloadHandler::s_instance
->autoloadClassOrType(nameStr
)) {
260 // Autoload succeeded, try to grab a typedef and if that doesn't work,
262 def
= ne
->getCachedTypeAlias();
264 klass
= Unit::lookupClass(ne
);
270 assertx(!def
|| !klass
);
271 return std::make_pair(def
, klass
);
276 MaybeDataType
TypeConstraint::underlyingDataTypeResolved() const {
277 assertx(!isSelf() && !isParent() && !isCallable());
279 !hasConstraint() || isTypeVar() || isTypeConstant(),
283 if (isVArray() || isDArray() || isVArrayOrDArray()) return KindOfArray
;
287 auto t
= underlyingDataType();
290 // If we aren't a class or type alias, nothing special to do.
291 if (!isObject()) return t
;
293 assertx(t
== KindOfObject
);
294 auto p
= getTypeAliasOrClassWithAutoload(m_namedEntity
, m_typeName
);
298 // See if this is a type alias.
300 if (td
->type
!= Type::Object
) {
301 auto const metatype
= getAnnotMetaType(td
->type
);
302 if (metatype
== MetaType::Precise
) {
303 t
= getAnnotDataType(td
->type
);
304 } else if (metatype
== MetaType::VArray
|| metatype
== MetaType::DArray
||
305 metatype
== MetaType::VArrOrDArr
) {
315 // If the underlying type is a class, see if it is an enum and get that.
316 if (c
&& isEnum(c
)) {
323 bool TypeConstraint::checkTypeAliasNonObj(const TypedValue
* tv
) const {
324 assertx(tv
->m_type
!= KindOfObject
);
327 auto p
= getTypeAliasOrClassWithAutoload(m_namedEntity
, m_typeName
);
331 // Common case is that we actually find the alias:
333 if (td
->nullable
&& tv
->m_type
== KindOfNull
) return true;
334 auto result
= annotCompat(tv
->m_type
, td
->type
,
335 td
->klass
? td
->klass
->name() : nullptr);
337 case AnnotAction::Pass
: return true;
338 case AnnotAction::Fail
: return false;
339 case AnnotAction::CallableCheck
:
340 return is_callable(tvAsCVarRef(tv
));
341 case AnnotAction::ObjectCheck
: break;
342 case AnnotAction::VArrayCheck
:
343 assertx(tvIsArray(tv
));
344 return tv
->m_data
.parr
->isVArray();
345 case AnnotAction::DArrayCheck
:
346 assertx(tvIsArray(tv
));
347 return tv
->m_data
.parr
->isDArray();
348 case AnnotAction::VArrayOrDArrayCheck
:
349 assertx(tvIsArray(tv
));
350 return !tv
->m_data
.parr
->isNotDVArray();
351 case AnnotAction::NonVArrayOrDArrayCheck
:
352 assertx(tvIsArray(tv
));
353 return tv
->m_data
.parr
->isNotDVArray();
355 assertx(result
== AnnotAction::ObjectCheck
);
356 assertx(td
->type
== AnnotType::Object
);
357 // Fall through to the check below, since this could be a type
358 // alias to an enum type
362 // Otherwise, this isn't a proper type alias, but it *might* be a
363 // first-class enum. Check if the type is an enum and check the
364 // constraint if it is. We only need to do this when the underlying
365 // type is not an object, since only int and string can be enums.
366 if (c
&& isEnum(c
)) {
367 auto dt
= c
->enumBaseTy();
368 // For an enum, if the underlying type is mixed, we still require
369 // it is either an int or a string!
371 return equivDataTypes(*dt
, tv
->m_type
);
373 return isIntType(tv
->m_type
) || isStringType(tv
->m_type
);
379 bool TypeConstraint::checkTypeAliasObj(const Class
* cls
) const {
380 assertx(isObject() && m_namedEntity
&& m_typeName
);
381 // Look up the type alias (autoloading if necessary)
382 // and fail if we can't find it
383 auto const td
= getTypeAliasWithAutoload(m_namedEntity
, m_typeName
);
387 // We found the type alias, check if an object of type cls
389 switch (getAnnotMetaType(td
->type
)) {
390 case AnnotMetaType::Precise
:
391 return td
->type
== AnnotType::Object
&& td
->klass
&&
392 cls
->classof(td
->klass
);
393 case AnnotMetaType::Mixed
:
395 case AnnotMetaType::Callable
:
396 return cls
->lookupMethod(s___invoke
.get()) != nullptr;
397 case AnnotMetaType::Self
:
398 case AnnotMetaType::Parent
:
399 case AnnotMetaType::Number
:
400 case AnnotMetaType::ArrayKey
:
401 case AnnotMetaType::This
:
402 case AnnotMetaType::VArray
:
403 case AnnotMetaType::DArray
:
404 case AnnotMetaType::VArrOrDArr
:
405 case AnnotMetaType::VecOrDict
:
406 // Self and Parent should never happen, because type
407 // aliases are not allowed to use those MetaTypes
414 void TypeConstraint::verifyReturnNonNull(TypedValue
* tv
, const Func
* func
,
415 bool useStrictTypes
) const {
416 const auto DEBUG_ONLY tc
= func
->returnTypeConstraint();
417 assertx(!tc
.isNullable());
418 if (UNLIKELY(cellIsNull(tv
))) {
419 verifyReturnFail(func
, tv
, useStrictTypes
);
421 auto vm
= &*g_context
;
424 "HHBBC incorrectly converted VerifyRetTypeC to VerifyRetNonNull in {}:{}",
425 vm
->getContainingFileName()->data(),
431 bool TypeConstraint::check(TypedValue
* tv
, const Func
* func
) const {
432 assertx(hasConstraint() && !isTypeVar() && !isMixed() && !isTypeConstant());
434 // This is part of the interpreter runtime; perf matters.
435 if (tv
->m_type
== KindOfRef
) {
436 tv
= tv
->m_data
.pref
->tv();
439 if (isNullable() && tv
->m_type
== KindOfNull
) {
443 if (tv
->m_type
== KindOfObject
) {
444 // Perfect match seems common enough to be worth skipping the hash
446 const Class
*c
= nullptr;
448 if (m_typeName
->isame(tv
->m_data
.pobj
->getVMClass()->name())) {
451 // We can't save the Class* since it moves around from request
453 assertx(m_namedEntity
);
454 c
= Unit::lookupClass(m_namedEntity
);
456 switch (metaType()) {
458 selfToClass(func
, &c
);
461 switch (RuntimeOption::EvalThisTypeHintLevel
) {
462 case 0: // Like Mixed.
465 case 1: // Like Self.
466 selfToClass(func
, &c
);
468 case 2: // Soft this in irgen verifyTypeImpl and verifyFail.
469 case 3: // Hard this.
472 return tv
->m_data
.pobj
->getVMClass() == c
;
478 case MetaType::Parent
:
479 parentToClass(func
, &c
);
481 case MetaType::Callable
:
482 return is_callable(tvAsCVarRef(tv
));
483 case MetaType::Precise
:
484 case MetaType::Number
:
485 case MetaType::ArrayKey
:
486 case MetaType::VArray
:
487 case MetaType::DArray
:
488 case MetaType::VArrOrDArr
:
489 case MetaType::VecOrDict
:
491 case MetaType::Mixed
:
492 // We assert'd at the top of this function that the
493 // metatype cannot be Mixed
497 if (c
&& tv
->m_data
.pobj
->instanceof(c
)) {
500 return isObject() && checkTypeAliasObj(tv
->m_data
.pobj
->getVMClass());
503 auto const result
= annotCompat(tv
->m_type
, m_type
, m_typeName
);
505 case AnnotAction::Pass
: return true;
506 case AnnotAction::Fail
: return false;
507 case AnnotAction::CallableCheck
:
508 return is_callable(tvAsCVarRef(tv
));
509 case AnnotAction::ObjectCheck
:
511 return checkTypeAliasNonObj(tv
);
512 case AnnotAction::VArrayCheck
:
513 assertx(tvIsArray(tv
));
514 return tv
->m_data
.parr
->isVArray();
515 case AnnotAction::DArrayCheck
:
516 assertx(tvIsArray(tv
));
517 return tv
->m_data
.parr
->isDArray();
518 case AnnotAction::VArrayOrDArrayCheck
:
519 assertx(tvIsArray(tv
));
520 return !tv
->m_data
.parr
->isNotDVArray();
521 case AnnotAction::NonVArrayOrDArrayCheck
:
522 assertx(tvIsArray(tv
));
523 return tv
->m_data
.parr
->isNotDVArray();
528 const char* describe_actual_type(const TypedValue
* tv
, bool isHHType
) {
530 switch (tv
->m_type
) {
531 case KindOfUninit
: return "undefined variable";
532 case KindOfNull
: return "null";
533 case KindOfBoolean
: return "bool";
534 case KindOfInt64
: return "int";
535 case KindOfDouble
: return isHHType
? "float" : "double";
536 case KindOfPersistentString
:
537 case KindOfString
: return "string";
538 case KindOfPersistentVec
:
539 case KindOfVec
: return "HH\\vec";
540 case KindOfPersistentDict
:
541 case KindOfDict
: return "HH\\dict";
542 case KindOfPersistentKeyset
:
543 case KindOfKeyset
: return "HH\\keyset";
544 case KindOfPersistentArray
:
545 case KindOfArray
: return "array";
546 case KindOfObject
: return tv
->m_data
.pobj
->getClassName().c_str();
548 return tv
->m_data
.pres
->data()->o_getClassName().c_str();
556 void TypeConstraint::verifyParamFail(const Func
* func
, TypedValue
* tv
,
557 int paramNum
, bool useStrictTypes
) const {
558 verifyFail(func
, tv
, paramNum
, useStrictTypes
);
560 isSoft() || !RuntimeOption::EvalHardTypeHints
|| (isThis() && couldSeeMockObject()) ||
561 (RuntimeOption::EvalHackArrCompatTypeHintNotices
&&
562 isArrayType(tv
->m_type
)) ||
567 void TypeConstraint::verifyOutParamFail(const Func
* func
,
569 int paramNum
) const {
570 auto const c
= tvToCell(tv
);
572 auto const done
= [&] {
573 auto const check
= [&](AnnotType at
) {
575 case AnnotType::Array
:
576 if (c
->m_data
.parr
->isNotDVArray()) return true;
578 case AnnotType::VArray
:
579 if (c
->m_data
.parr
->isVArray()) return true;
581 case AnnotType::DArray
:
582 if (c
->m_data
.parr
->isDArray()) return true;
584 case AnnotType::VArrOrDArr
:
585 if (!c
->m_data
.parr
->isNotDVArray()) return true;
590 raise_hackarr_type_hint_outparam_notice(
591 func
, c
->m_data
.parr
, at
, paramNum
595 if (LIKELY(!RuntimeOption::EvalHackArrCompatTypeHintNotices
)) return false;
596 if (!isArrayType(c
->m_type
)) return false;
597 if (isArray()) return check(m_type
);
598 if (!isObject()) return false;
599 if (auto alias
= getTypeAliasWithAutoload(m_namedEntity
, m_typeName
)) {
600 return check(alias
->type
);
606 raise_return_typehint_error(
608 "Argument {} returned from {}() as an inout parameter must be of type "
611 func
->fullDisplayName(),
613 describe_actual_type(tv
, isHHType())
618 void TypeConstraint::verifyFail(const Func
* func
, TypedValue
* tv
,
619 int id
, bool useStrictTypes
) const {
621 std::string name
= displayName(func
);
622 auto const givenType
= describe_actual_type(tv
, isHHType());
624 auto const c
= tvToCell(tv
);
626 auto const done
= [&] {
627 auto const check
= [&](AnnotType at
) {
629 case AnnotType::Array
:
630 if (c
->m_data
.parr
->isNotDVArray()) return true;
632 case AnnotType::VArray
:
633 if (c
->m_data
.parr
->isVArray()) return true;
635 case AnnotType::DArray
:
636 if (c
->m_data
.parr
->isDArray()) return true;
638 case AnnotType::VArrOrDArr
:
639 if (!c
->m_data
.parr
->isNotDVArray()) return true;
644 if (id
== ReturnId
) {
645 raise_hackarr_type_hint_ret_notice(
651 raise_hackarr_type_hint_param_notice(
660 if (LIKELY(!RuntimeOption::EvalHackArrCompatTypeHintNotices
)) return false;
661 if (!isArrayType(c
->m_type
)) return false;
662 if (isArray()) return check(m_type
);
663 if (!isObject()) return false;
664 if (auto alias
= getTypeAliasWithAutoload(m_namedEntity
, m_typeName
)) {
665 return check(alias
->type
);
671 if (UNLIKELY(isThis() && c
->m_type
== KindOfObject
)) {
672 Class
* cls
= c
->m_data
.pobj
->getVMClass();
673 const Class
* thisClass
= nullptr;
674 thisToClass(&thisClass
);
675 if (cls
->preClass()->userAttributes().count(s___MockClass
.get()) &&
676 cls
->parent() == thisClass
) {
681 if (UNLIKELY(!useStrictTypes
)) {
682 if (auto dt
= underlyingDataType()) {
683 // In non-strict mode we may be able to coerce a type failure. For object
684 // typehints there is no possible coercion in the failure case, but HNI
685 // builtins currently only guard on kind not class so the following wil
686 // generate false positives for objects.
687 if (*dt
!= KindOfObject
) {
688 // HNI conversions implicitly unbox references, this behavior is wrong,
689 // in particular it breaks the way type conversion works for PHP 7
691 if (tv
->m_type
== KindOfRef
) {
692 auto inner
= tv
->m_data
.pref
->var()->asTypedValue();
693 if (tvCoerceParamInPlace(inner
, *dt
, func
->isBuiltin())) {
694 tvAsVariant(tv
) = tvAsVariant(inner
);
698 if (tvCoerceParamInPlace(tv
, *dt
, func
->isBuiltin())) {
704 } else if (UNLIKELY((!func
->unit()->isHHFile() || func
->isBuiltin()) &&
705 !RuntimeOption::EnableHipHopSyntax
)) {
706 // PHP 7 allows for a widening conversion from Int to Float. We still ban
708 if (auto dt
= underlyingDataType()) {
709 if (*dt
== KindOfDouble
&& tv
->m_type
== KindOfInt64
&&
710 tvCoerceParamToDoubleInPlace(tv
, func
->isBuiltin())) {
716 // Handle return type constraint failures
717 if (id
== ReturnId
) {
719 if (func
->isClosureBody()) {
722 "Value returned from {}closure must be of type {}, {} given",
723 func
->isAsync() ? "async " : "",
730 "Value returned from {}{} {}() must be of type {}, {} given",
731 func
->isAsync() ? "async " : "",
732 func
->preClass() ? "method" : "function",
733 func
->fullDisplayName(),
738 if (RuntimeOption::EvalCheckReturnTypeHints
>= 2 && !isSoft()
739 && (!isThis() || RuntimeOption::EvalThisTypeHintLevel
!= 2)) {
740 raise_return_typehint_error(msg
);
742 raise_warning_unsampled(msg
);
747 // Handle implicit collection->array conversion for array parameter type
749 if (isArray() && !isSoft() && !func
->mustBeRef(id
) &&
750 c
->m_type
== KindOfObject
&& c
->m_data
.pobj
->isCollection()) {
751 // To ease migration, the 'array' type constraint will implicitly cast
752 // collections to arrays, provided the type constraint is not soft and
753 // the parameter is not by reference. We raise a notice to let the user
754 // know that there was a type mismatch and that an implicit conversion
758 "Argument {} to {}() must be of type {}, {} given; argument {} was "
759 "implicitly cast to array",
760 id
+ 1, func
->fullDisplayName(), name
, givenType
, id
+ 1
763 tvCastToArrayInPlace(tv
);
767 // Handle parameter type constraint failures
769 (isSoft() || (isThis() && RuntimeOption::EvalThisTypeHintLevel
== 2))) {
770 // Soft extended type hints raise warnings instead of recoverable
771 // errors, to ease migration.
772 raise_warning_unsampled(
774 "Argument {} to {}() must be of type {}, {} given",
775 id
+ 1, func
->fullDisplayName(), name
, givenType
778 } else if (isExtended() && isNullable()) {
779 raise_typehint_error(
781 "Argument {} to {}() must be of type {}, {} given",
782 id
+ 1, func
->fullDisplayName(), name
, givenType
786 auto cls
= Unit::lookupClass(m_typeName
);
787 if (cls
&& isInterface(cls
)) {
788 raise_typehint_error(
790 "Argument {} passed to {}() must implement interface {}, {} given",
791 id
+ 1, func
->fullDisplayName(), name
, givenType
795 raise_typehint_error(
797 "Argument {} passed to {}() must be an instance of {}, {} given",
798 id
+ 1, func
->fullDisplayName(), name
, givenType
805 void TypeConstraint::thisToClass(const Class
**cls
) const {
806 const ActRec
* ar
= vmfp();
807 if (ar
->func()->cls()) {
809 *cls
= ar
->getThis()->getVMClass();
811 assertx(ar
->hasClass());
812 *cls
= ar
->getClass();
817 void TypeConstraint::selfToClass(const Func
* func
, const Class
**cls
) const {
818 const Class
* c
= func
->cls();
824 void TypeConstraint::selfToTypeName(const Func
* func
,
825 const StringData
**typeName
) const {
826 const Class
* c
= func
->cls();
828 *typeName
= c
->name();
832 void TypeConstraint::parentToClass(const Func
* func
, const Class
**cls
) const {
833 Class
* c1
= func
->cls();
834 const Class
* c2
= c1
? c1
->parent() : nullptr;
840 void TypeConstraint::parentToTypeName(const Func
* func
,
841 const StringData
**typeName
) const {
842 const Class
* c
= nullptr;
843 parentToClass(func
, &c
);
845 *typeName
= c
->name();
849 //////////////////////////////////////////////////////////////////////
851 MemoKeyConstraint
memoKeyConstraintFromTC(const TypeConstraint
& tc
) {
852 using MK
= MemoKeyConstraint
;
854 // Soft constraints aren't useful because they're not enforced.
855 if (!tc
.hasConstraint() || tc
.isTypeVar() ||
856 tc
.isTypeConstant() || tc
.isSoft()) {
860 // Only a subset of possible type-constraints are useful to use. Namely,
861 // single types which might be nullable, and int/string combination.
862 switch (tc
.metaType()) {
863 case AnnotMetaType::Precise
: {
864 auto const dt
= tc
.underlyingDataType();
865 assertx(dt
.hasValue());
867 case KindOfNull
: return MK::Null
;
869 return tc
.isNullable() ? MK::BoolOrNull
: MK::Bool
;
871 return tc
.isNullable() ? MK::IntOrNull
: MK::Int
;
872 case KindOfPersistentString
:
874 return tc
.isNullable() ? MK::StrOrNull
: MK::Str
;
876 case KindOfPersistentVec
:
878 case KindOfPersistentDict
:
880 case KindOfPersistentKeyset
:
882 case KindOfPersistentArray
:
885 case KindOfObject
: return MK::None
;
888 always_assert_flog(false, "Unexpected DataType");
892 case AnnotMetaType::ArrayKey
:
893 return tc
.isNullable() ? MK::None
: MK::IntOrStr
;
894 case AnnotMetaType::Mixed
:
895 case AnnotMetaType::Self
:
896 case AnnotMetaType::This
:
897 case AnnotMetaType::Parent
:
898 case AnnotMetaType::Callable
:
899 case AnnotMetaType::Number
:
900 case AnnotMetaType::VArray
:
901 case AnnotMetaType::DArray
:
902 case AnnotMetaType::VArrOrDArr
:
903 case AnnotMetaType::VecOrDict
:
909 //////////////////////////////////////////////////////////////////////