Skip to content

Commit 6762285

Browse files
paulbissfacebook-github-bot
authored andcommitted
Various fixes for enum class
Summary: This diff fixes the following issues that arise from the fact that enum classes are tagged with `AttrEnum` by making `AttrEnum` and `AttrEnumClass` mutually exclusive: - Jumpstart serialization assumed that any `AttrEnum` class had a base type that was either `int` or `string` (this was actually doubly wrong as even for enums this type could also have been `arraykey`) - A variety of places in hhbbc, the jit, and the runtime treat any `AttrEnum` type name as though it's an alias for the enum's base type. This is not true of enum classes. - Checks were missing to prevent enum classes from including enums and vice-versa - BuiltinEnumClass was the only class marked AttrEnumClass but not AttrEnum, now it is marked neither to match BuiltinEnum (which is not marked AttrEnum). This made the fix to Class::setParent below possible Additionally the following mostly benign issues were fixed: - Dead code in Class::classof (which would have been broken had it been possible to instantiate an enum class) was removed - Dead code in Class::setParent that assumed it was possible to extend an enum class (by means other than enum inclusion) was removed (all enums extend BuiltinEnum and all enum classes extend BuiltinEnumClass) - In index.cpp a check for enums which include other enums had erroneously negated a check for AttrEnum - In prof-data-serialize.cpp we weren't reading/writing included enums. While not strictly a requirement for correctness it is better to do this here. Reviewed By: jano Differential Revision: D27574956 fbshipit-source-id: 36910981a3daf7d5c5c01a48df2f62fd22fd8608
1 parent 0460f25 commit 6762285

File tree

20 files changed

+91
-78
lines changed

20 files changed

+91
-78
lines changed

