am 11274a73: Validate FS (and RS) ASTs.

* commit '11274a7324b478ec13e1d10a1b81350b34a65ab1':
  Validate FS (and RS) ASTs.
diff --git a/Android.mk b/Android.mk
index 748665a..da28351 100644
--- a/Android.mk
+++ b/Android.mk
@@ -231,6 +231,7 @@
 	llvm-rs-cc.cpp	\
 	slang_rs.cpp	\
 	slang_rs_ast_replace.cpp	\
+	slang_rs_check_ast.cpp	\
 	slang_rs_context.cpp	\
 	slang_rs_pragma_handler.cpp	\
 	slang_rs_backend.cpp	\
diff --git a/slang_rs.cpp b/slang_rs.cpp
index af86e78..98b9771 100644
--- a/slang_rs.cpp
+++ b/slang_rs.cpp
@@ -40,6 +40,8 @@
 
 namespace slang {
 
+#define FS_SUFFIX  "fs"
+
 #define RS_HEADER_SUFFIX  "rsh"
 
 /* RS_HEADER_ENTRY(name) */
@@ -61,6 +63,15 @@
   RS_HEADER_ENTRY(rs_time) \
   RS_HEADER_ENTRY(rs_types) \
 
+// Returns true if \p Filename ends in ".fs".
+bool SlangRS::isFilterscript(const char *Filename) {
+  const char *c = strrchr(Filename, '.');
+  if (c && !strncmp(FS_SUFFIX, c + 1, strlen(FS_SUFFIX) + 1)) {
+    return true;
+  } else {
+    return false;
+  }
+}
 
 bool SlangRS::reflectToJava(const std::string &OutputPathBase,
                             const std::string &OutputPackageName,
@@ -233,7 +244,8 @@
                          OS,
                          OT,
                          getSourceManager(),
-                         mAllowRSPrefix);
+                         mAllowRSPrefix,
+                         mIsFilterscript);
 }
 
 bool SlangRS::IsRSHeaderFile(const char *File) {
@@ -245,9 +257,9 @@
   return false;
 }
 
-bool SlangRS::IsFunctionInRSHeaderFile(const clang::FunctionDecl *FD,
-                                       const clang::SourceManager &SourceMgr) {
-  clang::FullSourceLoc FSL(FD->getLocStart(), SourceMgr);
+bool SlangRS::IsLocInRSHeaderFile(const clang::SourceLocation &Loc,
+                                  const clang::SourceManager &SourceMgr) {
+  clang::FullSourceLoc FSL(Loc, SourceMgr);
   clang::PresumedLoc PLoc = SourceMgr.getPresumedLoc(FSL);
 
   const char *Filename = PLoc.getFilename();
@@ -259,7 +271,8 @@
 }
 
 SlangRS::SlangRS()
-  : Slang(), mRSContext(NULL), mAllowRSPrefix(false), mTargetAPI(0) {
+  : Slang(), mRSContext(NULL), mAllowRSPrefix(false), mTargetAPI(0),
+    mIsFilterscript(false) {
 }
 
 bool SlangRS::compile(
@@ -329,6 +342,8 @@
           JavaReflectionPackageName);
     }
 
+    mIsFilterscript = isFilterscript(InputFile);
+
     if (Slang::compile() > 0)
       return false;
 
diff --git a/slang_rs.h b/slang_rs.h
index 571f1fa..8ad70de 100644
--- a/slang_rs.h
+++ b/slang_rs.h
@@ -29,10 +29,6 @@
 #include "slang_rs_reflect_utils.h"
 #include "slang_version.h"
 
-namespace clang {
-  class FunctionDecl;
-}
-
 namespace slang {
   class RSContext;
   class RSExportRecordType;
@@ -46,6 +42,8 @@
 
   unsigned int mTargetAPI;
 
+  bool mIsFilterscript;
+
   // Custom diagnostic identifiers
   unsigned mDiagErrorInvalidOutputDepParameter;
   unsigned mDiagErrorODR;
@@ -77,6 +75,9 @@
   // and is valid before compile() ends.
   bool checkODR(const char *CurInputFile);
 
+  // Returns true if this is a Filterscript file.
+  static bool isFilterscript(const char *Filename);
+
  protected:
   virtual void initDiagnostic();
   virtual void initPreprocessor();
@@ -90,11 +91,11 @@
 
  public:
   static bool IsRSHeaderFile(const char *File);
-  // FIXME: Determine whether a function is in RS header (i.e., one of the RS
+  // FIXME: Determine whether a location is in RS header (i.e., one of the RS
   //        built-in APIs) should only need its names (we need a "list" of RS
   //        built-in APIs).
-  static bool IsFunctionInRSHeaderFile(const clang::FunctionDecl *FD,
-                                       const clang::SourceManager &SourceMgr);
+  static bool IsLocInRSHeaderFile(const clang::SourceLocation &Loc,
+                                  const clang::SourceManager &SourceMgr);
 
   SlangRS();
 
diff --git a/slang_rs_backend.cpp b/slang_rs_backend.cpp
index a1efad1..c643908 100644
--- a/slang_rs_backend.cpp
+++ b/slang_rs_backend.cpp
@@ -54,25 +54,29 @@
                      llvm::raw_ostream *OS,
                      Slang::OutputType OT,
                      clang::SourceManager &SourceMgr,
-                     bool AllowRSPrefix)
+                     bool AllowRSPrefix,
+                     bool IsFilterscript)
   : Backend(DiagEngine, CodeGenOpts, TargetOpts, Pragmas, OS, OT),
     mContext(Context),
     mSourceMgr(SourceMgr),
     mAllowRSPrefix(AllowRSPrefix),
