Don't allow user-defined uninitialized const variables to be exported.

Bug: 24607459

User-defined variables that are both uninitialized and const can never
be linked/initialized properly. We limit llvm-rs-cc to only accepting
extern const variables defined in its own headers (such that a proper
initialization value can be provided by the runtime library). This will
allow us to make constants that lldb can refer to from user commands,
since the extern const symbols will remain in the final shared object.

Change-Id: I44dbd680278af637c12868e93487745f78936ed0
diff --git a/slang_rs_context.cpp b/slang_rs_context.cpp
index 6b07804..bc674a0 100644
--- a/slang_rs_context.cpp
+++ b/slang_rs_context.cpp
@@ -77,8 +77,6 @@
 bool RSContext::processExportVar(const clang::VarDecl *VD) {
   slangAssert(!VD->getName().empty() && "Variable name should not be empty");
 
-  // TODO(zonr): some check on variable
-
   RSExportType *ET = RSExportType::CreateFromDecl(this, VD);
   if (!ET)
     return false;
@@ -231,8 +229,25 @@
     switch (D->getKind()) {
     case clang::Decl::Var: {
       clang::VarDecl* VD = llvm::dyn_cast<clang::VarDecl>(D);
+      bool ShouldExportVariable = true;
       if (VD->getFormalLinkage() == clang::ExternalLinkage) {
-        if (!processExportVar(VD)) {
+        clang::QualType QT = VD->getTypeSourceInfo()->getType();
+        if (QT.isConstQualified() && !VD->hasInit()) {
+          if (Slang::IsLocInRSHeaderFile(VD->getLocation(),
+                                         *getSourceManager())) {
+            // We don't export variables internal to the runtime's
+            // implementation.
+            ShouldExportVariable = false;
+          } else {
+            clang::DiagnosticsEngine *DiagEngine = getDiagnostics();
+            DiagEngine->Report(VD->getLocation(), DiagEngine->getCustomDiagID(
+                clang::DiagnosticsEngine::Error,
+                "invalid declaration of uninitialized constant variable '%0'"))
+              << VD->getName();
+            valid = false;
+          }
+        }
+        if (valid && ShouldExportVariable && !processExportVar(VD)) {
           valid = false;
         }
       }
diff --git a/tests/F_extern_const/extern_const.rs b/tests/F_extern_const/extern_const.rs
new file mode 100644
index 0000000..bf630de
--- /dev/null
+++ b/tests/F_extern_const/extern_const.rs
@@ -0,0 +1,4 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+extern const int extern_const_int;
diff --git a/tests/F_extern_const/stderr.txt.expect b/tests/F_extern_const/stderr.txt.expect
new file mode 100644
index 0000000..567c9a5
--- /dev/null
+++ b/tests/F_extern_const/stderr.txt.expect
@@ -0,0 +1 @@
+extern_const.rs:4:18: error: invalid declaration of uninitialized constant variable 'extern_const_int'
diff --git a/tests/F_extern_const/stdout.txt.expect b/tests/F_extern_const/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_extern_const/stdout.txt.expect
diff --git a/tests/P_constant/constant.rs b/tests/P_constant/constant.rs
index 6f98ff1..4457e37 100644
--- a/tests/P_constant/constant.rs
+++ b/tests/P_constant/constant.rs
@@ -18,3 +18,5 @@
 
 const bool boolTest = true;
 
+extern const int i = 5;
+
diff --git a/tests/P_constant/stderr.txt.expect b/tests/P_constant/stderr.txt.expect
index e69de29..3728bee 100644
--- a/tests/P_constant/stderr.txt.expect
+++ b/tests/P_constant/stderr.txt.expect
@@ -0,0 +1 @@
+constant.rs:21:18: warning: 'extern' variable has an initializer
diff --git a/tests/P_extern_const/extern_const.rs b/tests/P_extern_const/extern_const.rs
new file mode 100644
index 0000000..e80906a
--- /dev/null
+++ b/tests/P_extern_const/extern_const.rs
@@ -0,0 +1,7 @@
+// -I .
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int __attribute__((kernel)) foo() {
+    return my_extern_const_in_header;
+}
diff --git a/tests/P_extern_const/rs_core.rsh b/tests/P_extern_const/rs_core.rsh
new file mode 100644
index 0000000..4332ed0
--- /dev/null
+++ b/tests/P_extern_const/rs_core.rsh
@@ -0,0 +1,8 @@
+// Fake rs_core.rsh header file
+//
+// We use -I . to pick up this header file implicitly, instead of the proper
+// rs_core.rsh header file.
+
+// Declare an uninitialized external constant, which should be ok for our
+// official header files.
+extern const int my_extern_const_in_header;
diff --git a/tests/P_extern_const/stderr.txt.expect b/tests/P_extern_const/stderr.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/P_extern_const/stderr.txt.expect
diff --git a/tests/P_extern_const/stdout.txt.expect b/tests/P_extern_const/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/P_extern_const/stdout.txt.expect