Permalink
Browse files

Add arraylike typehint

Summary:
Union typehint for array|varray|darray|vec|dict|keyset.

We intend to support arraylike only for builtins, and thus will not add
it to the Hack typechecker.

Reviewed By: ricklavoie

Differential Revision: D8627257

fbshipit-source-id: 129ef901767217c9cefa4e6a443b8047262c5847
  • Loading branch information...
mxw authored and hhvm-bot committed Jun 27, 2018
1 parent ba3e2f7 commit 1f09dd2aa09d2670a481c5ee873f0f37dcada92f
@@ -207,6 +207,7 @@ void ParameterExpression::compatibleDefault(FileScopeRawPtr file) {
strcasecmp(hint, "HH\\darray") &&
strcasecmp(hint, "HH\\varray_or_darray") &&
strcasecmp(hint, "HH\\vec_or_dict") &&
strcasecmp(hint, "HH\\arraylike") &&
strcasecmp(hint, "array"));
// Normally a named type like 'int' is compatible with Int but not integer
@@ -254,6 +255,7 @@ void ParameterExpression::compatibleDefault(FileScopeRawPtr file) {
compat = (acceptAny ||
!strcasecmp(hint, "HH\\vec") ||
!strcasecmp(hint, "HH\\vec_or_dict") ||
!strcasecmp(hint, "HH\\arraylike") ||
(RuntimeOption::EvalHackArrDVArrs &&
(!strcasecmp(hint, "HH\\varray") ||
!strcasecmp(hint, "HH\\varray_or_darray")))
@@ -265,6 +267,7 @@ void ParameterExpression::compatibleDefault(FileScopeRawPtr file) {
compat = (acceptAny ||
!strcasecmp(hint, "HH\\dict") ||
!strcasecmp(hint, "HH\\vec_or_dict") ||
!strcasecmp(hint, "HH\\arraylike") ||
(RuntimeOption::EvalHackArrDVArrs &&
(!strcasecmp(hint, "HH\\darray") ||
!strcasecmp(hint, "HH\\varray_or_darray")))
@@ -273,13 +276,16 @@ void ParameterExpression::compatibleDefault(FileScopeRawPtr file) {
case KindOfPersistentKeyset:
case KindOfKeyset:
compat = (acceptAny || !strcasecmp(hint, "HH\\keyset"));
compat = (acceptAny ||
!strcasecmp(hint, "HH\\keyset") ||
!strcasecmp(hint, "HH\\arraylike"));
return;
case KindOfPersistentArray:
case KindOfArray:
compat = (acceptAny ||
!strcasecmp(hint, "array") ||
!strcasecmp(hint, "HH\\arraylike") ||
(!RuntimeOption::EvalHackArrDVArrs &&
(!strcasecmp(hint, "HH\\varray") ||
!strcasecmp(hint, "HH\\darray") ||
@@ -1502,6 +1502,7 @@ void Parser::checkClassDeclName(const std::string& name) {
case AnnotType::DArray:
case AnnotType::VArrOrDArr:
case AnnotType::VecOrDict:
case AnnotType::ArrayLike:
case AnnotType::Self:
case AnnotType::This:
case AnnotType::Parent:
@@ -2685,6 +2686,7 @@ Parser::AutoAliasMap getAutoAliasedClassesHelper() {
HH_ONLY_TYPE(this),
HH_ONLY_TYPE(varray_or_darray),
HH_ONLY_TYPE(vec_or_dict),
HH_ONLY_TYPE(arraylike),
HH_ONLY_TYPE(nonnull),
HH_ALIAS(classname, string),
HH_ALIAS(typename, string),
@@ -290,6 +290,9 @@ TypeStructure::Kind TypeAnnotation::getKind() const {
if (!strcasecmp(m_name.c_str(), "HH\\vec_or_dict")) {
return TypeStructure::Kind::T_vec_or_dict;
}
if (!strcasecmp(m_name.c_str(), "HH\\arraylike")) {
return TypeStructure::Kind::T_arraylike;
}
if (!strcasecmp(m_name.c_str(), "HH\\dict")) {
return TypeStructure::Kind::T_dict;
}
@@ -51,6 +51,7 @@ let get_hint_display_name hint =
| "HH\\darray" -> "HH\\darray"
| "HH\\varray_or_darray" -> "HH\\varray_or_darray"
| "HH\\vec_or_dict" -> "HH\\vec_or_dict"
| "HH\\arraylike" -> "HH\\arraylike"
| "HH\\int" -> "int"
| "HH\\num" -> "num"
| "HH\\arraykey" -> "arraykey"
@@ -33,9 +33,6 @@ let get_kind_num ~tparams p =
| "hh\\mixed" -> 9
| "tuple" -> 10
| "fun" -> 11
| "hh\\darray" -> 24
| "hh\\varray" -> 25
| "hh\\varray_or_darray" -> 26
| "array" -> 12
| "typevar" | "hh\\_" -> 13 (* corresponds to user OF_GENERIC *)
| "shape" -> 14
@@ -48,6 +45,10 @@ let get_kind_num ~tparams p =
| "hh\\keyset" -> 21
| "hh\\vec_or_dict" -> 22
| "hh\\nonnull" -> 23
| "hh\\darray" -> 24
| "hh\\varray" -> 25
| "hh\\varray_or_darray" -> 26
| "hh\\arraylike" -> 27
| "typeaccess" -> 102
| _ when String.length p > 4 && String.sub p 0 4 = "xhp_" -> 103
| "unresolved"
@@ -63,10 +64,15 @@ and is_prim = function
| _ -> false
and is_resolved_classname = function
| "HH\\darray" | "HH\\varray" | "HH\\varray_or_darray"
| "array" | "HH\\vec"
| "HH\\dict" | "HH\\keyset"
| "HH\\vec_or_dict" -> true
| "array"
| "HH\\darray"
| "HH\\varray"
| "HH\\varray_or_darray"
| "HH\\vec"
| "HH\\dict"
| "HH\\keyset"
| "HH\\vec_or_dict"
| "HH\\arraylike" -> true
| _ -> false
let add_ns ~namespace id =
@@ -89,6 +89,7 @@ let alias_map = List.fold_left ~f:add_alias ~init:SMap.empty
HH_ONLY_TYPE("this");
HH_ONLY_TYPE("varray_or_darray");
HH_ONLY_TYPE("vec_or_dict");
HH_ONLY_TYPE("arraylike");
HH_ONLY_TYPE("nonnull");
HH_ALIAS("classname", "string");
@@ -4138,6 +4138,14 @@ folly::Optional<Type> Index::get_type_for_annotated_type(
if (candidate.subtypeOf(TVec)) return TVec;
if (candidate.subtypeOf(TDict)) return TDict;
break;
case AnnotMetaType::ArrayLike:
if (candidate.subtypeOf(TVArr)) return TVArr;
if (candidate.subtypeOf(TDArr)) return TDArr;
if (candidate.subtypeOf(TArr)) return TArr;
if (candidate.subtypeOf(TVec)) return TVec;
if (candidate.subtypeOf(TDict)) return TDict;
if (candidate.subtypeOf(TKeyset)) return TKeyset;
break;
}
return folly::none;
}();
@@ -2372,6 +2372,7 @@ void isAsTypeStructImpl(ISS& env, SArray ts) {
case TypeStructure::Kind::T_enum:
case TypeStructure::Kind::T_resource:
case TypeStructure::Kind::T_vec_or_dict:
case TypeStructure::Kind::T_arraylike:
// TODO(T29232862): implement
return result(TBool);
case TypeStructure::Kind::T_typeaccess:
@@ -2848,6 +2848,10 @@ folly::Optional<Type> type_of_type_structure(SArray ts) {
return is_nullable ? TOptKeyset : TKeyset;
case TypeStructure::Kind::T_vec_or_dict:
return is_nullable ? union_of(TOptVec, TOptDict) : union_of(TVec, TDict);
case TypeStructure::Kind::T_arraylike:
return is_nullable
? union_of(union_of(union_of(TOptArr, TOptVec), TOptDict), TOptKeyset)
: union_of(union_of(union_of(TArr, TVec), TDict), TKeyset);
case TypeStructure::Kind::T_void:
return TNull;
case TypeStructure::Kind::T_tuple: {
@@ -3005,6 +3009,7 @@ Type from_hni_constraint(SString s) {
if (!strcasecmp(p, "HH\\float")) return union_of(ret, TDbl);
if (!strcasecmp(p, "HH\\num")) return union_of(ret, TNum);
if (!strcasecmp(p, "HH\\string")) return union_of(ret, TStr);
if (!strcasecmp(p, "HH\\arraykey")) return union_of(ret, TArrKey);
if (!strcasecmp(p, "HH\\dict")) return union_of(ret, TDict);
if (!strcasecmp(p, "HH\\vec")) return union_of(ret, TVec);
if (!strcasecmp(p, "HH\\keyset")) return union_of(ret, TKeyset);
@@ -3020,13 +3025,16 @@ Type from_hni_constraint(SString s) {
}
return union_of(ret, TArr);
}
if (!strcasecmp(p, "array")) return union_of(ret, TArr);
if (!strcasecmp(p, "HH\\arraykey")) return union_of(ret, TArrKey);
if (!strcasecmp(p, "HH\\mixed")) return TInitGen;
if (!strcasecmp(p, "HH\\nonnull")) return TInitGen;
if (!strcasecmp(p, "HH\\vec_or_dict")) {
return union_of(ret, union_of(TVec, TDict));
}
if (!strcasecmp(p, "HH\\arraylike")) {
return union_of(ret,
union_of(TArr, union_of(TVec, union_of(TDict, TKeyset))));
}
if (!strcasecmp(p, "array")) return union_of(ret, TArr);
if (!strcasecmp(p, "HH\\mixed")) return TInitGen;
if (!strcasecmp(p, "HH\\nonnull")) return TInitGen;
// It might be an object, or we might want to support type aliases in HNI at
// some point. For now just be conservative.
@@ -96,6 +96,7 @@ static const std::pair<HhvmStrToTypeMap, StdStrToTypeMap>& getAnnotTypeMaps() {
? AnnotType::VecOrDict : AnnotType::VArrOrDArr
},
{ "HH\\vec_or_dict", AnnotType::VecOrDict },
{ "HH\\arraylike", AnnotType::ArrayLike },
};
for (unsigned i = 0; i < sizeof(pairs) / sizeof(Pair); ++i) {
mappedPairs.first[makeStaticString(pairs[i].name)] = pairs[i].type;
@@ -46,7 +46,8 @@ enum class AnnotMetaType : uint8_t {
DArray = 9,
VArrOrDArr = 10,
VecOrDict = 11,
Nonnull = 12,
ArrayLike = 12,
Nonnull = 13,
};
enum class AnnotType : uint16_t {
@@ -75,6 +76,7 @@ enum class AnnotType : uint16_t {
DArray = (uint16_t)AnnotMetaType::DArray << 8 | (uint8_t)KindOfUninit,
VArrOrDArr = (uint16_t)AnnotMetaType::VArrOrDArr << 8 | (uint8_t)KindOfUninit,
VecOrDict = (uint16_t)AnnotMetaType::VecOrDict << 8 | (uint8_t)KindOfUninit,
ArrayLike = (uint16_t)AnnotMetaType::ArrayLike << 8 | (uint8_t)KindOfUninit,
};
inline AnnotMetaType getAnnotMetaType(AnnotType at) {
@@ -227,7 +229,13 @@ annotCompat(DataType dt, AnnotType at, const StringData* annotClsName) {
: AnnotAction::Pass;
case AnnotMetaType::VecOrDict:
return (isVecType(dt) || isDictType(dt))
? AnnotAction::Pass : AnnotAction::Fail;
? AnnotAction::Pass
: AnnotAction::Fail;
case AnnotMetaType::ArrayLike:
return (isArrayType(dt) || isVecType(dt) ||
isDictType(dt) || isKeysetType(dt))
? AnnotAction::Pass
: AnnotAction::Fail;
case AnnotMetaType::Precise:
if (UNLIKELY(RuntimeOption::EvalHackArrCompatTypeHintNotices) &&
at == AnnotType::Array && isArrayType(dt)) {
@@ -240,6 +240,7 @@ MaybeDataType get_datatype(
}
if (!strcasecmp(name.c_str(), "HH\\varray_or_darray")) return folly::none;
if (!strcasecmp(name.c_str(), "HH\\vec_or_dict")) return folly::none;
if (!strcasecmp(name.c_str(), "HH\\arraylike")) return folly::none;
return KindOfObject;
}
if (is_nullable || is_soft) {
@@ -267,6 +268,7 @@ MaybeDataType get_datatype(
}
if (!strcasecmp(name.c_str(), "HH\\varray_or_darray")) return folly::none;
if (!strcasecmp(name.c_str(), "HH\\vec_or_dict")) return folly::none;
if (!strcasecmp(name.c_str(), "HH\\arraylike")) return folly::none;
if (!strcasecmp(name.c_str(), "HH\\resource")) return KindOfResource;
if (!strcasecmp(name.c_str(), "HH\\mixed")) return folly::none;
if (!strcasecmp(name.c_str(), "HH\\nonnull")) return folly::none;
@@ -189,6 +189,10 @@ bool checkTypeStructureMatchesCellImpl(
case TypeStructure::Kind::T_vec_or_dict:
result = isVecType(type) || isDictType(type);
break;
case TypeStructure::Kind::T_arraylike:
result = isArrayType(type) || isVecType(type) ||
isDictType(type) || isKeysetType(type);
break;
case TypeStructure::Kind::T_enum: {
assertx(ts.exists(s_classname));
auto const cls = Unit::lookupClass(ts[s_classname].asStrRef().get());
@@ -417,6 +421,7 @@ void errorOnIsAsExpressionInvalidTypes(const Array& ts) {
case TypeStructure::Kind::T_vec:
case TypeStructure::Kind::T_keyset:
case TypeStructure::Kind::T_vec_or_dict:
case TypeStructure::Kind::T_arraylike:
case TypeStructure::Kind::T_enum:
case TypeStructure::Kind::T_class:
case TypeStructure::Kind::T_interface:
@@ -477,6 +482,7 @@ bool typeStructureCouldBeNonStatic(const Array& ts) {
case TypeStructure::Kind::T_vec:
case TypeStructure::Kind::T_keyset:
case TypeStructure::Kind::T_vec_or_dict:
case TypeStructure::Kind::T_arraylike:
case TypeStructure::Kind::T_unresolved:
case TypeStructure::Kind::T_typeaccess:
return true;
@@ -100,6 +100,7 @@ const std::string
s_hh_dict("HH\\dict"),
s_hh_keyset("HH\\keyset"),
s_hh_vec_or_dict("HH\\vec_or_dict"),
s_hh_arraylike("HH\\arraylike"),
s_hh("HH\\")
;
@@ -331,6 +332,12 @@ std::string fullName(const Array& arr, bool forDisplay) {
genericTypeName(arr, name, forDisplay);
}
break;
case TypeStructure::Kind::T_arraylike:
name += s_hh_arraylike;
if (arr.exists(s_generic_types)) {
genericTypeName(arr, name, forDisplay);
}
break;
case TypeStructure::Kind::T_typevar:
assertx(arr.exists(s_name));
name += arr[s_name].toCStrRef().toCppString();
@@ -617,7 +624,8 @@ Array resolveTS(TSEnv& env,
case TypeStructure::Kind::T_dict:
case TypeStructure::Kind::T_vec:
case TypeStructure::Kind::T_keyset:
case TypeStructure::Kind::T_vec_or_dict: {
case TypeStructure::Kind::T_vec_or_dict:
case TypeStructure::Kind::T_arraylike: {
if (kind == TypeStructure::Kind::T_array ||
kind == TypeStructure::Kind::T_darray ||
kind == TypeStructure::Kind::T_varray ||
@@ -64,6 +64,7 @@ enum class Kind : uint8_t {
T_darray = 24,
T_varray = 25,
T_varray_or_darray = 26,
T_arraylike = 27,
/* The following kinds needs class/alias resolution, and
* are not exposed to the users. */
@@ -66,6 +66,14 @@ TypedValue HHVM_FUNCTION(dummy_varr_or_darr_builtin, const Variant& var) {
return tvReturn(staticEmptyVArray());
}
TypedValue HHVM_FUNCTION(dummy_arraylike_builtin, const Variant& var) {
if (var.isArray()) {
auto const& arr = var.asCArrRef();
return tvReturn(arr);
}
return tvReturn(staticEmptyKeysetArray());
}
Array HHVM_FUNCTION(dummy_array_builtin, const Array& arr) {
if (!arr.isVecOrVArray() && !arr.isDictOrDArray()) return arr;
return Array::Create();
@@ -82,6 +90,8 @@ void StandardExtension::initIntrinsics() {
HHVM_FALIAS(__hhvm_intrinsics\\dummy_darray_builtin, dummy_darray_builtin);
HHVM_FALIAS(__hhvm_intrinsics\\dummy_varr_or_darr_builtin,
dummy_varr_or_darr_builtin);
HHVM_FALIAS(__hhvm_intrinsics\\dummy_arraylike_builtin,
dummy_arraylike_builtin);
HHVM_FALIAS(__hhvm_intrinsics\\dummy_array_builtin, dummy_array_builtin);
loadSystemlib("std_intrinsics");
@@ -25,6 +25,9 @@ function trigger_oom(bool $oom): void;
<<__Native, __HipHopSyntax>>
function launder_value(mixed $value): mixed;
/*
* Builtins for testing array-ish builtin typehints.
*/
<<__Native, __HipHopSyntax>>
function dummy_varray_builtin(varray $x): varray;
@@ -34,6 +37,9 @@ function dummy_darray_builtin(darray $x): darray;
<<__Native, __HipHopSyntax>>
function dummy_varr_or_darr_builtin(varray_or_darray $x): varray_or_darray;
<<__Native, __HipHopSyntax>>
function dummy_arraylike_builtin(arraylike $x): arraylike;
<<__Native, __HipHopSyntax>>
function dummy_array_builtin(array $x): array;
Oops, something went wrong.

0 comments on commit 1f09dd2

Please sign in to comment.