+    mIsFilterscript(IsFilterscript),
     mExportVarMetadata(NULL),
     mExportFuncMetadata(NULL),
     mExportForEachNameMetadata(NULL),
     mExportForEachSignatureMetadata(NULL),
     mExportTypeMetadata(NULL),
     mRSObjectSlotsMetadata(NULL),
-    mRefCount(mContext->getASTContext()) {
+    mRefCount(mContext->getASTContext()),
+    mASTChecker(mContext->getASTContext(), mContext->getTargetAPI(),
+                IsFilterscript) {
 }
 
 // 1) Add zero initialization of local RS object types
 void RSBackend::AnnotateFunction(clang::FunctionDecl *FD) {
   if (FD &&
       FD->hasBody() &&
-      !SlangRS::IsFunctionInRSHeaderFile(FD, mSourceMgr)) {
+      !SlangRS::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr)) {
     mRefCount.Init();
     mRefCount.Visit(FD->getBody());
   }
@@ -90,7 +94,7 @@
         continue;
       if (!FD->getName().startswith("rs"))  // Check prefix
         continue;
-      if (!SlangRS::IsFunctionInRSHeaderFile(FD, mSourceMgr))
+      if (!SlangRS::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr))
         mDiagEngine.Report(
           clang::FullSourceLoc(FD->getLocation(), mSourceMgr),
           mDiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
@@ -125,53 +129,19 @@
   return Backend::HandleTopLevelDecl(D);
 }
 
