using references in constant expressions

References to enumerators or other constants can be used in constant
expressions. This enables setting defaults to enum type variables.

References are resolved at compilation time.

Note that recursive references are not supported yet.

Bug: 142893595
Test: aidl_unittests
Test: aidl_integration_test
Change-Id: Ic7d7e74c1306462fa9148d474c764ebb9a5fae45
diff --git a/aidl_const_expressions.cpp b/aidl_const_expressions.cpp
index a3d1a01..423bfca 100644
--- a/aidl_const_expressions.cpp
+++ b/aidl_const_expressions.cpp
@@ -494,6 +494,23 @@
     AIDL_ERROR(this) << "Invalid constant value: " + value_;
     return "";
   }
+
+  const AidlDefinedType* defined_type = type.GetDefinedType();
+  if (defined_type && !type.IsArray()) {
+    const AidlEnumDeclaration* enum_type = defined_type->AsEnumDeclaration();
+    if (!enum_type) {
+      AIDL_ERROR(this) << "Invalid type (" << defined_type->GetCanonicalName()
+                       << ") for a const value(" << value_ << ")";
+      return "";
+    }
+    if (type_ != Type::REF) {
+      AIDL_ERROR(this) << "Invalid value (" << value_ << ") for enum "
+                       << enum_type->GetCanonicalName();
+      return "";
+    }
+    return decorator(type, value_);
+  }
+
   const string& type_string = type.GetName();
   int err = 0;
 
@@ -607,6 +624,7 @@
     case Type::ARRAY:      // fall-through
     case Type::CHARACTER:  // fall-through
     case Type::STRING:     // fall-through
+    case Type::REF:        // fall-through
     case Type::FLOATING:   // fall-through
     case Type::UNARY:      // fall-through
     case Type::BINARY:
@@ -714,6 +732,8 @@
       return "a literal char";
     case Type::STRING:
       return "a literal string";
+    case Type::REF:
+      return "a reference";
     case Type::FLOATING:
       return "a literal float";
     case Type::UNARY:
@@ -730,6 +750,106 @@
   }
 }
 
+AidlConstantReference::AidlConstantReference(const AidlLocation& location, const std::string& value,
+                                             const std::string& comments)
+    : AidlConstantValue(location, Type::REF, value), comments_(comments) {
+  const auto pos = value.find_last_of('.');
+  if (pos == string::npos) {
+    field_name_ = value;
+  } else {
+    ref_type_ =
+        std::make_unique<AidlTypeSpecifier>(location, value.substr(0, pos), false, nullptr, "");
+    field_name_ = value.substr(pos + 1);
+  }
+}
+
+bool AidlConstantReference::CheckValid() const {
+  if (is_evaluated_) return is_valid_;
+
+  if (!GetRefType() || !GetRefType()->GetDefinedType()) {
+    // This can happen when "const reference" is used in an unsupported way,
+    // but missed in checks there. It works as a safety net.
+    AIDL_ERROR(*this) << "Can't resolve the reference (" << value_ << ")";
+    is_valid_ = false;
+    return false;
+  }
+
+  auto defined_type = GetRefType()->GetDefinedType();
+  if (auto enum_decl = defined_type->AsEnumDeclaration(); enum_decl) {
+    for (const auto& e : enum_decl->GetEnumerators()) {
+      if (e->GetName() == field_name_) {
+        is_valid_ = !e->GetValue() || e->GetValue()->CheckValid();
+        return is_valid_;
+      }
+    }
+  } else {
+    for (const auto& c : defined_type->GetConstantDeclarations()) {
+      if (c->GetName() == field_name_) {
+        is_valid_ = c->GetValue().CheckValid();
+        return is_valid_;
+      }
+    }
+  }
+  AIDL_ERROR(*this) << "Can't find " << field_name_ << " in " << ref_type_->GetName();
+  is_valid_ = false;
+  return false;
+}
+
+bool AidlConstantReference::evaluate(const AidlTypeSpecifier& type) const {
+  if (is_evaluated_) return is_valid_;
+  is_evaluated_ = true;
+
+  const AidlDefinedType* view_type = type.GetDefinedType();
+  if (view_type) {
+    auto enum_decl = view_type->AsEnumDeclaration();
+    if (!enum_decl) {
+      AIDL_ERROR(type) << "Can't refer to a constant expression: " << value_;
+      return false;
+    }
+  }
+
+  auto defined_type = GetRefType()->GetDefinedType();
+  if (auto enum_decl = defined_type->AsEnumDeclaration(); enum_decl) {
+    for (const auto& e : enum_decl->GetEnumerators()) {
+      if (e->GetName() == field_name_) {
+        if (e->GetValue()->evaluate(type)) {
+          is_valid_ = e->GetValue()->is_valid_;
+          if (is_valid_) {
+            final_type_ = e->GetValue()->final_type_;
+            if (final_type_ == Type::STRING) {
+              final_string_value_ = e->GetValue()->final_string_value_;
+            } else {
+              final_value_ = e->GetValue()->final_value_;
+            }
+            return true;
+          }
+        }
+        break;
+      }
+    }
+  } else {
+    for (const auto& c : defined_type->GetConstantDeclarations()) {
+      if (c->GetName() == field_name_) {
+        if (c->GetValue().evaluate(type)) {
+          is_valid_ = c->GetValue().is_valid_;
+          if (is_valid_) {
+            final_type_ = c->GetValue().final_type_;
+            if (final_type_ == Type::STRING) {
+              final_string_value_ = c->GetValue().final_string_value_;
+            } else {
+              final_value_ = c->GetValue().final_value_;
+            }
+            return true;
+          }
+        }
+      }
+    }
+  }
+
+  is_valid_ = false;
+  return false;
+}
+
 bool AidlUnaryConstExpression::CheckValid() const {
   if (is_evaluated_) return is_valid_;
   AIDL_FATAL_IF(unary_ == nullptr, this);