Pass that ensures that constant expressions are acyclic

Adds a check that constant expression definitions are acyclic.
Minor refactor of post parse passes order (with comments).
Refactor of constant expression passes (adds getReferences).

Bug: 31827278

Test: mma
Test: local test with moved lookups
Change-Id: Ic5764c6d5b403cfcefcf44f5c2015430633abb67
diff --git a/ConstantExpression.cpp b/ConstantExpression.cpp
index 84b434f..a593025 100644
--- a/ConstantExpression.cpp
+++ b/ConstantExpression.cpp
@@ -448,6 +448,18 @@
     return ret;
 }
 
+std::vector<Reference<LocalIdentifier>*> ConstantExpression::getReferences() {
+    const auto& constRet = static_cast<const ConstantExpression*>(this)->getReferences();
+    std::vector<Reference<LocalIdentifier>*> ret(constRet.size());
+    std::transform(constRet.begin(), constRet.end(), ret.begin(),
+                   [](const auto* ce) { return const_cast<Reference<LocalIdentifier>*>(ce); });
+    return ret;
+}
+
+std::vector<const Reference<LocalIdentifier>*> ConstantExpression::getReferences() const {
+    return {};
+}
+
 status_t ConstantExpression::recursivePass(const std::function<status_t(ConstantExpression*)>& func,
                                            std::unordered_set<const ConstantExpression*>* visited) {
     if (mIsPostParseCompleted) return OK;
@@ -460,6 +472,13 @@
         if (err != OK) return err;
     }
 
+    for (auto* nextRef : getReferences()) {
+        auto* nextCE = nextRef->shallowGet()->constExpr();
+        CHECK(nextCE != nullptr) << "Local identifier is not a constant expression";
+        status_t err = nextCE->recursivePass(func, visited);
+        if (err != OK) return err;
+    }
+
     // Unlike types, constant expressions need to be proceeded after dependencies
     status_t err = func(this);
     if (err != OK) return err;
@@ -481,6 +500,13 @@
         if (err != OK) return err;
     }
 
+    for (const auto* nextRef : getReferences()) {
+        const auto* nextCE = nextRef->shallowGet()->constExpr();
+        CHECK(nextCE != nullptr) << "Local identifier is not a constant expression";
+        status_t err = nextCE->recursivePass(func, visited);
+        if (err != OK) return err;
+    }
+
     // Unlike types, constant expressions need to be proceeded after dependencies
     status_t err = func(this);
     if (err != OK) return err;
@@ -488,6 +514,60 @@
     return OK;
 }
 
+ConstantExpression::CheckAcyclicStatus::CheckAcyclicStatus(status_t status,
+                                                           const ConstantExpression* cycleEnd)
+    : status(status), cycleEnd(cycleEnd) {
+    CHECK(cycleEnd == nullptr || status != OK);
+}
+
+ConstantExpression::CheckAcyclicStatus ConstantExpression::checkAcyclic(
+    std::unordered_set<const ConstantExpression*>* visited,
+    std::unordered_set<const ConstantExpression*>* stack) const {
+    if (stack->find(this) != stack->end()) {
+        std::cerr << "ERROR: Cyclic declaration:\n";
+        return CheckAcyclicStatus(UNKNOWN_ERROR, this);
+    }
+
+    if (visited->find(this) != visited->end()) return CheckAcyclicStatus(OK);
+    visited->insert(this);
+    stack->insert(this);
+
+    for (const auto* nextCE : getConstantExpressions()) {
+        auto err = nextCE->checkAcyclic(visited, stack);
+        if (err.status != OK) {
+            return err;
+        }
+    }
+
+    for (const auto* nextRef : getReferences()) {
+        const auto* nextCE = nextRef->shallowGet()->constExpr();
+        CHECK(nextCE != nullptr) << "Local identifier is not a constant expression";
+        auto err = nextCE->checkAcyclic(visited, stack);
+
+        if (err.status != OK) {
+            if (err.cycleEnd == nullptr) return err;
+
+            // Only ReferenceConstantExpression has references,
+            // mExpr is defined explicitly before evaluation
+
+            std::cerr << "  ";
+            if (!mTrivialDescription) {
+                std::cerr << "  '" << mExpr << "' ";
+            };
+            std::cerr << "at " << nextRef->location() << "\n";
+
+            if (err.cycleEnd == this) {
+                return CheckAcyclicStatus(err.status);
+            }
+            return err;
+        }
+    }
+
+    CHECK(stack->find(this) != stack->end());
+    stack->erase(this);
+    return CheckAcyclicStatus(OK);
+}
+
 void ConstantExpression::setPostParseCompleted() {
     CHECK(!mIsPostParseCompleted);
     mIsPostParseCompleted = true;
@@ -529,8 +609,12 @@
 }
 
 std::vector<const ConstantExpression*> ReferenceConstantExpression::getConstantExpressions() const {
-    CHECK(mReference->constExpr() != nullptr);
-    return {mReference->constExpr()};
+    // Returns reference instead
+    return {};
+}
+
+std::vector<const Reference<LocalIdentifier>*> ReferenceConstantExpression::getReferences() const {
+    return {&mReference};
 }
 
 /*