-namespace {
-
-static bool ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI) {
-  if (!VD) {
-    return true;
-  }
-
-  clang::ASTContext &C = VD->getASTContext();
-  const clang::Type *T = VD->getType().getTypePtr();
-  bool valid = true;
-
-  if (VD->getLinkage() == clang::ExternalLinkage) {
-    llvm::StringRef TypeName;
-    if (!RSExportType::NormalizeType(T, TypeName, &C.getDiagnostics(), VD)) {
-      valid = false;
-    }
-  }
-  valid &= RSExportType::ValidateVarDecl(VD, TargetAPI);
-
-  return valid;
-}
-
-static bool ValidateASTContext(clang::ASTContext &C, unsigned int TargetAPI) {
-  bool valid = true;
-  clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
-  for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
-          DE = TUDecl->decls_end();
-       DI != DE;
-       DI++) {
-    clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*DI);
-    if (VD && !ValidateVarDecl(VD, TargetAPI)) {
-      valid = false;
-    }
-  }
-
-  return valid;
-}
-
-}  // namespace
 
 void RSBackend::HandleTranslationUnitPre(clang::ASTContext &C) {
   clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
 
-  if (!ValidateASTContext(C, getTargetAPI())) {
+  // If we have an invalid RS/FS AST, don't check further.
+  if (!mASTChecker.Validate()) {
     return;
   }
 
+  if (mIsFilterscript) {
+    mContext->addPragma("rs_fp_relaxed", "");
+  }
+
   int version = mContext->getVersion();
   if (version == 0) {
     // Not setting a version is an error
diff --git a/slang_rs_backend.h b/slang_rs_backend.h
index 0dc70e6..80c643d 100644
--- a/slang_rs_backend.h
+++ b/slang_rs_backend.h
@@ -19,6 +19,7 @@
 
 #include "slang_backend.h"
 #include "slang_pragma_recorder.h"
+#include "slang_rs_check_ast.h"
 #include "slang_rs_object_ref_count.h"
 
 namespace llvm {
@@ -47,6 +48,8 @@
 
   bool mAllowRSPrefix;
 
+  bool mIsFilterscript;
+
   llvm::NamedMDNode *mExportVarMetadata;
   llvm::NamedMDNode *mExportFuncMetadata;
   llvm::NamedMDNode *mExportForEachNameMetadata;
@@ -57,6 +60,8 @@
 
   RSObjectRefCount mRefCount;
 
+  RSCheckAST mASTChecker;
+
   void AnnotateFunction(clang::FunctionDecl *FD);
 
  protected:
@@ -79,7 +84,8 @@
             llvm::raw_ostream *OS,
             Slang::OutputType OT,
             clang::SourceManager &SourceMgr,
-            bool AllowRSPrefix);
+            bool AllowRSPrefix,
+            bool IsFilterscript);
 
   virtual ~RSBackend();
 };
diff --git a/slang_rs_check_ast.cpp b/slang_rs_check_ast.cpp
new file mode 100644
index 0000000..118bda7
--- /dev/null
+++ b/slang_rs_check_ast.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "slang_rs_check_ast.h"
+
+#include "slang_assert.h"
+#include "slang_rs.h"
+#include "slang_rs_export_type.h"
+
+namespace slang {
+
+void RSCheckAST::VisitStmt(clang::Stmt *S) {
+  // This function does the actual iteration through all sub-Stmt's within
+  // a given Stmt. Note that this function is skipped by all of the other
+  // Visit* functions if we have already found a higher-level match.
+  for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end();
+       I != E;
+       I++) {
+    if (clang::Stmt *Child = *I) {
+      Visit(Child);
+    }
+  }
+}
+
+void RSCheckAST::ValidateFunctionDecl(clang::FunctionDecl *FD) {
+  if (!FD) {
+    return;
+  }
+
+  if (!mIsFilterscript) {
+    // No additional validation for non-Filterscript functions.
+    if (clang::Stmt *Body = FD->getBody()) {
+      Visit(Body);
+    }
+    return;
+  }
+
+  size_t numParams = FD->getNumParams();
+
+  clang::QualType resultType = FD->getResultType().getCanonicalType();
+
+  // We use FD as our NamedDecl in the case of a bad return type.
+  if (!RSExportType::ValidateType(C, resultType, FD,
+                                  FD->getLocStart(), mTargetAPI,
+                                  mIsFilterscript)) {
+    mValid = false;
+  }
+
+  for (size_t i = 0; i < numParams; i++) {
+    clang::ParmVarDecl *PVD = FD->getParamDecl(i);
+    clang::QualType QT = PVD->getType().getCanonicalType();
+    if (!RSExportType::ValidateType(C, QT, PVD, PVD->getLocStart(),
+                                    mTargetAPI, mIsFilterscript)) {
+      mValid = false;
+    }
+  }
+
+  if (clang::Stmt *Body = FD->getBody()) {
+    Visit(Body);
+  }
+}
+
+
+void RSCheckAST::ValidateVarDecl(clang::VarDecl *VD) {
+  if (!VD) {
+    return;
+  }
+
+  const clang::Type *T = VD->getType().getTypePtr();
+
+  if (VD->getLinkage() == clang::ExternalLinkage) {
+    llvm::StringRef TypeName;
+    if (!RSExportType::NormalizeType(T, TypeName, &mDiagEngine, VD)) {
+      mValid = false;
+    }
+  }
+
+  if (!RSExportType::ValidateVarDecl(VD, mTargetAPI, mIsFilterscript)) {
+    mValid = false;
+  } else if (clang::Expr *Init = VD->getInit()) {
+    // Only check the initializer if the decl is already ok.
+    Visit(Init);
+  }
+}
+
+
+void RSCheckAST::VisitDeclStmt(clang::DeclStmt *DS) {
+  if (!SlangRS::IsLocInRSHeaderFile(DS->getLocStart(), mSM)) {
+    for (clang::DeclStmt::decl_iterator I = DS->decl_begin(),
+                                        E = DS->decl_end();
+         I != E;
+         ++I) {
+      if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*I)) {
+        ValidateVarDecl(VD);
+      } else if (clang::FunctionDecl *FD =
+            llvm::dyn_cast<clang::FunctionDecl>(*I)) {
+        ValidateFunctionDecl(FD);
+      }
+    }
+  }
+}
+
+
+void RSCheckAST::VisitExpr(clang::Expr *E) {
+  // This is where FS checks for code using pointer and/or 64-bit expressions
+  // (i.e. things like casts).
+
+  // First we skip implicit casts (things like function calls and explicit
+  // array accesses rely heavily on them and they are valid.
+  E = E->IgnoreImpCasts();
+  if (mIsFilterscript &&
+      !SlangRS::IsLocInRSHeaderFile(E->getExprLoc(), mSM) &&
+      !RSExportType::ValidateType(C, E->getType(), NULL, E->getExprLoc(),
+                                  mTargetAPI, mIsFilterscript)) {
+    mValid = false;
+  } else {
+    // Only visit sub-expressions if we haven't already seen a violation.
+    VisitStmt(E);
+  }
+}
+
+
+bool RSCheckAST::Validate() {
+  clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
+  for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
+          DE = TUDecl->decls_end();
+       DI != DE;
+       DI++) {
+    if (!SlangRS::IsLocInRSHeaderFile(DI->getLocStart(), mSM)) {
+      if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*DI)) {
+        ValidateVarDecl(VD);
+      } else if (clang::FunctionDecl *FD =
+            llvm::dyn_cast<clang::FunctionDecl>(*DI)) {
+        ValidateFunctionDecl(FD);
+      } else if (clang::Stmt *Body = (*DI)->getBody()) {
+        Visit(Body);
+      }
+    }
+  }
+
+  return mValid;
+}
+
+}  // namespace slang
diff --git a/slang_rs_check_ast.h b/slang_rs_check_ast.h
new file mode 100644
index 0000000..6a76360
--- /dev/null
+++ b/slang_rs_check_ast.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_CHECK_AST_H_  // NOLINT
+#define _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_CHECK_AST_H_
+
+#include "slang_assert.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/StmtVisitor.h"
+
+namespace slang {
+
+// This class is designed to walk a Renderscript/Filterscript AST looking for
+// violations. Examples of violations for FS are pointer declarations and
+// casts (i.e. no pointers allowed in FS whatsoever).
+class RSCheckAST : public clang::StmtVisitor<RSCheckAST> {
+ private:
+  clang::ASTContext &C;
+  clang::DiagnosticsEngine &mDiagEngine;
+  clang::SourceManager &mSM;
+  bool mValid;
+  unsigned int mTargetAPI;
+  bool mIsFilterscript;
+
+ public:
+  explicit RSCheckAST(clang::ASTContext &Con, unsigned int TargetAPI,
+                      bool IsFilterscript)
+      : C(Con), mDiagEngine(Con.getDiagnostics()), mSM(C.getSourceManager()),
+        mValid(true), mTargetAPI(TargetAPI), mIsFilterscript(IsFilterscript) {
+    return;
+  }
+
+  void VisitStmt(clang::Stmt *S);
+
+  void VisitExpr(clang::Expr *E);
+
+  void VisitDeclStmt(clang::DeclStmt *DS);
+
+  void ValidateFunctionDecl(clang::FunctionDecl *FD);
+
+  void ValidateVarDecl(clang::VarDecl *VD);
+
+  bool Validate();
+};
+
+}  // namespace slang
+
+#endif  // _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_CHECK_AST_H_  NOLINT
diff --git a/slang_rs_export_type.cpp b/slang_rs_export_type.cpp
index 2d35ef6..94bd6bb 100644
--- a/slang_rs_export_type.cpp
+++ b/slang_rs_export_type.cpp
@@ -89,7 +89,7 @@
     const clang::RecordDecl *TopLevelRecord);
 
 static void ReportTypeError(clang::DiagnosticsEngine *DiagEngine,
-                            const clang::VarDecl *VD,
+                            const clang::NamedDecl *ND,
                             const clang::RecordDecl *TopLevelRecord,
                             const char *Message,
                             unsigned int TargetAPI = 0) {
@@ -107,11 +107,11 @@
       clang::FullSourceLoc(TopLevelRecord->getLocation(), SM),
       DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, Message))
       << TopLevelRecord->getName() << TargetAPI;