hphp/hack/src/hhbc/print.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ fn print_class_special_attributes<W: Write>(
751751
if c.is_sealed() {
752752
special_attributes.push("sealed");
753753
}
754-
if c.enum_type.is_some() {
754+
if c.enum_type.is_some() && !hhas_attribute::has_enum_class(user_attrs) {
755755
special_attributes.push("enum");
756756
}
757757
if c.is_abstract() {

hphp/hack/test/enum_atom/compilation/test.php.exp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
}
117117
}
118118

119-
.class {} [enum enum_class "__EnumClass"("""v:0:{}""")] E (16,19) extends HH\BuiltinEnumClass {
119+
.class {} [enum_class "__EnumClass"("""v:0:{}""")] E (16,19) extends HH\BuiltinEnumClass {
120120
.enum_ty <"I" extended_hint>;
121121
.const A = uninit;
122122
.const B = uninit;
@@ -181,7 +181,7 @@
181181
}
182182
}
183183

184-
.class {} [enum enum_class "__EnumClass"("""v:0:{}""")] F (25,27) extends HH\BuiltinEnumClass enum_includes (E) {
184+
.class {} [enum_class "__EnumClass"("""v:0:{}""")] F (25,27) extends HH\BuiltinEnumClass enum_includes (E) {
185185
.enum_ty <"I" extended_hint>;
186186
.const C = uninit;
187187
.method {}{} [private static no_injection] (25,27) 86cinit($constName) {

hphp/hhbbc/index.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,7 +1564,7 @@ bool build_class_constants(BuildClsInfo& info,
15641564

15651565
// A constant from an interface or from an included enum collides
15661566
// with an existing constant.
1567-
if (rparent->cls->attrs & (AttrInterface | AttrEnum)) {
1567+
if (rparent->cls->attrs & (AttrInterface | AttrEnum | AttrEnumClass)) {
15681568
ITRACE(2,
15691569
"build_cls_info_rec failed for `{}' because "
15701570
"`{}' was defined by both `{}' and `{}'\n",
@@ -2658,11 +2658,13 @@ void resolve_combinations(ClassNamingEnv& env,
26582658

26592659
for (auto& included_enum_name : cls->includedEnumNames) {
26602660
auto const included_enum = map.at(included_enum_name);
2661-
if (!(included_enum->cls->attrs & AttrEnum)) {
2661+
auto const want_attr = cls->attrs & (AttrEnum | AttrEnumClass);
2662+
if (!(included_enum->cls->attrs & want_attr)) {
26622663
ITRACE(2,
26632664
"Resolve combinations failed for `{}' because `{}' "
2664-
"is not an enum\n",
2665-
cls->name, included_enum_name);
2665+
"is not an enum{}\n",
2666+
cls->name, included_enum_name,
2667+
want_attr & AttrEnumClass ? " class" : "");
26662668
return;
26672669
}
26682670
cinfo->includedEnums.push_back(included_enum);
@@ -2780,6 +2782,7 @@ void compute_subclass_list(IndexData& index) {
27802782
trace_time _("compute subclass list");
27812783
auto fixupTraits = false;
27822784
auto fixupEnums = false;
2785+
auto const AnyEnum = AttrEnum | AttrEnumClass;
27832786
for (auto& cinfo : index.allClassInfos) {
27842787
if (cinfo->cls->attrs & AttrInterface) continue;
27852788
for (auto& cparent : cinfo->baseList) {
@@ -2791,13 +2794,13 @@ void compute_subclass_list(IndexData& index) {
27912794
compute_subclass_list_rec(index, cinfo.get(), cinfo.get());
27922795
}
27932796
// Add the included enum lists if cinfo is an enum
2794-
if (!(cinfo->cls->attrs & AttrEnum) &&
2797+
if ((cinfo->cls->attrs & AnyEnum) &&
27952798
cinfo->cls->includedEnumNames.size()) {
27962799
fixupEnums = true;
27972800
compute_included_enums_list_rec(index, cinfo.get(), cinfo.get());
27982801
}
27992802
// Also add instantiable classes to their interface's subclassLists
2800-
if (cinfo->cls->attrs & (AttrTrait | AttrEnum | AttrAbstract)) continue;
2803+
if (cinfo->cls->attrs & (AttrTrait | AnyEnum | AttrAbstract)) continue;
28012804
for (auto& ipair : cinfo->implInterfaces) {
28022805
auto impl = const_cast<ClassInfo*>(ipair.second);
28032806
impl->subclassList.push_back(cinfo.get());
@@ -2807,7 +2810,7 @@ void compute_subclass_list(IndexData& index) {
28072810
for (auto& cinfo : index.allClassInfos) {
28082811
auto& sub = cinfo->subclassList;
28092812
if ((fixupTraits && cinfo->cls->attrs & AttrTrait) ||
2810-
(fixupEnums && cinfo->cls->attrs & AttrEnum)) {
2813+
(fixupEnums && cinfo->cls->attrs & AnyEnum)) {
28112814
// traits and enums can be reached by multiple paths, so we need to
28122815
// uniquify their subclassLists.
28132816
std::sort(begin(sub), end(sub));
@@ -3027,7 +3030,7 @@ void trace_interfaces(const IndexData& index, const ConflictGraph& cg) {
30273030
ifaces.emplace_back(cinfo->cls);
30283031
continue;
30293032
}
3030-
if (cinfo->cls->attrs & (AttrTrait | AttrEnum)) continue;
3033+
if (cinfo->cls->attrs & (AttrTrait | AttrEnum | AttrEnumClass)) continue;
30313034

30323035
classes.emplace_back(Cls{cinfo.get()});
30333036
auto& vtable = classes.back().vtable;
@@ -3150,7 +3153,7 @@ void compute_iface_vtables(IndexData& index) {
31503153
}
31513154

31523155
// Only worry about classes with methods that can be called.
3153-
if (cinfo->cls->attrs & (AttrTrait | AttrEnum)) continue;
3156+
if (cinfo->cls->attrs & (AttrTrait | AttrEnum | AttrEnumClass)) continue;
31543157

31553158
for (auto& ipair : cinfo->implInterfaces) {
31563159
++iface_uses[ipair.second->cls];

hphp/runtime/base/enum-cache.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ const EnumValues* EnumCache::getValues(const Class* klass,
4343
}
4444

4545
const EnumValues* EnumCache::getValuesBuiltin(const Class* klass) {
46-
assertx(isEnum(klass));
46+
assertx(isAnyEnum(klass));
4747
if (auto const values = klass->getEnumValues()) return values;
4848
return s_cache.getEnumValues(klass, false);
4949
}
5050

5151
const EnumValues* EnumCache::getValuesStatic(const Class* klass) {
52-
assertx(isEnum(klass));
52+
assertx(isAnyEnum(klass));
5353
auto const result = [&]() -> const EnumValues* {
5454
if (auto const values = klass->getEnumValues()) return values;
5555
return s_cache.getEnumValues(klass, false, true);
@@ -144,7 +144,7 @@ const EnumValues* EnumCache::loadEnumValues(
144144
}
145145
// The outer condition below enables caching of enum constants defined
146146
// in enums included by the current class.
147-
if (!(isEnum(klass)
147+
if (!(isAnyEnum(klass)
148148
&& klass->hasIncludedEnums()
149149
&& klass->allIncludedEnums().contains(consts[i].cls->name()))) {
150150
if (consts[i].cls != klass && !recurse) {

hphp/runtime/base/object-data-inl.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ template <bool Unlocked>
120120
NEVER_INLINE ObjectData* ObjectData::newInstanceSlow(Class* cls) {
121121
assertx(cls);
122122
if (UNLIKELY(cls->attrs() &
123-
(AttrAbstract | AttrInterface | AttrTrait | AttrEnum))) {
123+
(AttrAbstract | AttrInterface | AttrTrait | AttrEnum |
124+
AttrEnumClass))) {
124125
raiseAbstractClassError(cls);
125126
}
126127
if (cls->hasReifiedGenerics()) {
@@ -158,7 +159,8 @@ inline ObjectData* ObjectData::newInstance(Class* cls) {
158159
return newInstanceSlow<Unlocked>(cls);
159160
}
160161
if (UNLIKELY(cls->attrs() &
161-
(AttrAbstract | AttrInterface | AttrTrait | AttrEnum))) {
162+
(AttrAbstract | AttrInterface | AttrTrait | AttrEnum |
163+
AttrEnumClass))) {
162164
raiseAbstractClassError(cls);
163165
}
164166
auto obj = ObjectData::newInstanceImpl<Unlocked>(
@@ -176,7 +178,8 @@ inline ObjectData* ObjectData::newInstanceReified(Class* cls,
176178
ArrayData* reifiedTypes) {
177179
assertx(cls);
178180
if (UNLIKELY(cls->attrs() &
179-
(AttrAbstract | AttrInterface | AttrTrait | AttrEnum))) {
181+
(AttrAbstract | AttrInterface | AttrTrait | AttrEnum |
182+
AttrEnumClass))) {
180183
raiseAbstractClassError(cls);
181184
}
182185
if (cls->hasReifiedGenerics()) {
@@ -207,7 +210,8 @@ inline ObjectData* ObjectData::newInstanceReified(Class* cls,
207210

208211
inline ObjectData* ObjectData::newInstanceNoPropInit(Class* cls) {
209212
assertx(!(cls->attrs() &
210-
(AttrAbstract | AttrInterface | AttrTrait | AttrEnum)));
213+
(AttrAbstract | AttrInterface | AttrTrait | AttrEnum |
214+
AttrEnumClass)));
211215
return ObjectData::newInstanceImpl<false>(
212216
cls,
213217
[&](void* mem, uint8_t sizeFlag) {

hphp/runtime/base/object-data.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1554,7 +1554,7 @@ void ObjectData::raiseAbstractClassError(Class* cls) {
15541554
raise_error("Cannot instantiate %s %s",
15551555
(attrs & AttrInterface) ? "interface" :
15561556
(attrs & AttrTrait) ? "trait" :
1557-
(attrs & AttrEnum) ? "enum" : "abstract class",
1557+
(attrs & (AttrEnum|AttrEnumClass)) ? "enum" : "abstract class",
15581558
cls->preClass()->name()->data());
15591559
}
15601560

hphp/runtime/base/type-structure.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ bool resolveClass(TSEnv& env, const TSCtx& ctx, Array& ret,
602602
if (!cls) return false;
603603

604604
TypeStructure::Kind resolvedKind;
605-
if (isNormalClass(cls)) {
605+
if (isNormalClass(cls) || isEnumClass(cls)) {
606606
resolvedKind = TypeStructure::Kind::T_class;
607607
} else if (isInterface(cls)) {
608608
resolvedKind = TypeStructure::Kind::T_interface;

hphp/runtime/ext/enum/ext_enum.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ final public static function assertAll(
8787
* definition below is not actually used at run time; it is simply
8888
* provided for the typechecker and for developer reference.
8989
*/
90-
<<__EnumClass>>
9190
abstract class BuiltinEnumClass<+T> {
9291
/**
9392
* Get the values of the public consts defined on this class,

hphp/runtime/ext/reflection/ext_reflection.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,8 @@ static bool HHVM_METHOD(ReflectionClass, isInternal) {
11421142

11431143
static bool HHVM_METHOD(ReflectionClass, isInstantiable) {
11441144
auto const cls = ReflectionClassHandle::GetClassFor(this_);
1145-
return !(cls->attrs() & (AttrAbstract | AttrInterface | AttrTrait | AttrEnum))
1145+
return !(cls->attrs() & (AttrAbstract | AttrInterface | AttrTrait | AttrEnum |
1146+
AttrEnumClass))
11461147
&& (cls->getCtor()->attrs() & AttrPublic);
11471148
}
11481149

@@ -1168,12 +1169,12 @@ static bool HHVM_METHOD(ReflectionClass, isTrait) {
11681169

11691170
static bool HHVM_METHOD(ReflectionClass, isEnum) {
11701171
auto const cls = ReflectionClassHandle::GetClassFor(this_);
1171-
return cls->attrs() & AttrEnum;
1172+
return cls->attrs() & (AttrEnum|AttrEnumClass);
11721173
}
11731174

11741175
static String HHVM_METHOD(ReflectionClass, getEnumUnderlyingType) {
11751176
auto const cls = ReflectionClassHandle::GetClassFor(this_);
1176-
if (!(cls->attrs() & AttrEnum)) {
1177+
if (!(cls->attrs() & (AttrEnum|AttrEnumClass))) {
11771178
Reflection::ThrowReflectionExceptionObject(
11781179
"Trying to read the Enum-type of a non-Enum");
11791180
}

hphp/runtime/ext/std/ext_std_classobj.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ bool HHVM_FUNCTION(trait_exists, const String& trait_name,
9696
bool HHVM_FUNCTION(enum_exists, const String& enum_name,
9797
bool autoload /* = true */) {
9898
Class* cls = Class::get(enum_name.get(), autoload);
99-
return cls && isEnum(cls);
99+
return cls && isAnyEnum(cls);
100100
}
101101

102102
Variant HHVM_FUNCTION(get_class_methods, const Variant& class_or_object) {

0 commit comments

Comments
 (0)