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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/jit/irgen-types.h"
18 #include "hphp/runtime/base/type-structure.h"
19 #include "hphp/runtime/base/type-structure-helpers.h"
20 #include "hphp/runtime/base/type-structure-helpers-defs.h"
22 #include "hphp/runtime/vm/repo-global-data.h"
23 #include "hphp/runtime/vm/runtime.h"
24 #include "hphp/runtime/vm/super-inlining-bros.h"
26 #include "hphp/runtime/vm/jit/is-type-struct-profile.h"
27 #include "hphp/runtime/vm/jit/guard-constraint.h"
28 #include "hphp/runtime/vm/jit/target-profile.h"
29 #include "hphp/runtime/vm/jit/decref-profile.h"
30 #include "hphp/runtime/vm/jit/type.h"
32 #include "hphp/runtime/vm/jit/ir-opcode.h"
33 #include "hphp/runtime/vm/jit/irgen-exit.h"
34 #include "hphp/runtime/vm/jit/irgen-interpone.h"
35 #include "hphp/runtime/vm/jit/irgen-builtin.h"
37 #include "hphp/runtime/vm/jit/irgen-internal.h"
39 namespace HPHP::jit::irgen
{
43 //////////////////////////////////////////////////////////////////////
46 s_StringishObject("StringishObject"),
47 s_Awaitable("HH\\Awaitable"),
48 s_CLASS_TO_STRING_IMPLICIT(Strings::CLASS_TO_STRING_IMPLICIT
),
49 s_CLASS_TO_CLASSNAME(Strings::CLASS_TO_CLASSNAME
);
51 //////////////////////////////////////////////////////////////////////
54 * Returns a {Cls|Nullptr} suitable for use in instance checks.
56 SSATmp
* ldClassSafe(IRGS
& env
, const StringData
* className
) {
57 if (auto const knownCls
= lookupUniqueClass(env
, className
)) {
58 return cns(env
, knownCls
);
64 return gen(env
, LdClsCachedSafe
, taken
, cns(env
, className
));
66 [&] (SSATmp
* cls
) { // next
70 hint(env
, Block::Hint::Unlikely
);
71 return cns(env
, nullptr);
77 * Returns a Bool value indicating if src (which must be <= TObj) is an
78 * instance of the class given in className, or nullptr if we don't have an
79 * efficient translation of the required check. checkCls must be the TCls for
80 * className (but it doesn't have to be constant).
82 SSATmp
* implInstanceCheck(IRGS
& env
, SSATmp
* src
, const StringData
* className
,
84 assertx(src
->isA(TObj
));
85 if (s_Awaitable
.get()->isame(className
)) {
86 return gen(env
, IsWaitHandle
, src
);
88 if (s_StringishObject
.get()->isame(className
)) {
89 return gen(env
, HasToString
, src
);
92 auto knownCls
= checkCls
->hasConstVal(TCls
) ? checkCls
->clsVal() : nullptr;
93 assertx(IMPLIES(knownCls
, classIsUniqueOrCtxParent(env
, knownCls
)));
94 assertx(IMPLIES(knownCls
, knownCls
->name()->isame(className
)));
96 auto const srcType
= src
->type();
99 * If the value is a specialized object type and we don't have to constrain a
100 * guard to get it, we can avoid emitting runtime checks if we know the
101 * result is true. If we don't know, we still have to emit a runtime check
102 * because src might be a subtype of the specialized type.
104 if (srcType
< TObj
&& srcType
.clsSpec()) {
105 auto const cls
= srcType
.clsSpec().cls();
106 if (!env
.irb
->constrainValue(src
, GuardConstraint(cls
).setWeak()) &&
107 ((knownCls
&& cls
->classof(knownCls
)) ||
108 cls
->name()->isame(className
))) {
109 return cns(env
, true);
113 // Every case after this point requires knowing things about knownCls.
114 if (knownCls
== nullptr) return nullptr;
116 auto const ssaClassName
= cns(env
, className
);
117 auto const objClass
= gen(env
, LdObjClass
, src
);
119 if (env
.context
.kind
== TransKind::Profile
&& !InstanceBits::initted()) {
120 gen(env
, ProfileInstanceCheck
, cns(env
, className
));
121 } else if (env
.context
.kind
== TransKind::Optimize
||
122 InstanceBits::initted()) {
123 InstanceBits::init();
124 if (InstanceBits::lookup(className
) != 0) {
125 return gen(env
, InstanceOfBitmask
, objClass
, ssaClassName
);
129 // If the class is an interface, we can just hit the class's vtable or
130 // interface map and call it a day.
131 if (isInterface(knownCls
)) {
132 auto const slot
= knownCls
->preClass()->ifaceVtableSlot();
133 if (slot
!= kInvalidSlot
) {
134 assertx(RO::RepoAuthoritative
);
136 InstanceOfIfaceVtable
,
137 InstanceOfIfaceVtableData
{knownCls
, true},
141 return gen(env
, InstanceOfIface
, objClass
, ssaClassName
);
144 // If knownCls isn't a normal class, our caller may want to do something
146 return isNormalClass(knownCls
) ?
147 gen(env
, ExtendsClass
, ExtendsClassData
{ knownCls
}, objClass
) : nullptr;
150 constexpr size_t kNumDataTypes
= 17;
151 constexpr std::array
<DataType
, kNumDataTypes
> computeDataTypes() {
152 std::array
<DataType
, kNumDataTypes
> result
= {};
154 #define DT(name, value, ...) { \
155 auto constexpr dt = KindOf##name; \
156 if (dt == dt_modulo_persistence(dt)) result[index++] = dt; \
161 always_assert(index
== kNumDataTypes
);
166 constexpr std::array
<DataType
, kNumDataTypes
> kDataTypes
= computeDataTypes();
169 * Emit a type-check for the given type-constraint. Since the details can vary
170 * quite a bit depending on what the type-constraint represents, this function
171 * is heavily templatized.
173 * The lambda parameters are as follows:
175 * - GetVal: Return the SSATmp of the value to test
176 * - GetThisCls: Return the SSATmp of the the class of `this'
177 * - SetVal: Emit code to update the value with the coerced value.
178 * - Fail: Emit code to deal with the type check failing.
179 * - Callable: Emit code to verify that the given value is callable.
180 * - VerifyCls: Emit code to verify that the given value is an instance of the
182 * - Fallback: Called when the type check cannot be resolved statically.
183 * Call a runtime helper to do the check.
185 template <typename TGetVal
,
186 typename TGetThisCls
,
192 void verifyTypeImpl(IRGS
& env
,
193 const TypeConstraint
& tc
,
194 bool onlyCheckNullability
,
196 TGetThisCls getThisCls
,
200 TVerifyCls verifyCls
,
201 TFallback fallback
) {
203 if (!tc
.isCheckable()) return;
204 assertx(!tc
.isUpperBound() || RuntimeOption::EvalEnforceGenericsUB
!= 0);
206 auto const genThisCls
= [&]() {
207 return tc
.isThis() ? getThisCls() : cns(env
, nullptr);
210 auto const genFail
= [&](SSATmp
* val
, SSATmp
* thisCls
= nullptr) {
211 if (thisCls
== nullptr) thisCls
= genThisCls();
213 auto const failHard
= RuntimeOption::RepoAuthoritative
215 && (!tc
.isThis() || !tc
.couldSeeMockObject())
216 && (!tc
.isUpperBound() || RuntimeOption::EvalEnforceGenericsUB
>= 2);
217 return fail(val
, thisCls
, failHard
);
220 auto const checkOneType
= [&](SSATmp
* val
, AnnotAction result
) {
221 assertx(val
->type().isKnownDataType());
224 case AnnotAction::Pass
:
227 case AnnotAction::Fail
:
231 case AnnotAction::Fallback
:
232 fallback(val
, genThisCls(), false);
235 case AnnotAction::FallbackCoerce
:
236 setVal(fallback(val
, genThisCls(), true));
239 case AnnotAction::CallableCheck
:
243 case AnnotAction::ObjectCheck
:
246 case AnnotAction::WarnClass
:
247 case AnnotAction::ConvertClass
:
248 assertx(val
->type() <= TCls
);
249 setVal(gen(env
, LdClsName
, val
));
250 if (result
== AnnotAction::WarnClass
) {
251 gen(env
, RaiseNotice
, cns(env
, s_CLASS_TO_STRING_IMPLICIT
.get()));
255 case AnnotAction::WarnLazyClass
:
256 case AnnotAction::ConvertLazyClass
:
257 assertx(val
->type() <= TLazyCls
);
258 setVal(gen(env
, LdLazyClsName
, val
));
259 if (result
== AnnotAction::WarnLazyClass
) {
260 gen(env
, RaiseNotice
, cns(env
, s_CLASS_TO_STRING_IMPLICIT
.get()));
264 case AnnotAction::WarnClassname
:
265 assertx(val
->type() <= TCls
|| val
->type() <= TLazyCls
);
266 gen(env
, RaiseNotice
, cns(env
, s_CLASS_TO_CLASSNAME
.get()));
269 assertx(result
== AnnotAction::ObjectCheck
);
270 assertx(val
->type() <= TObj
);
271 if (onlyCheckNullability
) return;
273 // At this point, we know that val is a TObj.
275 // For this type checks, the class needs to be an exact match.
276 auto const thisCls
= getThisCls();
277 auto const objClass
= gen(env
, LdObjClass
, val
);
281 gen(env
, JmpZero
, taken
, gen(env
, EqCls
, thisCls
, objClass
));
284 hint(env
, Block::Hint::Unlikely
);
285 genFail(val
, thisCls
);
291 // At this point, we know that val is a TObj and that tc is an Object.
292 assertx(tc
.isObject() || tc
.isUnresolved());
293 auto const clsName
= tc
.isObject() ? tc
.clsName() : tc
.typeName();
294 auto const checkCls
= ldClassSafe(env
, clsName
);
295 auto const fastIsInstance
= implInstanceCheck(env
, val
, clsName
, checkCls
);
296 if (fastIsInstance
) {
300 gen(env
, JmpZero
, taken
, fastIsInstance
);
303 hint(env
, Block::Hint::Unlikely
);
310 verifyCls(val
, checkCls
);
313 auto const genericVal
= getVal();
314 assertx(genericVal
->type() <= TCell
);
315 auto const genericValType
= genericVal
->type();
317 auto const computeAction
= [&](DataType dt
) {
318 if (dt
== KindOfNull
&& tc
.isNullable()) return AnnotAction::Pass
;
319 auto const name
= tc
.isObject() ? tc
.clsName() : tc
.typeName();
320 return annotCompat(dt
, tc
.type(), name
);
323 if (genericValType
.isKnownDataType()) {
324 auto const dt
= genericValType
.toDataType();
325 return checkOneType(genericVal
, computeAction(dt
));
328 enum { None
, Fail
, Fallback
, FallbackCoerce
} fallbackAction
= None
;
330 auto const options
= [&]{
331 TinyVector
<std::pair
<DataType
, AnnotAction
>, kNumDataTypes
> result
;
332 for (auto const dt
: kDataTypes
) {
333 auto const type
= Type(dt
);
334 if (!genericValType
.maybe(type
)) continue;
335 auto const action
= computeAction(dt
);
337 case AnnotAction::Fail
:
338 fallbackAction
= std::max(fallbackAction
, Fail
);
340 case AnnotAction::Fallback
:
341 fallbackAction
= std::max(fallbackAction
, Fallback
);
343 case AnnotAction::FallbackCoerce
:
344 fallbackAction
= std::max(fallbackAction
, FallbackCoerce
);
347 result
.emplace_back(dt
, action
);
354 if (fallbackAction
== None
&&
355 std::all_of(options
.begin(), options
.end(), [] (auto const& pair
) {
356 return pair
.second
== AnnotAction::Pass
;
361 // TODO(kshaunak): If we were a bit more sophisticated here, we could
362 // merge the cases for certain types, like TVec|TDict, or TArrLike.
364 for (auto const& pair
: options
) {
365 mc
.ifTypeThen(genericVal
, Type(pair
.first
), [&](SSATmp
* val
) {
366 checkOneType(val
, pair
.second
);
367 return cns(env
, TBottom
);
371 switch (fallbackAction
) {
373 gen(env
, Unreachable
, ASSERT_REASON
);
379 fallback(genericVal
, genThisCls(), false);
382 setVal(fallback(genericVal
, genThisCls(), true));
385 return cns(env
, TBottom
);
389 Type
typeOpToType(IsTypeOp op
) {
391 case IsTypeOp::Null
: return TInitNull
;
392 case IsTypeOp::Int
: return TInt
;
393 case IsTypeOp::Dbl
: return TDbl
;
394 case IsTypeOp::Bool
: return TBool
;
395 case IsTypeOp::Str
: return TStr
;
396 case IsTypeOp::Keyset
: return TKeyset
;
397 case IsTypeOp::Obj
: return TObj
;
398 case IsTypeOp::Res
: return TRes
;
399 case IsTypeOp::ClsMeth
:
401 case IsTypeOp::Class
:
404 case IsTypeOp::ArrLike
:
405 case IsTypeOp::LegacyArrLike
:
406 case IsTypeOp::Scalar
: not_reached();
411 SSATmp
* isScalarImpl(IRGS
& env
, SSATmp
* val
) {
412 // The simplifier works fine when val has a known DataType, but do some
413 // checks first in case val has a type like {Int|Str}.
414 auto const scalar
= TBool
| TInt
| TDbl
| TStr
| TCls
| TLazyCls
;
415 if (val
->isA(scalar
)) return cns(env
, true);
416 if (!val
->type().maybe(scalar
)) return cns(env
, false);
418 SSATmp
* result
= nullptr;
419 for (auto t
: {TBool
, TInt
, TDbl
, TStr
, TCls
, TLazyCls
}) {
420 auto const is_t
= gen(env
, ConvBoolToInt
, gen(env
, IsType
, t
, val
));
421 result
= result
? gen(env
, OrInt
, result
, is_t
) : is_t
;
423 return gen(env
, ConvIntToBool
, result
);
426 const StaticString
s_FUNC_CONVERSION(Strings::FUNC_TO_STRING
);
427 const StaticString
s_FUNC_IS_STRING("Func used in is_string");
428 const StaticString
s_CLASS_CONVERSION(Strings::CLASS_TO_STRING
);
429 const StaticString
s_CLASS_IS_STRING("Class used in is_string");
430 const StaticString
s_TYPE_STRUCT_NOT_DARR("Type-structure is not a darray");
432 SSATmp
* isStrImpl(IRGS
& env
, SSATmp
* src
) {
435 mc
.ifTypeThen(src
, TStr
, [&](SSATmp
*) { return cns(env
, true); });
437 mc
.ifTypeThen(src
, TLazyCls
, [&](SSATmp
*) {
438 if (RuntimeOption::EvalClassIsStringNotices
) {
439 gen(env
, RaiseNotice
, cns(env
, s_CLASS_IS_STRING
.get()));
441 return cns(env
, true);
444 mc
.ifTypeThen(src
, TCls
, [&](SSATmp
*) {
445 if (RuntimeOption::EvalClassIsStringNotices
) {
446 gen(env
, RaiseNotice
, cns(env
, s_CLASS_IS_STRING
.get()));
448 return cns(env
, true);
451 return mc
.elseDo([&]{ return cns(env
, false); });
454 SSATmp
* isClassImpl(IRGS
& env
, SSATmp
* src
) {
456 mc
.ifTypeThen(src
, TLazyCls
, [&](SSATmp
*) { return cns(env
, true); });
457 mc
.ifTypeThen(src
, TCls
, [&](SSATmp
*) { return cns(env
, true); });
458 return mc
.elseDo([&]{ return cns(env
, false); });
461 SSATmp
* isFuncImpl(IRGS
& env
, SSATmp
* src
) {
463 mc
.ifTypeThen(src
, TFunc
, [&](SSATmp
* func
) {
464 auto const attr
= AttrData
{ AttrIsMethCaller
};
465 auto const isMC
= gen(env
, FuncHasAttr
, attr
, func
);
466 return gen(env
, EqBool
, isMC
, cns(env
, false));
468 mc
.ifTypeThen(src
, TRFunc
, [&](SSATmp
*) { return cns(env
, true); });
469 return mc
.elseDo([&]{ return cns(env
, false); });
472 SSATmp
* isClsMethImpl(IRGS
& env
, SSATmp
* src
) {
474 mc
.ifTypeThen(src
, TClsMeth
, [&](SSATmp
*) { return cns(env
, true); });
475 mc
.ifTypeThen(src
, TRClsMeth
, [&](SSATmp
*) { return cns(env
, true); });
476 return mc
.elseDo([&]{ return cns(env
, false); });
479 SSATmp
* isVecImpl(IRGS
& env
, SSATmp
* src
) {
482 mc
.ifTypeThen(src
, TVec
, [&](SSATmp
* src
) {
483 return cns(env
, true);
486 return mc
.elseDo([&]{ return cns(env
, false); });
489 SSATmp
* isDictImpl(IRGS
& env
, SSATmp
* src
) {
492 mc
.ifTypeThen(src
, TDict
, [&](SSATmp
* src
) {
493 return cns(env
, true);
496 return mc
.elseDo([&]{ return cns(env
, false); });
499 SSATmp
* isArrLikeImpl(IRGS
& env
, SSATmp
* src
) {
501 return mc
.elseDo([&]{ return gen(env
, IsType
, TArrLike
, src
); });
504 SSATmp
* isLegacyArrLikeImpl(IRGS
& env
, SSATmp
* src
) {
507 mc
.ifTypeThen(src
, TVec
|TDict
, [&](SSATmp
* src
) {
508 return gen(env
, IsLegacyArrLike
, src
);
511 return mc
.elseDo([&]{ return cns(env
, false); });
514 //////////////////////////////////////////////////////////////////////
518 SSATmp
* implInstanceOfD(IRGS
& env
, SSATmp
* src
, const StringData
* className
) {
520 * InstanceOfD is always false if it's not an object.
522 * We're prepared to generate translations for known non-object types, but if
523 * it's Gen/Cell we're going to PUNT because it's natural to translate that
524 * case with control flow TODO(#16781576)
526 if (TObj
< src
->type()) {
527 PUNT(InstanceOfD_MaybeObj
);
529 if (!src
->isA(TObj
)) {
530 if (src
->isA(TCls
| TLazyCls
)) {
531 if (!interface_supports_string(className
)) return cns(env
, false);
532 if (RuntimeOption::EvalClassIsStringNotices
&& src
->isA(TCls
)) {
536 cns(env
, s_CLASS_IS_STRING
.get())
539 return cns(env
, true);
543 (src
->isA(TArrLike
) && interface_supports_arrlike(className
)) ||
544 (src
->isA(TStr
) && interface_supports_string(className
)) ||
545 (src
->isA(TInt
) && interface_supports_int(className
)) ||
546 (src
->isA(TDbl
) && interface_supports_double(className
));
547 return cns(env
, res
);
550 auto const checkCls
= ldClassSafe(env
, className
);
551 if (auto isInstance
= implInstanceCheck(env
, src
, className
, checkCls
)) {
555 return gen(env
, InstanceOf
, gen(env
, LdObjClass
, src
), checkCls
);
558 //////////////////////////////////////////////////////////////////////
560 void emitInstanceOfD(IRGS
& env
, const StringData
* className
) {
561 auto const src
= popC(env
);
562 push(env
, implInstanceOfD(env
, src
, className
));
563 decRef(env
, src
, DecRefProfileId::Default
);
566 void emitInstanceOf(IRGS
& env
) {
567 auto const t1
= popC(env
);
568 auto const t2
= popC(env
); // t2 instanceof t1
570 if (t1
->isA(TObj
) && t2
->isA(TObj
)) {
571 auto const c2
= gen(env
, LdObjClass
, t2
);
572 auto const c1
= gen(env
, LdObjClass
, t1
);
573 push(env
, gen(env
, InstanceOf
, c2
, c1
));
574 decRef(env
, t2
, DecRefProfileId::InstanceOfSrc2
);
575 decRef(env
, t1
, DecRefProfileId::InstanceOfSrc1
);
579 if (!t1
->isA(TStr
)) PUNT(InstanceOf
-NotStr
);
582 auto const c1
= gen(env
, LookupClsRDS
, t1
);
583 auto const c2
= gen(env
, LdObjClass
, t2
);
584 push(env
, gen(env
, InstanceOf
, c2
, c1
));
585 decRef(env
, t2
, DecRefProfileId::InstanceOfSrc2
);
586 decRef(env
, t1
, DecRefProfileId::InstanceOfSrc1
);
590 auto const res
= [&]() -> SSATmp
* {
591 if (t2
->isA(TArrLike
)) return gen(env
, InterfaceSupportsArrLike
, t1
);
592 if (t2
->isA(TInt
)) return gen(env
, InterfaceSupportsInt
, t1
);
593 if (t2
->isA(TStr
)) return gen(env
, InterfaceSupportsStr
, t1
);
594 if (t2
->isA(TDbl
)) return gen(env
, InterfaceSupportsDbl
, t1
);
596 if (!RO::EvalRaiseClassConversionWarning
) {
597 return gen(env
, InterfaceSupportsStr
, t1
);
602 gen(env
, JmpZero
, taken
, gen(env
, InterfaceSupportsStr
, t1
));
605 gen(env
, RaiseNotice
, cns(env
, s_CLASS_CONVERSION
.get()));
606 return cns(env
, true);
608 [&] { return cns(env
, false); }
611 if (!t2
->type().maybe(TObj
|TArrLike
|TInt
|TStr
|TDbl
)) return cns(env
, false);
615 if (!res
) PUNT(InstanceOf
-Unknown
);
618 decRef(env
, t2
, DecRefProfileId::InstanceOfSrc2
);
619 decRef(env
, t1
, DecRefProfileId::InstanceOfSrc1
);
622 void emitIsLateBoundCls(IRGS
& env
) {
623 auto const cls
= curClass(env
);
624 if (!cls
) PUNT(IsLateBoundCls
-NoClassContext
);
625 if (isTrait(cls
)) PUNT(IsLateBoundCls
-Trait
);
626 auto const obj
= popC(env
);
627 if (obj
->isA(TObj
)) {
628 auto const rhs
= ldCtxCls(env
);
629 auto const lhs
= gen(env
, LdObjClass
, obj
);
630 push(env
, gen(env
, InstanceOf
, lhs
, rhs
));
631 } else if (!obj
->type().maybe(TObj
)) {
632 push(env
, cns(env
, false));
634 PUNT(IsLateBoundCls
-MaybeObject
);
636 decRef(env
, obj
, DecRefProfileId::Default
);
642 SSATmp
* resolveTypeStructureAndCacheInRDS(
645 bool typeStructureCouldBeNonStatic
647 if (typeStructureCouldBeNonStatic
) return resolveTypeStruct();
648 auto const handle
= rds::alloc
<TypedValue
>().handle();
649 auto const data
= RDSHandleAndType
{ handle
, TDict
};
650 auto const addr
= gen(env
, LdRDSAddr
, data
, TPtrToOther
);
654 gen(env
, CheckRDSInitialized
, taken
, RDSHandleData
{ handle
});
657 hint(env
, Block::Hint::Unlikely
);
658 gen(env
, StMem
, addr
, resolveTypeStruct());
659 gen(env
, MarkRDSInitialized
, RDSHandleData
{ handle
});
662 return gen(env
, LdMem
, TDict
, addr
);
665 SSATmp
* resolveTypeStructImpl(
667 bool typeStructureCouldBeNonStatic
,
672 auto const declaringCls
= curFunc(env
) ? curClass(env
) : nullptr;
673 auto const calledCls
=
674 declaringCls
&& typeStructureCouldBeNonStatic
677 auto const result
= resolveTypeStructureAndCacheInRDS(
683 ResolveTypeStructData
{
686 spOffBCFromIRSP(env
),
687 static_cast<uint32_t>(n
),
694 typeStructureCouldBeNonStatic
701 const ArrayData
* staticallyResolveTypeStructure(
707 auto const declaringCls
= curFunc(env
) ? curClass(env
) : nullptr;
708 bool persistent
= false;
709 // This shouldn't do a difference, but does on GCC 8.3 on Ubuntu 19.04;
710 // if we take the catch then return `ts`, it's a bogus value and we
711 // segfault... sometimes...
712 const ArrayData
* ts_copy
= ts
;
714 auto newTS
= TypeStructure::resolvePartial(
715 ArrNR(ts
), nullptr, declaringCls
, persistent
, partial
, invalidType
);
716 if (persistent
) return ArrayData::GetScalarArray(std::move(newTS
));
717 } catch (Exception
& e
) {}
718 // We are here because either we threw in the resolution or it wasn't
719 // persistent resolution which means we didn't really resolve it
724 SSATmp
* check_nullable(IRGS
& env
, SSATmp
* res
, SSATmp
* var
) {
727 [&] (Block
* taken
) { gen(env
, JmpNZero
, taken
, res
); },
728 [&] { return gen(env
, IsType
, TNull
, var
); },
729 [&] { return cns(env
, true); }
733 void chain_is_type(IRGS
& env
, SSATmp
* c
, bool nullable
, Type ty
) {
734 always_assert(false);
737 template<typename
... Types
>
738 void chain_is_type(IRGS
& env
, SSATmp
* c
, bool nullable
,
739 Type ty1
, Type ty2
, Types
&&... rest
) {
743 auto const res
= gen(env
, IsType
, ty1
, c
);
744 gen(env
, JmpNZero
, taken
, res
);
747 if (sizeof...(rest
) == 0) {
748 auto const res
= gen(env
, IsType
, ty2
, c
);
749 push(env
, nullable
? check_nullable(env
, res
, c
) : res
);
751 chain_is_type(env
, c
, nullable
, ty2
, rest
...);
755 push(env
, cns(env
, true));
761 * This function tries to emit is type struct operations without resolving
762 * the type structure when that's possible.
763 * When it returns true, it has popped two values from the stack, namely the
764 * type structure and the cell, and pushed one value back to stack, namely
765 * true/false if it is an is-operation or the cell if it is an as operation.
766 * This function does not modify the reference counts of these stack values,
767 * leaving that responsibility to the caller.
768 * When it returns false, it does not modify anything.
770 bool emitIsTypeStructWithoutResolvingIfPossible(
774 // Top of the stack is the type structure, so the thing we are checking is
776 auto const t
= topC(env
, BCSPRelOffset
{ 1 });
777 auto const is_nullable_ts
= is_ts_nullable(ts
);
779 auto const cnsResult
= [&] (bool value
) {
780 popC(env
); // pop the ts that's on the stack
781 popC(env
); // pop the cell
782 push(env
, cns(env
, value
));
786 auto const success
= [&] { return cnsResult(true); };
787 auto const fail
= [&] { return cnsResult(false); };
789 auto const primitive
= [&] (Type ty
, bool should_negate
= false) {
790 auto const nty
= is_nullable_ts
? ty
|TNull
: ty
;
791 if (t
->isA(nty
)) return should_negate
? fail() : success();
792 if (!t
->type().maybe(nty
)) return should_negate
? success() : fail();
793 popC(env
); // pop the ts that's on the stack
794 auto const c
= popC(env
);
795 auto const res
= gen(env
, should_negate
? IsNType
: IsType
, ty
, c
);
796 push(env
, is_nullable_ts
? check_nullable(env
, res
, c
) : res
);
800 // We explicitly bind is_nullable_ts because failing to do so causes a
801 // spurious compiler error on some g++ versions.
802 auto const unionOf
= [&,is_nullable_ts
] (Type ty1
, Type ty2
,
804 auto const ty
= Type::unionAll(ty1
, ty2
, rest
...) |
805 (is_nullable_ts
? TNull
: TBottom
);
806 if (t
->isA(ty
)) return success();
807 if (!t
->type().maybe(ty
)) return fail();
809 popC(env
); // pop the ts that's on the stack
810 auto const c
= popC(env
);
811 chain_is_type(env
, c
, is_nullable_ts
, ty1
, ty2
, rest
...);
815 if (t
->isA(TNull
) && is_nullable_ts
) return success();
817 auto kind
= get_ts_kind(ts
);
819 case TypeStructure::Kind::T_int
: return primitive(TInt
);
820 case TypeStructure::Kind::T_bool
: return primitive(TBool
);
821 case TypeStructure::Kind::T_float
: return primitive(TDbl
);
822 case TypeStructure::Kind::T_string
: {
823 if (t
->type().maybe(TLazyCls
) &&
824 RuntimeOption::EvalClassIsStringNotices
) {
827 gen(env
, CheckType
, TLazyCls
, taken
, t
);
830 gen(env
, RaiseNotice
, cns(env
, s_CLASS_IS_STRING
.get()));
834 if (t
->type().maybe(TCls
) &&
835 RuntimeOption::EvalClassIsStringNotices
) {
838 gen(env
, CheckType
, TCls
, taken
, t
);
841 gen(env
, RaiseNotice
, cns(env
, s_CLASS_IS_STRING
.get()));
845 return unionOf(TStr
, TLazyCls
, TCls
);
847 case TypeStructure::Kind::T_null
: return primitive(TNull
);
848 case TypeStructure::Kind::T_void
: return primitive(TNull
);
849 case TypeStructure::Kind::T_keyset
: return primitive(TKeyset
);
850 case TypeStructure::Kind::T_nonnull
: return primitive(TNull
, true);
851 case TypeStructure::Kind::T_mixed
:
852 case TypeStructure::Kind::T_dynamic
:
854 case TypeStructure::Kind::T_num
: return unionOf(TInt
, TDbl
);
855 case TypeStructure::Kind::T_arraykey
: {
856 if (t
->type().maybe(TLazyCls
) &&
857 RuntimeOption::EvalClassIsStringNotices
) {
860 gen(env
, CheckType
, TLazyCls
, taken
, t
);
863 gen(env
, RaiseNotice
, cns(env
, s_CLASS_IS_STRING
.get()));
867 if (t
->type().maybe(TCls
) &&
868 RuntimeOption::EvalClassIsStringNotices
) {
871 gen(env
, CheckType
, TCls
, taken
, t
);
874 gen(env
, RaiseNotice
, cns(env
, s_CLASS_IS_STRING
.get()));
878 return unionOf(TInt
, TStr
, TLazyCls
, TCls
);
880 case TypeStructure::Kind::T_any_array
:
881 return unionOf(TVec
, TDict
, TKeyset
);
882 case TypeStructure::Kind::T_vec_or_dict
:
883 case TypeStructure::Kind::T_varray_or_darray
:
884 case TypeStructure::Kind::T_dict
:
885 case TypeStructure::Kind::T_vec
:
886 case TypeStructure::Kind::T_darray
:
887 case TypeStructure::Kind::T_varray
: {
888 popC(env
); // pop the ts that's on the stack
889 auto const c
= popC(env
);
890 auto const res
= [&]{
891 if (kind
== TypeStructure::Kind::T_dict
||
892 kind
== TypeStructure::Kind::T_darray
) {
893 return isDictImpl(env
, c
);
894 } else if (kind
== TypeStructure::Kind::T_vec
||
895 kind
== TypeStructure::Kind::T_varray
) {
896 return isVecImpl(env
, c
);
898 assertx(kind
== TypeStructure::Kind::T_vec_or_dict
||
899 kind
== TypeStructure::Kind::T_varray_or_darray
);
902 [&](Block
* taken
) { gen(env
, JmpZero
, taken
, isVecImpl(env
, c
)); },
903 [&] { return cns(env
, true); },
904 [&] { return isDictImpl(env
, c
); }
908 push(env
, is_nullable_ts
? check_nullable(env
, res
, c
) : res
);
911 case TypeStructure::Kind::T_class
:
912 case TypeStructure::Kind::T_interface
:
913 case TypeStructure::Kind::T_xhp
: {
914 auto const clsname
= get_ts_classname(ts
);
915 auto cls
= lookupUniqueClass(env
, clsname
);
916 if (ts
->exists(s_generic_types
) &&
917 ((classIsPersistentOrCtxParent(env
, cls
) &&
918 cls
->hasReifiedGenerics()) ||
919 !isTSAllWildcards(ts
))) {
920 // If it is a reified class or has non wildcard generics,
924 popC(env
); // pop the ts that's on the stack
925 auto const c
= popC(env
);
926 auto const res
= implInstanceOfD(env
, c
, clsname
);
927 push(env
, is_nullable_ts
? check_nullable(env
, res
, c
) : res
);
930 case TypeStructure::Kind::T_nothing
:
931 case TypeStructure::Kind::T_noreturn
:
933 case TypeStructure::Kind::T_typevar
:
934 case TypeStructure::Kind::T_fun
:
935 case TypeStructure::Kind::T_trait
:
936 // Not supported, will throw an error on these at the resolution phase
938 case TypeStructure::Kind::T_enum
:
939 case TypeStructure::Kind::T_tuple
:
940 case TypeStructure::Kind::T_shape
:
941 case TypeStructure::Kind::T_typeaccess
:
942 case TypeStructure::Kind::T_unresolved
:
943 case TypeStructure::Kind::T_resource
:
944 case TypeStructure::Kind::T_reifiedtype
:
945 // TODO(T28423611): Implement these
952 * shouldDefRef is set iff the resulting SSATmp is a newly allocated type
954 * This function does not modify the reference count of its inputs, leaving that
957 SSATmp
* handleIsResolutionAndCommonOpts(
959 TypeStructResolveOp op
,
964 auto const a
= topC(env
);
965 if (!a
->isA(TDict
)) PUNT(IsTypeStructC
-NotArrayTypeStruct
);
966 if (!a
->hasConstVal(TDict
)) {
967 if (op
== TypeStructResolveOp::Resolve
) {
968 return resolveTypeStructImpl(env
, true, true, 1, true);
970 shouldDecRef
= false;
974 auto const ts
= a
->arrLikeVal();
975 auto maybe_resolved
= ts
;
977 bool invalidType
= true;
978 if (op
== TypeStructResolveOp::Resolve
) {
980 staticallyResolveTypeStructure(env
, ts
, partial
, invalidType
);
981 shouldDecRef
= maybe_resolved
!= ts
;
983 if (emitIsTypeStructWithoutResolvingIfPossible(env
, maybe_resolved
)) {
987 if (op
== TypeStructResolveOp::Resolve
&& (partial
|| invalidType
)) {
989 return resolveTypeStructImpl(
990 env
, typeStructureCouldBeNonStatic(ts
), true, 1, true);
993 if (op
== TypeStructResolveOp::DontResolve
) checkValid
= true;
994 return cns(env
, maybe_resolved
);
999 void emitIsTypeStructC(IRGS
& env
, TypeStructResolveOp op
) {
1000 auto const a
= topC(env
);
1001 auto const c
= topC(env
, BCSPRelOffset
{ 1 });
1002 bool done
= false, shouldDecRef
= true, checkValid
= false;
1004 handleIsResolutionAndCommonOpts(env
, op
, done
, shouldDecRef
, checkValid
);
1006 decRef(env
, c
, DecRefProfileId::IsTypeStructCc
);
1007 decRef(env
, a
, DecRefProfileId::IsTypeStructCa
);
1011 auto block
= opcodeMayRaise(IsTypeStruct
) && shouldDecRef
1012 ? create_catch_block(env
, [&]{
1013 decRef(env
, tc
, DecRefProfileId::IsTypeStructCTc
);
1016 auto const data
= RDSHandleData
{ rds::bindTSCache(curFunc(env
)).handle() };
1018 static const StaticString s_IsTypeStruct
{"IsTypeStruct"};
1019 auto const profile
= TargetProfile
<IsTypeStructProfile
> {
1021 env
.irb
->curMarker(),
1022 s_IsTypeStruct
.get()
1025 auto const generic
= [&] {
1026 if (checkValid
) gen(env
, RaiseErrorOnInvalidIsAsExpressionType
, tc
);
1027 return gen(env
, IsTypeStruct
, block
, data
, tc
, c
);
1030 auto const finish
= [&] (SSATmp
* result
) {
1032 decRef(env
, c
, DecRefProfileId::IsTypeStructCc
);
1033 decRef(env
, a
, DecRefProfileId::IsTypeStructCa
);
1036 if (profile
.profiling()) {
1037 gen(env
, ProfileIsTypeStruct
, RDSHandleData
{ profile
.handle() }, a
);
1042 if (!profile
.optimizing() || !profile
.data().shouldOptimize()) {
1049 [&] (Block
* taken
) {
1050 return gen(env
, IsTypeStructCached
, taken
, a
, c
);
1052 [&] (SSATmp
* result
) { // next
1056 hint(env
, Block::Hint::Unlikely
);
1062 void emitThrowAsTypeStructException(IRGS
& env
) {
1063 auto const arr
= topC(env
);
1064 auto const c
= topC(env
, BCSPRelOffset
{ 1 });
1065 auto const tsAndBlock
= [&]() -> std::pair
<SSATmp
*, Block
*> {
1066 if (arr
->hasConstVal(TDict
)) {
1067 auto const ts
= arr
->arrLikeVal();
1068 auto maybe_resolved
= ts
;
1069 bool partial
= true, invalidType
= true;
1071 staticallyResolveTypeStructure(env
, ts
, partial
, invalidType
);
1072 if (!ts
->same(maybe_resolved
)) {
1073 auto const inputTS
= cns(env
, maybe_resolved
);
1074 return {inputTS
, create_catch_block(
1075 env
, [&]{ decRef(env
, inputTS
, DecRefProfileId::Default
); })};
1078 auto const ts
= resolveTypeStructImpl(env
, true, false, 1, true);
1079 return {ts
, nullptr};
1081 // No need to decref inputs as this instruction will throw
1082 gen(env
, ThrowAsTypeStructException
, tsAndBlock
.second
, tsAndBlock
.first
, c
);
1085 void emitRecordReifiedGeneric(IRGS
& env
) {
1086 auto const ts
= popC(env
);
1087 if (!ts
->isA(TVec
)) {
1088 PUNT(RecordReifiedGeneric
-InvalidTS
);
1090 // RecordReifiedGenericsAndGetTSList decrefs the ts
1091 auto const result
= gen(env
, RecordReifiedGenericsAndGetTSList
, ts
);
1095 void emitCombineAndResolveTypeStruct(IRGS
& env
, uint32_t n
) {
1096 push(env
, resolveTypeStructImpl(env
, true, false, n
, false));
1099 void raiseClsmethCompatTypeHint(
1100 IRGS
& env
, int32_t id
, const Func
* func
, const TypeConstraint
& tc
) {
1101 auto name
= tc
.displayName(func
->cls());
1102 if (id
== TypeConstraint::ReturnId
) {
1103 gen(env
, RaiseNotice
, cns(env
, makeStaticString(
1104 folly::sformat("class_meth Compat: Value returned from function {}() "
1105 "must be of type {}, clsmeth given",
1106 func
->fullName(), name
))));
1108 gen(env
, RaiseNotice
, cns(env
, makeStaticString(
1109 folly::sformat("class_meth Compat: Argument {} passed to {}() "
1110 "must be of type {}, clsmeth given",
1111 id
+ 1, func
->fullName(), name
))));
1117 void verifyRetTypeImpl(IRGS
& env
, int32_t id
, int32_t ind
,
1118 bool onlyCheckNullability
) {
1119 auto const func
= curFunc(env
);
1120 auto const verifyFunc
= [&] (const TypeConstraint
& tc
) {
1124 onlyCheckNullability
,
1125 [&] { // Get value to test
1126 return topC(env
, BCSPRelOffset
{ ind
});
1128 [&] { // Get the class representing `this' type
1129 return ldCtxCls(env
);
1131 [&] (SSATmp
* updated
) { // Set the potentially coerced value
1132 auto const offset
= offsetFromIRSP(env
, BCSPRelOffset
{ ind
});
1133 gen(env
, StStk
, IRSPRelOffsetData
{offset
}, sp(env
), updated
);
1134 env
.irb
->exceptionStackBoundary();
1136 [&] (SSATmp
* val
, SSATmp
* thisCls
, bool hard
) { // Check failure
1138 env
.irb
->exceptionStackBoundary();
1141 hard
? VerifyRetFailHard
: VerifyRetFail
,
1142 FuncParamWithTCData
{ func
, id
, &tc
},
1147 [&] (SSATmp
* val
) { // Callable check
1151 FuncParamData
{ func
, id
},
1155 [&] (SSATmp
* val
, SSATmp
* checkCls
) {
1156 // Class/type-alias check
1160 FuncParamWithTCData
{ func
, id
, &tc
},
1162 gen(env
, LdObjClass
, val
),
1166 [&] (SSATmp
* val
, SSATmp
* thisCls
, bool mayCoerce
) { // Fallback
1169 mayCoerce
? VerifyRetCoerce
: VerifyRet
,
1170 FuncParamWithTCData
{ func
, id
, &tc
},
1177 auto const& tc
= (id
== TypeConstraint::ReturnId
)
1178 ? func
->returnTypeConstraint()
1179 : func
->params()[id
].typeConstraint
;
1182 if (id
== TypeConstraint::ReturnId
&& func
->hasReturnWithMultiUBs()) {
1183 auto& ubs
= const_cast<Func::UpperBoundVec
&>(func
->returnUBs());
1184 for (auto& ub
: ubs
) {
1185 applyFlagsToUB(ub
, tc
);
1188 } else if (func
->hasParamsWithMultiUBs()) {
1189 auto& ubs
= const_cast<Func::ParamUBMap
&>(func
->paramUBs());
1190 auto it
= ubs
.find(id
);
1191 if (it
!= ubs
.end()) {
1192 for (auto& ub
: it
->second
) {
1193 applyFlagsToUB(ub
, tc
);
1200 void verifyParamTypeImpl(IRGS
& env
, int32_t id
) {
1201 auto const func
= curFunc(env
);
1202 auto const verifyFunc
= [&](const TypeConstraint
& tc
) {
1207 [&] { // Get value to test
1208 return ldLoc(env
, id
, DataTypeSpecific
);
1210 [&] { // Get the class representing `this' type
1211 return ldCtxCls(env
);
1213 [&] (SSATmp
* updated
) { // Set the potentially coerced value
1214 stLocRaw(env
, id
, fp(env
), updated
);
1216 [&] (SSATmp
* val
, SSATmp
* thisCls
, bool hard
) { // Check failure
1219 hard
? VerifyParamFailHard
: VerifyParamFail
,
1220 FuncParamWithTCData
{ func
, id
, &tc
},
1225 [&] (SSATmp
* val
) { // Callable check
1228 VerifyParamCallable
,
1229 FuncParamData
{ func
, id
},
1233 [&] (SSATmp
* val
, SSATmp
* checkCls
) {
1234 // Class/type-alias check
1238 FuncParamWithTCData
{ func
, id
, &tc
},
1240 gen(env
, LdObjClass
, val
),
1244 [&] (SSATmp
* val
, SSATmp
* thisCls
, bool mayCoerce
) { // Fallback
1247 mayCoerce
? VerifyParamCoerce
: VerifyParam
,
1248 FuncParamWithTCData
{ func
, id
, &tc
},
1255 auto const& tc
= func
->params()[id
].typeConstraint
;
1257 if (func
->hasParamsWithMultiUBs()) {
1258 auto& ubs
= const_cast<Func::ParamUBMap
&>(func
->paramUBs());
1259 auto it
= ubs
.find(id
);
1260 if (it
!= ubs
.end()) {
1261 for (auto& ub
: it
->second
) {
1262 applyFlagsToUB(ub
, tc
);
1271 void verifyPropType(IRGS
& env
,
1273 const HPHP::TypeConstraint
* tc
,
1274 const Class::UpperBoundVec
* ubs
,
1279 SSATmp
** coerce
/* = nullptr */) {
1280 assertx(cls
->isA(TCls
));
1281 assertx(val
->isA(TCell
));
1283 if (coerce
) *coerce
= val
;
1284 if (RuntimeOption::EvalCheckPropTypeHints
<= 0) return;
1286 auto const verifyFunc
= [&](const TypeConstraint
* tc
) {
1287 if (!tc
|| !tc
->isCheckable()) return;
1288 assertx(tc
->validForProp());
1290 auto const fallback
= [&](SSATmp
* val
, SSATmp
*, bool mayCoerce
) {
1293 mayCoerce
? VerifyPropCoerce
: VerifyProp
,
1294 TypeConstraintData
{ tc
},
1302 // For non-DataTypeSpecific values, verifyTypeImpl handles the different
1303 // cases separately. However, our callers want a single coerced value,
1304 // which we don't track, so we punt if we're going to split it up.
1305 if (!val
->type().isKnownDataType()) {
1306 auto const updated
= fallback(val
, nullptr, tc
->mayCoerce());
1307 if (coerce
&& tc
->mayCoerce()) *coerce
= updated
;
1315 [&] { // Get value to check
1316 env
.irb
->constrainValue(val
, DataTypeSpecific
);
1319 [&] { // Get the class representing `this' type
1322 [&] (SSATmp
* updated
) { // Set the potentially coerced value
1323 if (coerce
) *coerce
= updated
;
1325 [&] (SSATmp
* val
, SSATmp
*, bool hard
) { // Check failure
1326 auto const failHard
=
1327 hard
&& RuntimeOption::EvalCheckPropTypeHints
>= 3 &&
1328 (!tc
->isUpperBound() || RuntimeOption::EvalEnforceGenericsUB
>= 2);
1331 failHard
? VerifyPropFailHard
: VerifyPropFail
,
1332 TypeConstraintData
{ tc
},
1339 // We don't allow callable as a property type-hint, so we should never
1340 // need to check callability.
1341 [&] (SSATmp
*) { always_assert(false); },
1342 [&] (SSATmp
* val
, SSATmp
* checkCls
) { // Class/type-alias check
1346 TypeConstraintData
{ tc
},
1358 if (RuntimeOption::EvalEnforceGenericsUB
> 0) {
1359 for (auto const& ub
: *ubs
) {
1365 void verifyMysteryBoxConstraint(IRGS
& env
, const MysteryBoxConstraint
& c
,
1366 SSATmp
* val
, Block
* fail
) {
1367 auto const genFail
= [&] {
1368 gen(env
, Jmp
, fail
);
1370 UNUSED
auto const& valType
= val
->type();
1372 FTRACE_MOD(Trace::sib
, 3, "Verifying constraint {} {}\n", valType
.toString(),
1373 c
.tc
.displayName());
1379 [&] { // Get value to test
1382 [&] { // Get the class representing `this' type
1383 if (c
.propDecl
) return cns(env
, c
.propDecl
);
1384 if (c
.ctx
) return cns(env
, c
.ctx
);
1385 return cns(env
, nullptr);
1387 [&] (SSATmp
*) { // Set the potentially coerced value
1389 [&] (SSATmp
*, SSATmp
*, bool) { // Check failure
1392 [&] (SSATmp
*) { // Callable check
1395 [&] (SSATmp
*, SSATmp
*) {
1398 [&] (SSATmp
*, SSATmp
*, bool) { // Fallback
1405 void emitVerifyRetTypeC(IRGS
& env
) {
1406 verifyRetTypeImpl(env
, TypeConstraint::ReturnId
, 0, false);
1409 void emitVerifyRetTypeTS(IRGS
& env
) {
1410 verifyRetTypeImpl(env
, TypeConstraint::ReturnId
, 1, false);
1411 auto const ts
= popC(env
);
1412 auto const cell
= topC(env
);
1413 auto const reified
= tcCouldBeReified(curFunc(env
), TypeConstraint::ReturnId
);
1414 if (reified
|| cell
->isA(TObj
)) {
1415 auto const funcData
= FuncData
{ curFunc(env
) };
1416 gen(env
, VerifyReifiedReturnType
, funcData
, cell
, ts
, ldCtxCls(env
));
1417 } else if (cell
->type().maybe(TObj
) && !reified
) {
1418 // Meaning we did not not guard on the stack input correctly
1419 PUNT(VerifyRetTypeTS
-UnguardedObj
);
1423 void emitVerifyRetNonNullC(IRGS
& env
) {
1424 auto const func
= curFunc(env
);
1425 auto const& tc
= func
->returnTypeConstraint();
1426 always_assert(!tc
.isNullable());
1427 verifyRetTypeImpl(env
, TypeConstraint::ReturnId
, 0, true);
1430 void emitVerifyOutType(IRGS
& env
, int32_t paramId
) {
1431 verifyRetTypeImpl(env
, paramId
, 0, false);
1434 void emitVerifyParamType(IRGS
& env
, int32_t paramId
) {
1435 verifyParamTypeImpl(env
, paramId
);
1438 void emitVerifyParamTypeTS(IRGS
& env
, int32_t paramId
) {
1439 verifyParamTypeImpl(env
, paramId
);
1440 auto const ts
= popC(env
);
1441 auto const cell
= ldLoc(env
, paramId
, DataTypeSpecific
);
1442 auto const reified
= tcCouldBeReified(curFunc(env
), paramId
);
1443 if (cell
->isA(TObj
) || reified
) {
1446 [&] (Block
* taken
) {
1447 return gen(env
, CheckType
, TDict
, taken
, ts
);
1450 auto const fpData
= FuncParamData
{ curFunc(env
), paramId
};
1451 gen(env
, VerifyReifiedLocalType
, fpData
, cell
, dts
, ldCtxCls(env
));
1455 gen(env
, RaiseError
, cns(env
, s_TYPE_STRUCT_NOT_DARR
.get()));
1459 } else if (cell
->type().maybe(TObj
)) {
1460 // Meaning we did not not guard on the stack input correctly
1461 PUNT(VerifyReifiedLocalType
-UnguardedObj
);
1465 void emitOODeclExists(IRGS
& env
, OODeclExistsOp subop
) {
1466 auto const tAutoload
= topC(env
);
1467 auto const tCls
= topC(env
, BCSPRelOffset
{1});
1469 if (!tCls
->isA(TStr
) || !tAutoload
->isA(TBool
)){ // result of Cast
1470 PUNT(OODeclExists
-BadTypes
);
1475 case OODeclExistsOp::Class
: kind
= ClassKind::Class
; break;
1476 case OODeclExistsOp::Trait
: kind
= ClassKind::Trait
; break;
1477 case OODeclExistsOp::Interface
: kind
= ClassKind::Interface
; break;
1480 auto const val
= gen(
1483 ClassKindData
{ kind
},
1489 decRef(env
, tCls
, DecRefProfileId::Default
);
1492 void emitIssetL(IRGS
& env
, int32_t id
) {
1493 auto const ld
= ldLoc(env
, id
, DataTypeSpecific
);
1494 push(env
, gen(env
, IsNType
, TNull
, ld
));
1497 void emitIsUnsetL(IRGS
& env
, int32_t id
) {
1498 auto const ld
= ldLoc(env
, id
, DataTypeSpecific
);
1499 push(env
, gen(env
, IsType
, TUninit
, ld
));
1502 SSATmp
* isTypeHelper(IRGS
& env
, IsTypeOp subop
, SSATmp
* val
) {
1504 case IsTypeOp::Vec
: return isVecImpl(env
, val
);
1505 case IsTypeOp::Dict
: return isDictImpl(env
, val
);
1506 case IsTypeOp::Scalar
: return isScalarImpl(env
, val
);
1507 case IsTypeOp::Str
: return isStrImpl(env
, val
);
1508 case IsTypeOp::ArrLike
: return isArrLikeImpl(env
, val
);
1509 case IsTypeOp::LegacyArrLike
: return isLegacyArrLikeImpl(env
, val
);
1510 case IsTypeOp::Class
: return isClassImpl(env
, val
);
1511 case IsTypeOp::Func
: return isFuncImpl(env
, val
);
1512 case IsTypeOp::ClsMeth
: return isClsMethImpl(env
, val
);
1516 auto const t
= typeOpToType(subop
);
1517 return t
<= TObj
? optimizedCallIsObject(env
, val
) : gen(env
, IsType
, t
, val
);
1520 void emitIsTypeC(IRGS
& env
, IsTypeOp subop
) {
1521 auto const val
= popC(env
, DataTypeSpecific
);
1522 push(env
, isTypeHelper(env
, subop
, val
));
1523 decRef(env
, val
, DecRefProfileId::Default
);
1526 void emitIsTypeL(IRGS
& env
, NamedLocal loc
, IsTypeOp subop
) {
1527 auto const val
= ldLocWarn(env
, loc
, DataTypeSpecific
);
1528 push(env
, isTypeHelper(env
, subop
, val
));
1531 //////////////////////////////////////////////////////////////////////
1533 void emitAssertRATL(IRGS
& env
, int32_t loc
, RepoAuthType rat
) {
1534 assertTypeLocal(env
, loc
, typeFromRAT(rat
, curClass(env
)));
1537 void emitAssertRATStk(IRGS
& env
, uint32_t offset
, RepoAuthType rat
) {
1540 BCSPRelOffset
{safe_cast
<int32_t>(offset
)},
1541 typeFromRAT(rat
, curClass(env
))
1545 //////////////////////////////////////////////////////////////////////