[vm] Replace cycle_free and type_finalized bits with class loading state

This change renames Class::is_cycle_free to Class::is_declaration_loaded
and under the hood replaces cycle_free and type_finalized bits with
class loading state enum.

Also, added new assertions to check is_declaration_loaded().

Change-Id: Ib43e12731d0dc782e273be8e55912494e102cd79
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104920
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 88858e7..18173a7 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -191,15 +191,15 @@
     class_array = object_store->pending_classes();
     ASSERT(!class_array.IsNull());
     Class& cls = Class::Handle();
-    // Mark all classes as cycle-free (should be checked by front-end).
-    // TODO(alexmarkov): Cleanup is_cycle_free bit on classes.
+
+#if defined(DEBUG)
     for (intptr_t i = 0; i < class_array.Length(); i++) {
       cls ^= class_array.At(i);
-      if (!cls.is_cycle_free()) {
-        cls.set_is_cycle_free();
-      }
+      ASSERT(cls.is_declaration_loaded());
     }
-    // Finalize all classes.
+#endif
+
+    // Finalize types in all classes.
     for (intptr_t i = 0; i < class_array.Length(); i++) {
       cls ^= class_array.At(i);
       FinalizeTypesInClass(cls);
@@ -1005,6 +1005,7 @@
 void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
   Thread* thread = Thread::Current();
   HANDLESCOPE(thread);
+  ASSERT(cls.is_declaration_loaded());
   if (cls.is_type_finalized()) {
     return;
   }
diff --git a/runtime/vm/class_finalizer_test.cc b/runtime/vm/class_finalizer_test.cc
index a1440d2..7768c44 100644
--- a/runtime/vm/class_finalizer_test.cc
+++ b/runtime/vm/class_finalizer_test.cc
@@ -16,6 +16,7 @@
   const Class& cls = Class::Handle(Class::New(
       Library::Handle(), class_name, script, TokenPosition::kNoSource));
   cls.set_interfaces(Object::empty_array());
+  cls.set_is_declaration_loaded();
   cls.SetFunctions(Object::empty_array());
   cls.SetFields(Object::empty_array());
   return cls.raw();
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 4186f23..c7e57fce 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -1066,8 +1066,8 @@
   Class& toplevel_class =
       Class::Handle(Z, Class::New(library, Symbols::TopLevel(), script,
                                   TokenPosition::kNoSource, register_class));
+  toplevel_class.set_is_declaration_loaded();
   toplevel_class.set_is_type_finalized();
-  toplevel_class.set_is_cycle_free();
   library.set_toplevel_class(toplevel_class);
 
   library_helper.ReadUntilExcluding(LibraryHelper::kDependencies);
@@ -1386,6 +1386,8 @@
   if (class_helper->is_transformed_mixin_application()) {
     klass->set_is_transformed_mixin_application();
   }
+
+  klass->set_is_declaration_loaded();
 }
 
 void KernelLoader::LoadClass(const Library& library,
@@ -1438,10 +1440,9 @@
       helper_.ReadListLength();  // read type_parameters list length.
 
   ActiveClassScope active_class_scope(&active_class_, out_class);
-  if (!out_class->is_cycle_free()) {
+  if (!out_class->is_declaration_loaded()) {
     LoadPreliminaryClass(&class_helper, type_parameter_counts);
   } else {
-    // do not use type parameters with cycle_free
     ASSERT(type_parameter_counts == 0);
     class_helper.SetJustRead(ClassHelper::kTypeParameters);
   }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 08b8a5f..40a56d7 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -533,8 +533,8 @@
     cls.set_id(Class::kClassId);
     cls.set_state_bits(0);
     cls.set_is_finalized();
+    cls.set_is_declaration_loaded();
     cls.set_is_type_finalized();
-    cls.set_is_cycle_free();
     cls.set_type_arguments_field_offset_in_words(Class::kNoTypeArguments);
     cls.set_num_type_arguments(0);
     cls.set_num_own_type_arguments(0);
@@ -555,16 +555,16 @@
   cls.set_num_type_arguments(0);
   cls.set_num_own_type_arguments(0);
   cls.set_is_finalized();
+  cls.set_is_declaration_loaded();
   cls.set_is_type_finalized();
-  cls.set_is_cycle_free();
 
   // Allocate and initialize the forwarding corpse class.
   cls = Class::New<ForwardingCorpse::FakeInstance>(kForwardingCorpse);
   cls.set_num_type_arguments(0);
   cls.set_num_own_type_arguments(0);
   cls.set_is_finalized();
+  cls.set_is_declaration_loaded();
   cls.set_is_type_finalized();
-  cls.set_is_cycle_free();
 
   // Allocate and initialize the sentinel values of Null class.
   {
@@ -832,22 +832,22 @@
   cls.set_num_type_arguments(0);
   cls.set_num_own_type_arguments(0);
   cls.set_is_finalized();
+  cls.set_is_declaration_loaded();
   cls.set_is_type_finalized();
-  cls.set_is_cycle_free();
   dynamic_class_ = cls.raw();
 
   cls = Class::New<Instance>(kVoidCid);
   cls.set_num_type_arguments(0);
   cls.set_num_own_type_arguments(0);
   cls.set_is_finalized();
+  cls.set_is_declaration_loaded();
   cls.set_is_type_finalized();
-  cls.set_is_cycle_free();
   void_class_ = cls.raw();
 
   cls = Class::New<Type>();
   cls.set_is_finalized();
+  cls.set_is_declaration_loaded();
   cls.set_is_type_finalized();
-  cls.set_is_cycle_free();
 
   cls = dynamic_class_;
   *dynamic_type_ = Type::NewNonParameterizedType(cls);
@@ -1604,7 +1604,6 @@
     // Class that represents the Dart class _Closure and C++ class Closure.
     cls = Class::New<Closure>();
     object_store->set_closure_class(cls);
-    cls.ResetFinalization();  // To calculate field offsets from Dart source.
     RegisterPrivateClass(cls, Symbols::_Closure(), core_lib);
     pending_classes.Add(cls);
 
@@ -2309,8 +2308,10 @@
       (FakeObject::kClassId == kTypeArgumentsCid)) {
     // VM internal classes are done. There is no finalization needed or
     // possible in this case.
+    result.set_is_declaration_loaded();
+    result.set_is_type_finalized();
     result.set_is_finalized();
-  } else {
+  } else if (FakeObject::kClassId != kClosureCid) {
     // VM backed classes are almost ready: run checks and resolve class
     // references, but do not recompute size.
     result.set_is_prefinalized();
@@ -3739,9 +3740,9 @@
     cls.set_next_field_offset(instance_size);
     cls.set_num_native_fields(field_count);
     cls.set_is_finalized();
+    cls.set_is_declaration_loaded();
     cls.set_is_type_finalized();
     cls.set_is_synthesized_class();
-    cls.set_is_cycle_free();
     cls.set_kernel_offset(-1);
     library.AddClass(cls);
     return cls.raw();
@@ -4069,8 +4070,17 @@
   set_state_bits(AbstractBit::update(true, raw_ptr()->state_bits_));
 }
 
+void Class::set_is_declaration_loaded() const {
+  ASSERT(!is_declaration_loaded());
+  set_state_bits(ClassLoadingBits::update(RawClass::kDeclarationLoaded,
+                                          raw_ptr()->state_bits_));
+}
+
 void Class::set_is_type_finalized() const {
-  set_state_bits(TypeFinalizedBit::update(true, raw_ptr()->state_bits_));
+  ASSERT(is_declaration_loaded());
+  ASSERT(!is_type_finalized());
+  set_state_bits(ClassLoadingBits::update(RawClass::kTypeFinalized,
+                                          raw_ptr()->state_bits_));
 }
 
 void Class::set_is_patch() const {
@@ -4098,11 +4108,6 @@
   set_state_bits(FieldsMarkedNullableBit::update(true, raw_ptr()->state_bits_));
 }
 
-void Class::set_is_cycle_free() const {
-  ASSERT(!is_cycle_free());
-  set_state_bits(CycleFreeBit::update(true, raw_ptr()->state_bits_));
-}
-
 void Class::set_is_allocated(bool value) const {
   set_state_bits(IsAllocatedBit::update(value, raw_ptr()->state_bits_));
 }
@@ -4117,13 +4122,6 @@
       ClassFinalizedBits::update(RawClass::kFinalized, raw_ptr()->state_bits_));
 }
 
