Improve type validation for root() functions.

Change-Id: I16af906106d4b38966e37534703f959f7d6c956c
diff --git a/slang_rs_root.cpp b/slang_rs_root.cpp
index 6391739..2309a8d 100644
--- a/slang_rs_root.cpp
+++ b/slang_rs_root.cpp
@@ -50,7 +50,8 @@
   const clang::ASTContext &C = FD->getASTContext();
 
   if (isRootRSFunc(FD)) {
-    if (FD->getNumParams() == 0) {
+    unsigned int numParams = FD->getNumParams();
+    if (numParams == 0) {
       // Graphics root function, so verify that it returns an int
       if (FD->getResultType().getCanonicalType() != C.IntTy) {
         Diags->Report(
@@ -72,10 +73,71 @@
       }
 
       // Validate remaining parameter types
-      // TODO(srhines)
+      const clang::ParmVarDecl *tooManyParams = NULL;
+      for (unsigned int i = 0; i < numParams; i++) {
+        const clang::ParmVarDecl *PVD = FD->getParamDecl(i);
+        clang::QualType QT = PVD->getType().getCanonicalType();
+        switch (i) {
+          case 0:     // const T1 *ain
+          case 2: {   // const T3 *usrData
+            if (!QT->isPointerType() ||
+                !QT->getPointeeType().isConstQualified()) {
+              Diags->Report(
+                  clang::FullSourceLoc(PVD->getLocation(),
+                                       Diags->getSourceManager()),
+                  Diags->getCustomDiagID(clang::Diagnostic::Error,
+                                         "compute root() parameter must be a "
+                                         "const pointer type"));
+              valid = false;
+            }
+            break;
+          }
+          case 1: {   // T2 *aout
+            if (!QT->isPointerType()) {
+              Diags->Report(
+                  clang::FullSourceLoc(PVD->getLocation(),
+                                       Diags->getSourceManager()),
+                  Diags->getCustomDiagID(clang::Diagnostic::Error,
+                                         "compute root() parameter must be a "
+                                         "pointer type"));
+              valid = false;
+            }
+            break;
+          }
+          case 3:     // unsigned int x
+          case 4:     // unsigned int y
+          case 5:     // unsigned int z
+          case 6: {   // unsigned int ar
+            if (QT.getUnqualifiedType() != C.UnsignedIntTy) {
+              Diags->Report(
+                  clang::FullSourceLoc(PVD->getLocation(),
+                                       Diags->getSourceManager()),
+                  Diags->getCustomDiagID(clang::Diagnostic::Error,
+                                         "compute root() parameter must be a "
+                                         "uint32_t type"));
+              valid = false;
+            }
+            break;
+          }
+          default: {
+            if (!tooManyParams) {
+              tooManyParams = PVD;
+            }
+            break;
+          }
+        }
+      }
+      if (tooManyParams) {
+        Diags->Report(
+            clang::FullSourceLoc(tooManyParams->getLocation(),
+                                 Diags->getSourceManager()),
+            Diags->getCustomDiagID(clang::Diagnostic::Error,
+                                   "too many compute root() parameters "
+                                   "specified"));
+        valid = false;
+      }
     }
-  }
-  else if (isInitRSFunc(FD)) {
+  } else if (isInitRSFunc(FD)) {
     if (FD->getNumParams() != 0) {
       Diags->Report(
           clang::FullSourceLoc(FD->getLocation(), Diags->getSourceManager()),
@@ -93,8 +155,7 @@
                                  "return type"));
       valid = false;
     }
-  }
-  else {
+  } else {
     slangAssert(false && "must be called on init or root function!");
   }
 
diff --git a/slang_rs_root.h b/slang_rs_root.h
index 235181b..1c7efa5 100644
--- a/slang_rs_root.h
+++ b/slang_rs_root.h
@@ -24,8 +24,6 @@
 
 #include "slang_assert.h"
 #include "slang_rs_context.h"
-//#include "slang_rs_export_type.h"
-//#include "slang_rs_exportable.h"
 
 namespace clang {
   class FunctionDecl;
@@ -36,8 +34,6 @@
 // Base class for handling root() functions (including reflection of
 // control-side code for issuing rsForEach).
 class RSRoot {
-  //friend class RSContext;
-
  private:
   std::string mName;
   std::string mMangledName;
@@ -90,10 +86,8 @@
 
   static bool validateSpecialFuncDecl(clang::Diagnostic *Diags,
                                       const clang::FunctionDecl *FD);
-
 };  // RSRoot
 
-
 }  // namespace slang
 
 #endif  // _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_ROOT_H_  NOLINT
diff --git a/tests/F_root_compute/root_compute.rs b/tests/F_root_compute/root_compute.rs
deleted file mode 100644
index f07de57..0000000
--- a/tests/F_root_compute/root_compute.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma version(1)
-#pragma rs java_package_name(foo)
-
-int root(int ain, int aout, void *usrData) {
-    return 10;
-}
diff --git a/tests/F_root_compute/stderr.txt.expect b/tests/F_root_compute/stderr.txt.expect
deleted file mode 100644
index 681d14b..0000000
--- a/tests/F_root_compute/stderr.txt.expect
+++ /dev/null
@@ -1 +0,0 @@
-root_compute.rs:4:5: error: compute root() is required to return a void type
diff --git a/tests/F_root_compute_non_const_ain/root_compute_non_const_ain.rs b/tests/F_root_compute_non_const_ain/root_compute_non_const_ain.rs
new file mode 100644
index 0000000..5cbf52b
--- /dev/null
+++ b/tests/F_root_compute_non_const_ain/root_compute_non_const_ain.rs
@@ -0,0 +1,5 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(int *ain) {
+}
diff --git a/tests/F_root_compute_non_const_ain/stderr.txt.expect b/tests/F_root_compute_non_const_ain/stderr.txt.expect
new file mode 100644
index 0000000..539e033
--- /dev/null
+++ b/tests/F_root_compute_non_const_ain/stderr.txt.expect
@@ -0,0 +1 @@
+root_compute_non_const_ain.rs:4:16: error: compute root() parameter must be a const pointer type
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_non_const_ain/stdout.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/F_root_compute_non_const_ain/stdout.txt.expect
diff --git a/tests/F_root_compute_non_const_non_ptr_ain/root_compute_non_const_non_ptr_ain.rs b/tests/F_root_compute_non_const_non_ptr_ain/root_compute_non_const_non_ptr_ain.rs
new file mode 100644
index 0000000..364b7c8
--- /dev/null
+++ b/tests/F_root_compute_non_const_non_ptr_ain/root_compute_non_const_non_ptr_ain.rs
@@ -0,0 +1,5 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(int ain) {
+}
diff --git a/tests/F_root_compute_non_const_non_ptr_ain/stderr.txt.expect b/tests/F_root_compute_non_const_non_ptr_ain/stderr.txt.expect
new file mode 100644
index 0000000..f774ed2
--- /dev/null
+++ b/tests/F_root_compute_non_const_non_ptr_ain/stderr.txt.expect
@@ -0,0 +1 @@
+root_compute_non_const_non_ptr_ain.rs:4:15: error: compute root() parameter must be a const pointer type
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_non_const_non_ptr_ain/stdout.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/F_root_compute_non_const_non_ptr_ain/stdout.txt.expect
diff --git a/tests/F_root_compute_non_const_usrData/root_compute_non_const_usrData.rs b/tests/F_root_compute_non_const_usrData/root_compute_non_const_usrData.rs
new file mode 100644
index 0000000..46dc642
--- /dev/null
+++ b/tests/F_root_compute_non_const_usrData/root_compute_non_const_usrData.rs
@@ -0,0 +1,5 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int *ain, int *aout, void *usrData) {
+}
diff --git a/tests/F_root_compute_non_const_usrData/stderr.txt.expect b/tests/F_root_compute_non_const_usrData/stderr.txt.expect
new file mode 100644
index 0000000..053353d
--- /dev/null
+++ b/tests/F_root_compute_non_const_usrData/stderr.txt.expect
@@ -0,0 +1 @@
+root_compute_non_const_usrData.rs:4:44: error: compute root() parameter must be a const pointer type
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_non_const_usrData/stdout.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/F_root_compute_non_const_usrData/stdout.txt.expect
diff --git a/tests/F_root_compute_non_ptr_ain/root_compute_non_ptr_ain.rs b/tests/F_root_compute_non_ptr_ain/root_compute_non_ptr_ain.rs
new file mode 100644
index 0000000..a311d47
--- /dev/null
+++ b/tests/F_root_compute_non_ptr_ain/root_compute_non_ptr_ain.rs
@@ -0,0 +1,5 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int ain) {
+}
diff --git a/tests/F_root_compute_non_ptr_ain/stderr.txt.expect b/tests/F_root_compute_non_ptr_ain/stderr.txt.expect
new file mode 100644
index 0000000..3ffb17a
--- /dev/null
+++ b/tests/F_root_compute_non_ptr_ain/stderr.txt.expect
@@ -0,0 +1 @@
+root_compute_non_ptr_ain.rs:4:21: error: compute root() parameter must be a const pointer type
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_non_ptr_ain/stdout.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/F_root_compute_non_ptr_ain/stdout.txt.expect
diff --git a/tests/F_root_compute_non_ptr_aout/root_compute_non_ptr_aout.rs b/tests/F_root_compute_non_ptr_aout/root_compute_non_ptr_aout.rs
new file mode 100644
index 0000000..351e335
--- /dev/null
+++ b/tests/F_root_compute_non_ptr_aout/root_compute_non_ptr_aout.rs
@@ -0,0 +1,5 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int *ain, int aout) {
+}
diff --git a/tests/F_root_compute_non_ptr_aout/stderr.txt.expect b/tests/F_root_compute_non_ptr_aout/stderr.txt.expect
new file mode 100644
index 0000000..0d256c0
--- /dev/null
+++ b/tests/F_root_compute_non_ptr_aout/stderr.txt.expect
@@ -0,0 +1 @@
+root_compute_non_ptr_aout.rs:4:31: error: compute root() parameter must be a pointer type
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_non_ptr_aout/stdout.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/F_root_compute_non_ptr_aout/stdout.txt.expect
diff --git a/tests/F_root_compute_non_ptr_usrData/root_compute_non_ptr_usrData.rs b/tests/F_root_compute_non_ptr_usrData/root_compute_non_ptr_usrData.rs
new file mode 100644
index 0000000..ce67f8a
--- /dev/null
+++ b/tests/F_root_compute_non_ptr_usrData/root_compute_non_ptr_usrData.rs
@@ -0,0 +1,5 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int *ain, int *aout, const int usrData) {
+}
diff --git a/tests/F_root_compute_non_ptr_usrData/stderr.txt.expect b/tests/F_root_compute_non_ptr_usrData/stderr.txt.expect
new file mode 100644
index 0000000..44940b8
--- /dev/null
+++ b/tests/F_root_compute_non_ptr_usrData/stderr.txt.expect
@@ -0,0 +1 @@
+root_compute_non_ptr_usrData.rs:4:48: error: compute root() parameter must be a const pointer type
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_non_ptr_usrData/stdout.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/F_root_compute_non_ptr_usrData/stdout.txt.expect
diff --git a/tests/F_root_compute_non_uint32_t_xyzar/root_compute_non_uint32_t_xyzar.rs b/tests/F_root_compute_non_uint32_t_xyzar/root_compute_non_uint32_t_xyzar.rs
new file mode 100644
index 0000000..94360b8
--- /dev/null
+++ b/tests/F_root_compute_non_uint32_t_xyzar/root_compute_non_uint32_t_xyzar.rs
@@ -0,0 +1,6 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int *ain, int *aout, const void *usrData,
+          int x, float y, double z, uchar ar) {
+}
diff --git a/tests/F_root_compute_non_uint32_t_xyzar/stderr.txt.expect b/tests/F_root_compute_non_uint32_t_xyzar/stderr.txt.expect
new file mode 100644
index 0000000..beaadfc
--- /dev/null
+++ b/tests/F_root_compute_non_uint32_t_xyzar/stderr.txt.expect
@@ -0,0 +1,4 @@
+root_compute_non_uint32_t_xyzar.rs:5:15: error: compute root() parameter must be a uint32_t type
+root_compute_non_uint32_t_xyzar.rs:5:24: error: compute root() parameter must be a uint32_t type
+root_compute_non_uint32_t_xyzar.rs:5:34: error: compute root() parameter must be a uint32_t type
+root_compute_non_uint32_t_xyzar.rs:5:43: error: compute root() parameter must be a uint32_t type
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_non_uint32_t_xyzar/stdout.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/F_root_compute_non_uint32_t_xyzar/stdout.txt.expect
diff --git a/tests/F_root_compute_non_void_ret/root_compute_non_void_ret.rs b/tests/F_root_compute_non_void_ret/root_compute_non_void_ret.rs
new file mode 100644
index 0000000..fe0c3cc
--- /dev/null
+++ b/tests/F_root_compute_non_void_ret/root_compute_non_void_ret.rs
@@ -0,0 +1,6 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int root(const int *ain, int *aout, const void *usrData) {
+    return 10;
+}
diff --git a/tests/F_root_compute_non_void_ret/stderr.txt.expect b/tests/F_root_compute_non_void_ret/stderr.txt.expect
new file mode 100644
index 0000000..f7eaec7
--- /dev/null
+++ b/tests/F_root_compute_non_void_ret/stderr.txt.expect
@@ -0,0 +1 @@
+root_compute_non_void_ret.rs:4:5: error: compute root() is required to return a void type
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_non_void_ret/stdout.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/F_root_compute_non_void_ret/stdout.txt.expect
diff --git a/tests/F_root_compute_really_bad/root_compute_really_bad.rs b/tests/F_root_compute_really_bad/root_compute_really_bad.rs
new file mode 100644
index 0000000..469575a
--- /dev/null
+++ b/tests/F_root_compute_really_bad/root_compute_really_bad.rs
@@ -0,0 +1,7 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int root(int ain, int aout, int usrData, float x, double y, uchar z, ushort ar,
+         uint32_t extra1, uint32_t extra2) {
+    return 10;
+}
diff --git a/tests/F_root_compute_really_bad/stderr.txt.expect b/tests/F_root_compute_really_bad/stderr.txt.expect
new file mode 100644
index 0000000..7babf29
--- /dev/null
+++ b/tests/F_root_compute_really_bad/stderr.txt.expect
@@ -0,0 +1,9 @@
+root_compute_really_bad.rs:4:5: error: compute root() is required to return a void type
+root_compute_really_bad.rs:4:14: error: compute root() parameter must be a const pointer type
+root_compute_really_bad.rs:4:23: error: compute root() parameter must be a pointer type
+root_compute_really_bad.rs:4:33: error: compute root() parameter must be a const pointer type
+root_compute_really_bad.rs:4:48: error: compute root() parameter must be a uint32_t type
+root_compute_really_bad.rs:4:58: error: compute root() parameter must be a uint32_t type
+root_compute_really_bad.rs:4:67: error: compute root() parameter must be a uint32_t type
+root_compute_really_bad.rs:4:77: error: compute root() parameter must be a uint32_t type
+root_compute_really_bad.rs:5:19: error: too many compute root() parameters specified
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_really_bad/stdout.txt.expect
similarity index 100%
rename from tests/F_root_compute/stdout.txt.expect
rename to tests/F_root_compute_really_bad/stdout.txt.expect
diff --git a/tests/F_root_compute_too_many_args/root_compute_too_many_args.rs b/tests/F_root_compute_too_many_args/root_compute_too_many_args.rs
new file mode 100644
index 0000000..655fba3
--- /dev/null
+++ b/tests/F_root_compute_too_many_args/root_compute_too_many_args.rs
@@ -0,0 +1,7 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int *ain, int *aout, const void *usrData,
+          uint32_t x, uint32_t y, uint32_t z, uint32_t ar,
+          uint32_t extra1, uint32_t extra2) {
+}
diff --git a/tests/F_root_compute_too_many_args/stderr.txt.expect b/tests/F_root_compute_too_many_args/stderr.txt.expect
new file mode 100644
index 0000000..78b3266
--- /dev/null
+++ b/tests/F_root_compute_too_many_args/stderr.txt.expect
@@ -0,0 +1 @@
+root_compute_too_many_args.rs:6:20: error: too many compute root() parameters specified
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/F_root_compute_too_many_args/stdout.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/F_root_compute_too_many_args/stdout.txt.expect
diff --git a/tests/P_root_compute/root_compute.rs b/tests/P_root_compute/root_compute.rs
new file mode 100644
index 0000000..a7c6ffd
--- /dev/null
+++ b/tests/P_root_compute/root_compute.rs
@@ -0,0 +1,6 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int *ain, int *aout, const void *usrData,
+          uint32_t x, uint32_t y, uint32_t z, uint32_t ar) {
+}
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/P_root_compute/stderr.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/P_root_compute/stderr.txt.expect
diff --git a/tests/P_root_compute/stdout.txt.expect b/tests/P_root_compute/stdout.txt.expect
new file mode 100644
index 0000000..acc7d56
--- /dev/null
+++ b/tests/P_root_compute/stdout.txt.expect
@@ -0,0 +1 @@
+Generating ScriptC_root_compute.java ...
diff --git a/tests/P_root_compute_non_void_ptr_usrData/root_compute_non_void_ptr_usrData.rs b/tests/P_root_compute_non_void_ptr_usrData/root_compute_non_void_ptr_usrData.rs
new file mode 100644
index 0000000..cf7f318
--- /dev/null
+++ b/tests/P_root_compute_non_void_ptr_usrData/root_compute_non_void_ptr_usrData.rs
@@ -0,0 +1,5 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void root(const int *ain, int *aout, const int *usrData) {
+}
diff --git a/tests/F_root_compute/stdout.txt.expect b/tests/P_root_compute_non_void_ptr_usrData/stderr.txt.expect
similarity index 100%
copy from tests/F_root_compute/stdout.txt.expect
copy to tests/P_root_compute_non_void_ptr_usrData/stderr.txt.expect
diff --git a/tests/P_root_compute_non_void_ptr_usrData/stdout.txt.expect b/tests/P_root_compute_non_void_ptr_usrData/stdout.txt.expect
new file mode 100644
index 0000000..25a9ab9
--- /dev/null
+++ b/tests/P_root_compute_non_void_ptr_usrData/stdout.txt.expect
@@ -0,0 +1 @@
+Generating ScriptC_root_compute_non_void_ptr_usrData.java ...