-  } else if (VD) {
+  } else if (ND) {
     DiagEngine->Report(
-      clang::FullSourceLoc(VD->getLocation(), SM),
+      clang::FullSourceLoc(ND->getLocation(), SM),
       DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, Message))
-      << VD->getName() << TargetAPI;
+      << ND->getName() << TargetAPI;
   } else {
     slangAssert(false && "Variables should be validated before exporting");
   }
@@ -337,7 +337,7 @@
     // Only if we are already in a composite type (like an array or structure).
     if (InCompositeType) {
       // Only if we are actually exported (i.e. non-static).
-      if (VD->getLinkage() == clang::ExternalLinkage) {
+      if (VD->hasLinkage() && (VD->getLinkage() == clang::ExternalLinkage)) {
         // Only if we are not a pointer to an object.
         const clang::Type *T = GET_CANONICAL_TYPE(VD->getType().getTypePtr());
         if (T->getTypeClass() != clang::Type::Pointer) {
@@ -355,25 +355,30 @@
   return true;
 }
 
-// Helper function for ValidateVarDecl(). We do a recursive descent on the
+// Helper function for ValidateType(). We do a recursive descent on the
 // type hierarchy to ensure that we can properly export/handle the
 // declaration.
 // \return true if the variable declaration is valid,
 //         false if it is invalid (along with proper diagnostics).
 //
-// VD - top-level variable declaration that we are validating.
-// T - sub-type of VD's type that we are validating.
+// C - ASTContext (for diagnostics + builtin types).
+// T - sub-type that we are validating.
+// ND - (optional) top-level named declaration that we are validating.
 // SPS - set of types we have already seen/validated.
 // InCompositeType - true if we are within an outer composite type.
 // UnionDecl - set if we are in a sub-type of a union.
 // TargetAPI - target SDK API level.
-static bool ValidateVarDeclHelper(
-    clang::VarDecl *VD,
+// IsFilterscript - whether or not we are compiling for Filterscript
+static bool ValidateTypeHelper(
+    clang::ASTContext &C,
     const clang::Type *&T,
+    clang::NamedDecl *ND,
+    clang::SourceLocation Loc,
     llvm::SmallPtrSet<const clang::Type*, 8>& SPS,
     bool InCompositeType,
     clang::RecordDecl *UnionDecl,
-    unsigned int TargetAPI) {
+    unsigned int TargetAPI,
+    bool IsFilterscript) {
   if ((T = GET_CANONICAL_TYPE(T)) == NULL)
     return true;
 
@@ -383,7 +388,8 @@
   switch (T->getTypeClass()) {
     case clang::Type::Record: {
       if (RSExportPrimitiveType::IsRSObjectType(T)) {
-        if (!ValidateRSObjectInVarDecl(VD, InCompositeType, TargetAPI)) {
+        clang::VarDecl *VD = (ND ? llvm::dyn_cast<clang::VarDecl>(ND) : NULL);
+        if (VD && !ValidateRSObjectInVarDecl(VD, InCompositeType, TargetAPI)) {
           return false;
         }
       }
@@ -393,8 +399,7 @@
         if (!UnionDecl) {
           return true;
         } else if (RSExportPrimitiveType::IsRSObjectType(T)) {
-          clang::ASTContext &C = VD->getASTContext();
-          ReportTypeError(&C.getDiagnostics(), VD, UnionDecl,
+          ReportTypeError(&C.getDiagnostics(), NULL, UnionDecl,
               "unions containing RS object types are not allowed");
           return false;
         }
@@ -437,7 +442,8 @@
         const clang::Type *FT = RSExportType::GetTypeOfDecl(FD);
         FT = GET_CANONICAL_TYPE(FT);
 
-        if (!ValidateVarDeclHelper(VD, FT, SPS, true, UnionDecl, TargetAPI)) {
+        if (!ValidateTypeHelper(C, FT, ND, Loc, SPS, true, UnionDecl,
+                                TargetAPI, IsFilterscript)) {
           return false;
         }
       }
@@ -446,16 +452,58 @@
     }
 
     case clang::Type::Builtin: {
+      if (IsFilterscript) {
+        clang::QualType QT = T->getCanonicalTypeInternal();
+        if (QT == C.DoubleTy ||
+            QT == C.LongDoubleTy ||
+            QT == C.LongTy ||
+            QT == C.LongLongTy) {
+          clang::DiagnosticsEngine &DiagEngine = C.getDiagnostics();
+          if (ND) {
+            DiagEngine.Report(
+              clang::FullSourceLoc(Loc, C.getSourceManager()),
+              DiagEngine.getCustomDiagID(
+                clang::DiagnosticsEngine::Error,
+                "Builtin types > 32 bits in size are forbidden in "
+                "Filterscript: '%0'")) << ND->getName();
+          } else {
+            DiagEngine.Report(
+              clang::FullSourceLoc(Loc, C.getSourceManager()),
+              DiagEngine.getCustomDiagID(
+                clang::DiagnosticsEngine::Error,
+                "Builtin types > 32 bits in size are forbidden in "
+                "Filterscript"));
+          }
+          return false;
+        }
+      }
       break;
     }
 
     case clang::Type::Pointer: {
+      if (IsFilterscript) {
+        if (ND) {
+          clang::DiagnosticsEngine &DiagEngine = C.getDiagnostics();
+          DiagEngine.Report(
+            clang::FullSourceLoc(Loc, C.getSourceManager()),
+            DiagEngine.getCustomDiagID(
+              clang::DiagnosticsEngine::Error,
+              "Pointers are forbidden in Filterscript: '%0'")) << ND->getName();
+          return false;
+        } else {
+          // TODO(srhines): Find a better way to handle expressions (i.e. no
+          // NamedDecl) involving pointers in FS that should be allowed.
+          // An example would be calls to library functions like
+          // rsMatrixMultiply() that take rs_matrixNxN * types.
+        }
+      }
+
       const clang::PointerType *PT =
         UNSAFE_CAST_TYPE(const clang::PointerType, T);
       const clang::Type *PointeeType = GET_POINTEE_TYPE(PT);
 
-      return ValidateVarDeclHelper(VD, PointeeType, SPS, InCompositeType,
-                                   UnionDecl, TargetAPI);
+      return ValidateTypeHelper(C, PointeeType, ND, Loc, SPS, InCompositeType,
+                                UnionDecl, TargetAPI, IsFilterscript);
     }
 
     case clang::Type::ExtVector: {
@@ -465,22 +513,21 @@
       if (TargetAPI < SLANG_ICS_TARGET_API &&
           InCompositeType &&
           EVT->getNumElements() == 3) {
-        clang::ASTContext &C = VD->getASTContext();
-        ReportTypeError(&C.getDiagnostics(), VD, NULL,
+        ReportTypeError(&C.getDiagnostics(), ND, NULL,
                         "structs containing vectors of dimension 3 cannot "
                         "be exported at this API level: '%0'");
         return false;
       }
-      return ValidateVarDeclHelper(VD, ElementType, SPS, true, UnionDecl,
-                                   TargetAPI);
+      return ValidateTypeHelper(C, ElementType, ND, Loc, SPS, true, UnionDecl,
+                                TargetAPI, IsFilterscript);
     }
 
     case clang::Type::ConstantArray: {
       const clang::ConstantArrayType *CAT =
           UNSAFE_CAST_TYPE(const clang::ConstantArrayType, T);
       const clang::Type *ElementType = GET_CONSTANT_ARRAY_ELEMENT_TYPE(CAT);
-      return ValidateVarDeclHelper(VD, ElementType, SPS, true, UnionDecl,
-                                   TargetAPI);
+      return ValidateTypeHelper(C, ElementType, ND, Loc, SPS, true, UnionDecl,
+                                TargetAPI, IsFilterscript);
     }
 
     default: {
@@ -523,12 +570,22 @@
   return true;
 }
 
-bool RSExportType::ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI) {
-  const clang::Type *T = VD->getType().getTypePtr();
+bool RSExportType::ValidateType(clang::ASTContext &C, clang::QualType QT,
+    clang::NamedDecl *ND, clang::SourceLocation Loc, unsigned int TargetAPI,
+    bool IsFilterscript) {
+  const clang::Type *T = QT.getTypePtr();
   llvm::SmallPtrSet<const clang::Type*, 8> SPS =
       llvm::SmallPtrSet<const clang::Type*, 8>();
 
-  return ValidateVarDeclHelper(VD, T, SPS, false, NULL, TargetAPI);
+  return ValidateTypeHelper(C, T, ND, Loc, SPS, false, NULL, TargetAPI,
+                            IsFilterscript);
+  return true;
+}
+
+bool RSExportType::ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI,
+                                   bool IsFilterscript) {
+  return ValidateType(VD->getASTContext(), VD->getType(), VD,
+                      VD->getLocation(), TargetAPI, IsFilterscript);
 }
 
 const clang::Type
diff --git a/slang_rs_export_type.h b/slang_rs_export_type.h
index ea1a885..e9af953 100644
--- a/slang_rs_export_type.h
+++ b/slang_rs_export_type.h
@@ -157,9 +157,18 @@
                             clang::DiagnosticsEngine *Diags,
                             const clang::VarDecl *VD);
 
+  // This function checks whether the specified type can be handled by RS/FS.
+  // If it cannot, this function returns false. Otherwise it returns true.
+  // Filterscript has additional restrictions on supported types.
+  static bool ValidateType(clang::ASTContext &C, clang::QualType QT,
+                           clang::NamedDecl *ND, clang::SourceLocation Loc,
+                           unsigned int TargetAPI, bool IsFilterscript);
+
   // This function ensures that the VarDecl can be properly handled by RS.
   // If it cannot, this function returns false. Otherwise it returns true.
-  static bool ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI);
+  // Filterscript has additional restrictions on supported types.
+  static bool ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI,
+                              bool IsFilterscript);
 
   // @T may not be normalized
   static RSExportType *Create(RSContext *Context, const clang::Type *T);
diff --git a/tests/F_fs_oldkernel/fs_oldkernel.fs b/tests/F_fs_oldkernel/fs_oldkernel.fs
new file mode 100644
index 0000000..4f607a5
--- /dev/null
+++ b/tests/F_fs_oldkernel/fs_oldkernel.fs
@@ -0,0 +1,10 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void old_kernel(const uint32_t *ain, uint32_t x, uint32_t y) {
+}
+
+uint32_t* bad_kernel(const uint32_t *ain, uint32_t x, uint32_t y) {
+    return 0;
+}
+
diff --git a/tests/F_fs_oldkernel/stderr.txt.expect b/tests/F_fs_oldkernel/stderr.txt.expect
new file mode 100644
index 0000000..5b53b56
--- /dev/null
+++ b/tests/F_fs_oldkernel/stderr.txt.expect
@@ -0,0 +1,3 @@
+fs_oldkernel.fs:4:17: error: Pointers are forbidden in Filterscript: 'ain'
+fs_oldkernel.fs:7:1: error: Pointers are forbidden in Filterscript: 'bad_kernel'
+fs_oldkernel.fs:7:22: error: Pointers are forbidden in Filterscript: 'ain'
diff --git a/tests/F_fs_oldkernel/stdout.txt.expect b/tests/F_fs_oldkernel/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_fs_oldkernel/stdout.txt.expect
diff --git a/tests/F_fs_ptr/fs_ptr.fs b/tests/F_fs_ptr/fs_ptr.fs
new file mode 100644
index 0000000..7ae49eb
--- /dev/null
+++ b/tests/F_fs_ptr/fs_ptr.fs
@@ -0,0 +1,39 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int *i;
+
+struct f {
+    int i;
+    float *pf;
+    char c;
+    short *ps;
+};
+
+int ia[10];
+
+int __attribute__((kernel)) root(uint32_t ain) {
+  char *c;
+
+  c = (char*) ain; // TODO(srhines): This is ok today.
+  return 0;
+}
+
+void __attribute__((kernel)) in_only(uint32_t ain) {
+}
+
+int __attribute__((kernel)) out_only() {
+  return 0;
+}
+
+int __attribute__((kernel)) everything(uint32_t ain, uint32_t x, uint32_t y) {
+  return (int)&ain; // TODO(srhines): This is ok today.
+}
+
+void old_kernel(const uint32_t *ain, uint32_t x, uint32_t y) {
+}
+
+void test_call() {
+    int i = root(ia[4]);
+}
+
diff --git a/tests/F_fs_ptr/stderr.txt.expect b/tests/F_fs_ptr/stderr.txt.expect
new file mode 100644
index 0000000..2ff21f9
--- /dev/null
+++ b/tests/F_fs_ptr/stderr.txt.expect
@@ -0,0 +1,3 @@
+fs_ptr.fs:4:6: error: Pointers are forbidden in Filterscript: 'i'
+fs_ptr.fs:16:9: error: Pointers are forbidden in Filterscript: 'c'
+fs_ptr.fs:33:17: error: Pointers are forbidden in Filterscript: 'ain'
diff --git a/tests/F_fs_ptr/stdout.txt.expect b/tests/F_fs_ptr/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_fs_ptr/stdout.txt.expect
diff --git a/tests/F_fs_types/fs_types.fs b/tests/F_fs_types/fs_types.fs
new file mode 100644
index 0000000..12c22ce
--- /dev/null
+++ b/tests/F_fs_types/fs_types.fs
@@ -0,0 +1,38 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+double d;
+
+struct s {
+    int i;
+    double d;
+    char c;
+    long l;
+};
+
+struct s myS;
+
+void foo_d(double d) {
+    double e;
+    float f = 0.0;
+    e = d;
+}
+
+void foo_l(long l) {
+    long m;
+    int i = 1l;
+    m = l;
+}
+
+void foo_ll(long long l) {
+    long long m;
+    int i = 1ll;
+    m = l;
+}
+
+void foo_ld(long double l) {
+    long double m;
+    float f = 0.0L;
+    m = l;
+}
+
diff --git a/tests/F_fs_types/stderr.txt.expect b/tests/F_fs_types/stderr.txt.expect
new file mode 100644
index 0000000..9422d76
--- /dev/null
+++ b/tests/F_fs_types/stderr.txt.expect
@@ -0,0 +1,18 @@
+fs_types.fs:4:8: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'd'
+fs_types.fs:13:10: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'myS'
+fs_types.fs:15:12: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'd'
+fs_types.fs:16:12: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'e'
+fs_types.fs:17:15: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:18:7: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:21:12: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'l'
+fs_types.fs:22:10: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'm'
+fs_types.fs:23:13: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:24:7: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:27:13: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'l'
+fs_types.fs:28:15: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'm'
+fs_types.fs:29:13: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:30:7: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:33:13: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'l'
+fs_types.fs:34:17: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'm'
+fs_types.fs:35:15: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:36:7: error: Builtin types > 32 bits in size are forbidden in Filterscript
diff --git a/tests/F_fs_types/stdout.txt.expect b/tests/F_fs_types/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_fs_types/stdout.txt.expect
diff --git a/tests/F_union/stderr.txt.expect b/tests/F_union/stderr.txt.expect
index 1848917..2bf177d 100644
--- a/tests/F_union/stderr.txt.expect
+++ b/tests/F_union/stderr.txt.expect
@@ -1,2 +1,3 @@
 union.rs:4:7: error: unions cannot be exported: 'u'
 union.rs:9:14: error: unions containing RS object types are not allowed
+union.rs:16:11: error: unions containing RS object types are not allowed
diff --git a/tests/F_union/union.rs b/tests/F_union/union.rs
index aee4948..c6d423c 100644
--- a/tests/F_union/union.rs
+++ b/tests/F_union/union.rs
@@ -11,3 +11,11 @@
     rs_font f;
 } myUnion2;
 
+
+void foo() {
+    union iu {
+        rs_font f;
+        int i;
+    } v;
+    (void) v;
+}
diff --git a/tests/P_fs_kernel/fs_kernel.fs b/tests/P_fs_kernel/fs_kernel.fs
new file mode 100644
index 0000000..d8f0c4d
--- /dev/null
+++ b/tests/P_fs_kernel/fs_kernel.fs
@@ -0,0 +1,18 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int __attribute__((kernel)) root(uint32_t ain) {
+  return 0;
+}
+
+void __attribute__((kernel)) in_only(uint32_t ain) {
+}
+
+int __attribute__((kernel)) out_only() {
+  return 0;
+}
+
+int __attribute__((kernel)) everything(uint32_t ain, uint32_t x, uint32_t y) {
+  return 0;
+}
+
diff --git a/tests/P_fs_kernel/stderr.txt.expect b/tests/P_fs_kernel/stderr.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/P_fs_kernel/stderr.txt.expect
diff --git a/tests/P_fs_kernel/stdout.txt.expect b/tests/P_fs_kernel/stdout.txt.expect
new file mode 100644
index 0000000..4a8eafd
--- /dev/null
+++ b/tests/P_fs_kernel/stdout.txt.expect
@@ -0,0 +1 @@
+Generating ScriptC_fs_kernel.java ...
diff --git a/tests/test.py b/tests/test.py
index 44327e8..7392cf6 100755
--- a/tests/test.py
+++ b/tests/test.py
@@ -75,6 +75,8 @@
                 '-I ../../../../../external/clang/lib/Headers/')
   base_args = cmd_string.split()
   rs_files = glob.glob('*.rs')
+  fs_files = glob.glob('*.fs')
+  rs_files += fs_files;
   rs_files.sort()
 
   # Extra command line arguments can be placed as // comments at the start of