-void Class::ResetFinalization() const {
-  ASSERT(IsTopLevel() || IsClosureClass());
-  set_state_bits(
-      ClassFinalizedBits::update(RawClass::kAllocated, raw_ptr()->state_bits_));
-  set_state_bits(TypeFinalizedBit::update(false, raw_ptr()->state_bits_));
-}
-
 void Class::set_is_prefinalized() const {
   ASSERT(!is_finalized());
   set_state_bits(ClassFinalizedBits::update(RawClass::kPreFinalized,
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 453e847..99bd0f6 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -950,7 +950,10 @@
   }
 
   // The super type of this class, Object type if not explicitly specified.
-  RawAbstractType* super_type() const { return raw_ptr()->super_type_; }
+  RawAbstractType* super_type() const {
+    ASSERT(is_declaration_loaded());
+    return raw_ptr()->super_type_;
+  }
   void set_super_type(const AbstractType& value) const;
   static intptr_t super_type_offset() {
     return OFFSET_OF(RawClass, super_type_);
@@ -1117,8 +1120,17 @@
   }
   void set_is_abstract() const;
 
+  RawClass::ClassLoadingState class_loading_state() const {
+    return ClassLoadingBits::decode(raw_ptr()->state_bits_);
+  }
+
+  bool is_declaration_loaded() const {
+    return class_loading_state() >= RawClass::kDeclarationLoaded;
+  }
+  void set_is_declaration_loaded() const;
+
   bool is_type_finalized() const {
-    return TypeFinalizedBit::decode(raw_ptr()->state_bits_);
+    return class_loading_state() >= RawClass::kTypeFinalized;
   }
   void set_is_type_finalized() const;
 
@@ -1146,8 +1158,6 @@
 
   void set_is_prefinalized() const;
 
-  void ResetFinalization() const;
-
   bool is_const() const { return ConstBit::decode(raw_ptr()->state_bits_); }
   void set_is_const() const;
 
@@ -1167,11 +1177,6 @@
   }
   void set_is_fields_marked_nullable() const;
 
-  bool is_cycle_free() const {
-    return CycleFreeBit::decode(raw_ptr()->state_bits_);
-  }
-  void set_is_cycle_free() const;
-
   bool is_allocated() const {
     return IsAllocatedBit::decode(raw_ptr()->state_bits_);
   }
@@ -1334,16 +1339,16 @@
   enum StateBits {
     kConstBit = 0,
     kImplementedBit = 1,
-    kTypeFinalizedBit = 2,
-    kClassFinalizedPos = 3,
+    kClassFinalizedPos = 2,
     kClassFinalizedSize = 2,
-    kAbstractBit = kClassFinalizedPos + kClassFinalizedSize,  // = 5
-    kPatchBit = 6,
+    kClassLoadingPos = kClassFinalizedPos + kClassFinalizedSize,  // = 4
+    kClassLoadingSize = 2,
+    kAbstractBit = kClassLoadingPos + kClassLoadingSize,  // = 6
+    kPatchBit,
     kSynthesizedClassBit,
     kMixinAppAliasBit,
     kMixinTypeAppliedBit,
     kFieldsMarkedNullableBit,
-    kCycleFreeBit,
     kEnumBit,
     kTransformedMixinApplicationBit,
     kIsAllocatedBit,
@@ -1351,19 +1356,20 @@
   };
   class ConstBit : public BitField<uint16_t, bool, kConstBit, 1> {};
   class ImplementedBit : public BitField<uint16_t, bool, kImplementedBit, 1> {};
-  class TypeFinalizedBit
-      : public BitField<uint16_t, bool, kTypeFinalizedBit, 1> {};
   class ClassFinalizedBits : public BitField<uint16_t,
                                              RawClass::ClassFinalizedState,
                                              kClassFinalizedPos,
                                              kClassFinalizedSize> {};
+  class ClassLoadingBits : public BitField<uint16_t,
+                                           RawClass::ClassLoadingState,
+                                           kClassLoadingPos,
+                                           kClassLoadingSize> {};
   class AbstractBit : public BitField<uint16_t, bool, kAbstractBit, 1> {};
   class PatchBit : public BitField<uint16_t, bool, kPatchBit, 1> {};
   class SynthesizedClassBit
       : public BitField<uint16_t, bool, kSynthesizedClassBit, 1> {};
   class FieldsMarkedNullableBit
       : public BitField<uint16_t, bool, kFieldsMarkedNullableBit, 1> {};
-  class CycleFreeBit : public BitField<uint16_t, bool, kCycleFreeBit, 1> {};
   class EnumBit : public BitField<uint16_t, bool, kEnumBit, 1> {};
   class TransformedMixinApplicationBit
       : public BitField<uint16_t, bool, kTransformedMixinApplicationBit, 1> {};
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index ace33b6..0921cc8 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -31,6 +31,7 @@
   const Class& cls = Class::Handle(Class::New(
       Library::Handle(), class_name, script, TokenPosition::kNoSource));
   cls.set_is_synthesized_class();  // Dummy class for testing.
+  cls.set_is_declaration_loaded();
   return cls.raw();
 }
 
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 355cb6b..629723c 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -735,6 +735,19 @@
     kPreFinalized,          // VM classes: size precomputed, but no checks done.
     kFinalized,             // Class parsed, finalized and ready for use.
   };
+  enum ClassLoadingState {
+    // Class object is created, but it is not filled up.
+    // At this state class can only be used as a forward reference during
+    // class loading.
+    kNameOnly = 0,
+    // Class declaration information such as type parameters, supertype and
+    // implemented interfaces are loaded. However, types in the class are
+    // not finalized yet.
+    kDeclarationLoaded,
+    // Types in the class are finalized. At this point, members can be loaded
+    // and class can be finalized.
+    kTypeFinalized,
+  };
 
  private:
   RAW_HEAP_OBJECT_IMPLEMENTATION(Class);