Skip to content

Commit

Permalink
Add support for "HH\object" type constraint
Browse files Browse the repository at this point in the history
Summary:
Some builtins, such as spl_object_hash(), accept arbitrary objects.

Expose ability to express that with a "HH\object" typehint that gets mapped to
a newly introduced AnnotType::Object, which uses the AnnotMetaType::Precise.

The new typehint is not yet used anywhere except of the added unit test, as
"object" typehints are not automatically imported into the HH namespace.

Reviewed By: mdko

Differential Revision: D52018664

fbshipit-source-id: 4ce1a2be65967f223bf6a088c49bad56cc102894
  • Loading branch information
jano authored and facebook-github-bot committed Dec 12, 2023
1 parent b0a378e commit 93db8b6
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 12 deletions.
1 change: 1 addition & 0 deletions hphp/hhbbc/type-system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4379,6 +4379,7 @@ Type from_hni_constraint(SString s) {
}

if (!strcasecmp(p, annotTypeName(AnnotType::Null))) return opt(std::move(ret));
if (!strcasecmp(p, annotTypeName(AnnotType::Object))) return union_of(std::move(ret), TObj);
if (!strcasecmp(p, annotTypeName(AnnotType::Resource))) return union_of(std::move(ret), TRes);
if (!strcasecmp(p, annotTypeName(AnnotType::Bool))) return union_of(std::move(ret), TBool);
if (!strcasecmp(p, annotTypeName(AnnotType::Int))) return union_of(std::move(ret), TInt);
Expand Down
11 changes: 7 additions & 4 deletions hphp/runtime/base/annot-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ static const std::pair<HhvmStrToTypeMap, StdStrToTypeMap>& getAnnotTypeMaps() {
{ annotTypeName(AnnotType::Int), AnnotType::Int },
{ annotTypeName(AnnotType::Float), AnnotType::Float },
{ annotTypeName(AnnotType::String), AnnotType::String },
{ annotTypeName(AnnotType::Object), AnnotType::Object },
{ annotTypeName(AnnotType::Resource), AnnotType::Resource },
{ annotTypeName(AnnotType::Mixed), AnnotType::Mixed },
{ annotTypeName(AnnotType::Nonnull), AnnotType::Nonnull },
Expand Down Expand Up @@ -158,6 +159,7 @@ TypedValue annotDefaultValue(AnnotType at) {
case AnnotType::This:
case AnnotType::Callable:
case AnnotType::Resource:
case AnnotType::Object:
case AnnotType::SubObject:
case AnnotType::Unresolved:
case AnnotType::Nothing:
Expand Down Expand Up @@ -257,9 +259,9 @@ annotCompat(DataType dt, AnnotType at, const StringData* annotClsName) {
}

if (metatype == AnnotMetaType::Precise) {
// If `at' is "bool", "int", "float", "string", "array", or "resource",
// then equivDataTypes() can definitively tell us whether or not `dt'
// is compatible.
// If `at' is "bool", "int", "float", "string", "vec", "dict", "keyset",
// "object", or "resource", then equivDataTypes() can definitively tell us
// whether or not `dt' is compatible.
return equivDataTypes(getAnnotDataType(at), dt)
? AnnotAction::Pass : AnnotAction::Fail;
}
Expand Down Expand Up @@ -333,7 +335,8 @@ const char* annotName(AnnotType at) {
case AnnotType::This: return "this";
case AnnotType::Callable: return "callable";
case AnnotType::Resource: return "resource";
case AnnotType::SubObject: return "object";
case AnnotType::Object: return "object";
case AnnotType::SubObject: return "subobject";
case AnnotType::Unresolved: return "unresolved";
case AnnotType::Nothing: return "nothing";
case AnnotType::NoReturn: return "noreturn";
Expand Down
2 changes: 2 additions & 0 deletions hphp/runtime/base/annot-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ enum class AnnotType : uint16_t {
Int = (uint8_t)KindOfInt64 | (uint16_t)AnnotMetaType::Precise << 8,
Float = (uint8_t)KindOfDouble | (uint16_t)AnnotMetaType::Precise << 8,
String = (uint8_t)KindOfString | (uint16_t)AnnotMetaType::Precise << 8,
Object = (uint8_t)KindOfObject | (uint16_t)AnnotMetaType::Precise << 8,
Resource = (uint8_t)KindOfResource | (uint16_t)AnnotMetaType::Precise << 8,
Dict = (uint8_t)KindOfDict | (uint16_t)AnnotMetaType::Precise << 8,
Vec = (uint8_t)KindOfVec | (uint16_t)AnnotMetaType::Precise << 8,
Expand Down Expand Up @@ -98,6 +99,7 @@ constexpr const char* annotNullableTypeName(AnnotType ty) {
case AnnotType::Nothing: return "?HH\\nothing";
case AnnotType::Null: return "?HH\\null";
case AnnotType::Number: return "?HH\\num";
case AnnotType::Object: return "?HH\\object";
case AnnotType::Resource: return "?HH\\resource";
case AnnotType::String: return "?HH\\string";
case AnnotType::SubObject: not_implemented();
Expand Down
1 change: 1 addition & 0 deletions hphp/runtime/vm/jit/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,7 @@ Type typeFromTCImpl(const HPHP::TypeConstraint& tc,
case A::Float: return TDbl;
case A::String: return TStr;
case A::Mixed: return TCell;
case A::Object: return TObj;
case A::Resource: return TRes;
case A::Dict: return TDict;
case A::Vec: return TVec;
Expand Down
11 changes: 11 additions & 0 deletions hphp/runtime/vm/type-constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ Optional<TypeConstraint> TypeConstraint::UnionBuilder::recordConstraint(const Ty
m_preciseTypeMask |= kUnionTypeString;
break;
}
case AnnotType::Object: {
m_preciseTypeMask |= kUnionTypeObject;
break;
}
case AnnotType::Resource: {
m_preciseTypeMask |= kUnionTypeResource;
break;
Expand Down Expand Up @@ -679,6 +683,7 @@ std::string TypeConstraint::displayName(const Class* context /*= nullptr*/,
case AnnotType::Int: str = "int"; break;
case AnnotType::Float: str = "float"; break;
case AnnotType::String: str = "string"; break;
case AnnotType::Object: str = "object"; break;
case AnnotType::Resource: str = "resource"; break;
case AnnotType::Dict: str = "dict"; break;
case AnnotType::Vec: str = "vec"; break;
Expand Down Expand Up @@ -726,6 +731,7 @@ std::string showUnionTypeMask(UnionTypeMask mask) {
bitName(res, mask, TypeConstraint::kUnionTypeFloat, "float");
bitName(res, mask, TypeConstraint::kUnionTypeInt, "int");
bitName(res, mask, TypeConstraint::kUnionTypeKeyset, "keyset");
bitName(res, mask, TypeConstraint::kUnionTypeObject, "object");
bitName(res, mask, TypeConstraint::kUnionTypeResource, "resource");
bitName(res, mask, TypeConstraint::kUnionTypeString, "string");
bitName(res, mask, TypeConstraint::kUnionTypeThis, "this");
Expand Down Expand Up @@ -763,6 +769,7 @@ std::string show(AnnotType t) {
case AnnotType::Float: return "Float";
case AnnotType::String: return "String";
case AnnotType::SubObject: return "SubObject";
case AnnotType::Object: return "Object";
case AnnotType::Resource: return "Resource";
case AnnotType::Dict: return "Dict";
case AnnotType::Vec: return "Vec";
Expand Down Expand Up @@ -2007,6 +2014,10 @@ void TcUnionPieceIterator::buildUnionTypeConstraint() {
m_outTc = TypeConstraint{ AnnotType::Keyset, flags, CC{LAZY_STATIC_STRING(annotTypeName(AnnotType::Keyset))} };
break;
}
case TypeConstraint::kUnionTypeObject: {
m_outTc = TypeConstraint{ AnnotType::Object, flags, CC{LAZY_STATIC_STRING(annotTypeName(AnnotType::Object))} };
break;
}
case TypeConstraint::kUnionTypeResource: {
m_outTc = TypeConstraint{ AnnotType::Resource, flags, CC{LAZY_STATIC_STRING(annotTypeName(AnnotType::Resource))} };
break;
Expand Down
18 changes: 10 additions & 8 deletions hphp/runtime/vm/type-constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,14 @@ struct TypeConstraint {
constexpr static UnionTypeMask kUnionTypeInt = 1 << 1;
constexpr static UnionTypeMask kUnionTypeFloat = 1 << 2;
constexpr static UnionTypeMask kUnionTypeCallable = 1 << 3;
constexpr static UnionTypeMask kUnionTypeResource = 1 << 4;
constexpr static UnionTypeMask kUnionTypeThis = 1 << 5;
constexpr static UnionTypeMask kUnionTypeString = 1 << 6;
constexpr static UnionTypeMask kUnionTypeVec = 1 << 7;
constexpr static UnionTypeMask kUnionTypeKeyset = 1 << 8;
constexpr static UnionTypeMask kUnionTypeDict = 1 << 9;
constexpr static UnionTypeMask kUnionTypeClassname = 1 << 10;
constexpr static UnionTypeMask kUnionTypeObject = 1 << 4;
constexpr static UnionTypeMask kUnionTypeResource = 1 << 5;
constexpr static UnionTypeMask kUnionTypeThis = 1 << 6;
constexpr static UnionTypeMask kUnionTypeString = 1 << 7;
constexpr static UnionTypeMask kUnionTypeVec = 1 << 8;
constexpr static UnionTypeMask kUnionTypeKeyset = 1 << 9;
constexpr static UnionTypeMask kUnionTypeDict = 1 << 10;
constexpr static UnionTypeMask kUnionTypeClassname = 1 << 11;
// Class should be the last flag because it indicates a list of classnames in
// the repr which we want to handle last and repeat until we're out of
// classes.
Expand Down Expand Up @@ -210,6 +211,7 @@ struct TypeConstraint {
case AnnotType::Int:
case AnnotType::Float:
case AnnotType::String:
case AnnotType::Object:
case AnnotType::Resource:
case AnnotType::Dict:
case AnnotType::Vec:
Expand Down Expand Up @@ -342,7 +344,7 @@ struct TypeConstraint {
bool isDict() const { return !isUnion() && m_u.single.type == Type::Dict; }
bool isVec() const { return !isUnion() && m_u.single.type == Type::Vec; }
bool isKeyset() const { return !isUnion() && m_u.single.type == Type::Keyset; }
bool isAnyObject() const { return false; }
bool isAnyObject() const { return !isUnion() && m_u.single.type == Type::Object; }
bool isSubObject() const { return !isUnion() && m_u.single.type == Type::SubObject; }
bool isInt() const { return !isUnion() && m_u.single.type == Type::Int; }
bool isString() const { return !isUnion() && m_u.single.type == Type::String; }
Expand Down
11 changes: 11 additions & 0 deletions hphp/test/slow/object/param-type-hh-object.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?hh

function test(HH\object $obj) {
var_dump(get_class($obj));
}

<<__EntryPoint>>
function main() {
test(new stdClass());
test("invalid");
}
3 changes: 3 additions & 0 deletions hphp/test/slow/object/param-type-hh-object.php.expectf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
string(8) "stdClass"

Catchable fatal error: Argument 1 passed to test() must be an instance of HH\object, string given in %s on line 10

0 comments on commit 93db8b6

Please sign in to comment.