leonardchan created this revision.
leonardchan added reviewers: phosek, mcgrathr.
leonardchan added a project: clang.

This patch adds the `noderef` attribute in clang and checks for dereferences of 
types that have this attribute. This attribute is currently used by sparse and 
would like to be ported to clang.

The following are examples of when a warning would be raised on dereferencing a 
noderef type.

  int __attribute__((noderef)) *p;
  int x = *p;  // warning: dereference of noderef expression 
[-Wignored-attributes]
  
  int __attribute__((noderef)) **p2;
  x = **p2;  // warning: dereference of noderef expression 
[-Wignored-attributes]
  
  int * __attribute__((noderef)) *p3;
  p = *p3;  // warning: dereference of noderef expression [-Wignored-attributes]
  
  struct S {
    int a;
  };
  struct S __attribute__((noderef)) *s;
  x = s->a;    // warning: dereference of noderef expression 
[-Wignored-attributes]
  x = (*s).a;   // warning: dereference of noderef expression 
[-Wignored-attributes]

Not all accesses may raise a warning if the value directed by the pointer may 
not be accessed. The following are examples where no warning may be raised:

  int *q;
  int __attribute__((noderef)) *p;
  q = &*p;
  q = *&p;
  
  struct S {
    int a;
  };
  struct S __attribute__((noderef)) *s;
  p = &s->a;
  p = &(*s).a;

More examples of existing usage of noderef in sparse can be found in 
https://git.kernel.org/pub/scm/devel/sparse/sparse.git/tree/validation/noderef.c


Repository:
  rC Clang

https://reviews.llvm.org/D49511

Files:
  include/clang/AST/Type.h
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/AST/Type.cpp
  lib/AST/TypePrinter.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaType.cpp
  test/Frontend/noderef.c

Index: test/Frontend/noderef.c
===================================================================
--- /dev/null
+++ test/Frontend/noderef.c
@@ -0,0 +1,133 @@
+// RUN: %clang_cc1 -x c -verify %s
+// RUN: %clang_cc1 -x c++ -verify %s
+
+#define NODEREF __attribute__((noderef))
+
+struct S {
+  int a;
+  int b;
+};
+
+struct S2 {
+  int a[2];
+  int NODEREF a2[2];
+  int *b;
+  int NODEREF *b2;
+  struct S *s;
+  struct S NODEREF *s2;
+};
+
+int NODEREF *func(int NODEREF *arg) {
+  int y = *arg; // expected-warning{{dereference of noderef expression}}
+  return arg;
+}
+
+void func2(int x) {}
+
+void test() {
+  int NODEREF *p;
+  int *p2;
+
+  int x = *p;               // expected-warning{{dereference of noderef expression}}
+  x = *((int NODEREF *)p2); // expected-warning{{dereference of noderef expression}}
+
+  int NODEREF **q;
+  int *NODEREF *q2;
+
+  // Indirection
+  p2 = *q;  // ok
+  x = **q;  // expected-warning{{dereference of noderef expression}}
+  p2 = *q2; // expected-warning{{dereference of noderef expression}}
+
+  **q; // expected-warning{{dereference of noderef expression}}
+       // expected-warning@-1{{expression result unused}}
+
+  p = *&*q;
+  p = **&q;
+  q = &**&q;
+  p = &*p;
+  p = *&p;
+  p = &(*p);
+  p = *(&p);
+  x = **&p; // expected-warning{{dereference of noderef expression}}
+
+  *p = 2;   // expected-warning{{dereference of noderef expression}}
+  *q = p;   // ok
+  **q = 2;  // expected-warning{{dereference of noderef expression}}
+  *q2 = p2; // expected-warning{{dereference of noderef expression}}
+
+  p2 = p;
+  p = p + 1;
+  p = &*(p + 1);
+
+  // Struct member access
+  struct S NODEREF *s;
+  x = s->a;   // expected-warning{{dereference of noderef expression}}
+  x = (*s).b; // expected-warning{{dereference of noderef expression}}
+  p = &s->a;
+  p = &(*s).b;
+
+  // Nested struct access
+  struct S2 NODEREF *s2_noderef;
+  p = s2_noderef->a;  // ok since result is an array in a struct
+  p = s2_noderef->a2; // ok
+  p = s2_noderef->b;  // expected-warning{{dereference of noderef expression}}
+  p = s2_noderef->b2; // expected-warning{{dereference of noderef expression}}
+  s = s2_noderef->s;  // expected-warning{{dereference of noderef expression}}
+  s = s2_noderef->s2; // expected-warning{{dereference of noderef expression}}
+  p = s2_noderef->a + 1;
+
+  struct S2 *s2;
+  p = s2->a;
+  p = s2->a2;
+  p = s2->b;
+  p = s2->b2;
+  s = s2->s;
+  s = s2->s2;
+
+  // Subscript access
+  x = p[1];    // expected-warning{{dereference of noderef expression}}
+  p2 = q[0];   // ok
+  x = q[0][0]; // expected-warning{{dereference of noderef expression}}
+  p2 = q2[0];  // expected-warning{{dereference of noderef expression}}
+
+  int NODEREF arr[10];
+  x = arr[1]; // expected-warning{{dereference of noderef expression}}
+
+  int NODEREF *(arr2[10]);
+  int NODEREF *elem = *arr2;
+
+  int NODEREF(*arr3)[10];
+  elem = *arr3;
+
+  // Combinations between indirection, subscript, and member access
+  struct S2 NODEREF *s2_arr[10];
+  s2 = s2_arr[1];
+  struct S2 NODEREF *s2_arr2[10][10];
+  s2 = s2_arr2[1][1];
+
+  p = s2_arr[1]->a;
+  p = s2_arr[1]->b; // expected-warning{{dereference of noderef expression}}
+  int **bptr = &s2_arr[1]->b;
+
+  x = s2->s2->a;        // expected-warning{{dereference of noderef expression}}
+  x = s2_noderef->a[1]; // expected-warning{{dereference of noderef expression}}
+  p = &s2_noderef->a[1];
+
+  // Functions
+  x = *(func(p)); // expected-warning{{dereference of noderef expression}}
+
+  // Casting is ok
+  q = (int NODEREF **)&p;
+  q = (int NODEREF **)&p2;
+  q = &p;
+  q = &p2;
+  x = s2->s2->a; // expected-warning{{dereference of noderef expression}}
+
+  // Other expressions
+  func2(*p);         // expected-warning{{dereference of noderef expression}}
+  func2(*p + 1);     // expected-warning{{dereference of noderef expression}}
+  func2(!*p);        // expected-warning{{dereference of noderef expression}}
+  func2((x = *p));   // expected-warning{{dereference of noderef expression}}
+  func2((char)(*p)); // expected-warning{{dereference of noderef expression}}
+}
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -5215,6 +5215,8 @@
     return ParsedAttr::AT_PreserveMost;
   case AttributedType::attr_preserve_all:
     return ParsedAttr::AT_PreserveAll;
+  case AttributedType::attr_noderef:
+    return ParsedAttr::AT_NoDeref;
   case AttributedType::attr_ptr32:
     return ParsedAttr::AT_Ptr32;
   case AttributedType::attr_ptr64:
@@ -7299,6 +7301,12 @@
       attr.setUsedAsTypeAttr();
       break;
 
+    case ParsedAttr::AT_NoDeref:
+      type = state.getSema().Context.getAttributedType(
+          AttributedType::attr_noderef, type, type);
+      attr.setUsedAsTypeAttr();
+      break;
+
     MS_TYPE_ATTRS_CASELIST:
       if (!handleMSPointerTypeQualifierAttr(state, attr, type))
         attr.setUsedAsTypeAttr();
Index: lib/Sema/SemaStmt.cpp
===================================================================
--- lib/Sema/SemaStmt.cpp
+++ lib/Sema/SemaStmt.cpp
@@ -55,6 +55,9 @@
   // void expression for its side effects.  Conversion to void allows any
   // operand, even incomplete types.
 
+  if (!FE.isInvalid())
+    NoDerefChecker(*this).Visit(FE.get());
+
   // Same thing in for stmt first clause (when expr) and third clause.
   return StmtResult(FE.getAs<Stmt>());
 }
@@ -77,7 +80,12 @@
   // If we have an invalid decl, just return an error.
   if (DG.isNull()) return StmtError();
 
-  return new (Context) DeclStmt(DG, StartLoc, EndLoc);
+  StmtResult Res = new (Context) DeclStmt(DG, StartLoc, EndLoc);
+
+  if (!Res.isInvalid())
+    NoDerefChecker(*this).Visit(Res.get());
+
+  return Res;
 }
 
 void Sema::ActOnForEachDeclStmt(DeclGroupPtrTy dg) {
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -4153,6 +4153,103 @@
   return isa<MSPropertySubscriptExpr>(BaseNoParens);
 }
 
+/// Raise a warning when we land on a dereference whose resulting type is marked
+/// with noderef. Any top level expressions that are an address of operator do
+/// not raise this warning.
+void NoDerefChecker::VisitUnaryOperator(const UnaryOperator *E) {
+  if (VisitedExprs.find(E) != VisitedExprs.end())
+    return;
+
+  if (E->getOpcode() == UO_AddrOf) {
+    VisitedExprs.insert(E);
+    return;
+  } else if (E->getOpcode() == UO_Deref && TypeHasNoDeref(E->getType())) {
+    S.Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+        << E->getSourceRange();
+    VisitedExprs.insert(E);
+    return;
+  }
+
+  Visit(E->getSubExpr());
+}
+
+/// Raise a warning if we dereference and the result is not an array. Otherwise
+/// keep traversing down.
+void NoDerefChecker::VisitMemberExpr(const MemberExpr *E) {
+  if (VisitedExprs.find(E) != VisitedExprs.end())
+    return;
+
+  if (E->isArrow()) {
+    // We only care if the type is a pointer to a struct that has noderef
+    if (const PointerType *Ptr =
+            dyn_cast<PointerType>(E->getBase()->getType())) {
+      if (TypeHasNoDeref(Ptr->getPointeeType()) &&
+          !isa<ArrayType>(E->getType())) {
+        S.Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+            << E->getSourceRange();
+        VisitedExprs.insert(E);
+        return;
+      }
+    }
+  }
+
+  Visit(E->getBase());
+}
+
+/// Raise a warning if the resulting type is noderef or we index the field of a
+/// struct that was marked with noderef.
+void NoDerefChecker::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+  if (VisitedExprs.find(E) != VisitedExprs.end())
+    return;
+
+  if (TypeHasNoDeref(E->getType())) {
+    S.Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+        << E->getSourceRange();
+    VisitedExprs.insert(E);
+    return;
+  }
+
+  const Expr *Base = E->getBase();
+  const QualType &BaseTy = Base->getType();
+
+  // Raise an error if the base type is a pointer to a member access of a struct
+  // marked with noderef.
+  QualType ElemTy;
+  if (const ArrayType *ArrayTy = dyn_cast<ArrayType>(BaseTy)) {
+    ElemTy = ArrayTy->getElementType();
+  } else if (const PointerType *PointerTy = dyn_cast<PointerType>(BaseTy)) {
+    ElemTy = PointerTy->getPointeeType();
+  } else {
+    // Not a pointer access
+    Visit(Base);
+    return;
+  }
+
+  if (const MemberExpr *Member =
+          dyn_cast<MemberExpr>(Base->IgnoreParenCasts())) {
+    QualType MemberBaseTy = Member->getBase()->getType();
+    if (const PointerType *Ptr = dyn_cast<PointerType>(MemberBaseTy)) {
+      if (TypeHasNoDeref(Ptr->getPointeeType())) {
+        S.Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+            << E->getSourceRange();
+        VisitedExprs.insert(E);
+        return;
+      }
+    }
+  }
+
+  // We do not need to check the index since it is an integer
+  Visit(E->getBase());
+}
+
+bool NoDerefChecker::TypeHasNoDeref(const QualType &Ty) {
+  if (const AttributedType *AT = dyn_cast<AttributedType>(Ty)) {
+    AttributedType::Kind AttrKind = AT->getAttrKind();
+    return AttrKind == AttributedType::attr_noderef;
+  }
+  return false;
+}
+
 ExprResult
 Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc,
                               Expr *idx, SourceLocation rbLoc) {
Index: lib/AST/TypePrinter.cpp
===================================================================
--- lib/AST/TypePrinter.cpp
+++ lib/AST/TypePrinter.cpp
@@ -1557,6 +1557,9 @@
   case AttributedType::attr_preserve_all:
     OS << "preserve_all";
     break;
+  case AttributedType::attr_noderef:
+    OS << "noderef";
+    break;
   }
   OS << "))";
 }
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -3192,6 +3192,7 @@
   case AttributedType::attr_preserve_all:
   case AttributedType::attr_ms_abi:
   case AttributedType::attr_sysv_abi:
+  case AttributedType::attr_noderef:
   case AttributedType::attr_ptr32:
   case AttributedType::attr_ptr64:
   case AttributedType::attr_sptr:
@@ -3218,6 +3219,7 @@
 
 bool AttributedType::isCallingConv() const {
   switch (getAttrKind()) {
+  case attr_noderef:
   case attr_ptr32:
   case attr_ptr64:
   case attr_sptr:
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -20,6 +20,7 @@
 #include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
@@ -58,6 +59,7 @@
 #include <deque>
 #include <memory>
 #include <string>
+#include <unordered_set>
 #include <vector>
 
 namespace llvm {
@@ -10782,6 +10784,25 @@
   Decl *D;
 };
 
+/// Report warnings on expressions that dereference a type marked with a
+/// noderef attribute.
+class NoDerefChecker : public ConstEvaluatedExprVisitor<NoDerefChecker> {
+public:
+  typedef ConstEvaluatedExprVisitor<NoDerefChecker> Inherited;
+
+  NoDerefChecker(Sema &S) : Inherited(S.Context), S(S) {}
+
+  void VisitUnaryOperator(const UnaryOperator *E);
+  void VisitMemberExpr(const MemberExpr *E);
+  void VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
+
+private:
+  static bool TypeHasNoDeref(const QualType &Ty);
+
+  Sema &S;
+  std::unordered_set<const Expr *> VisitedExprs;
+};
+
 } // end namespace clang
 
 namespace llvm {
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -9367,4 +9367,7 @@
    "member '%2' is missing|"
    "the type is not trivially copyable|"
    "the type does not have the expected form}1">;
+
+def warn_dereference_of_noderef_type : Warning<
+  "dereference of noderef expression">, InGroup<IgnoredAttributes>;
 } // end of sema component.
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -3348,3 +3348,49 @@
 corresponding line within the inlined callee.
   }];
 }
+
+def NoDerefDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``noderef`` attribute allows for showing a warning whenever a pointer marked with
+this attribute is dereferenced. This is ideally used with pointers that point to special
+memory which cannot be read from or written to, but allowing for the pointer to be used in
+pointer arithmetic. The following are examples of valid expressions where a warning may
+be raised:
+
+.. code-block:: c
+
+  int __attribute__((noderef)) *p;
+  int x = *p;  // warning
+
+  int __attribute__((noderef)) **p2;
+  x = **p2;  // warning
+
+  int * __attribute__((noderef)) *p3;
+  p = *p3;  // warning
+
+  struct S {
+    int a;
+  };
+  struct S __attribute__((noderef)) *s;
+  x = s->a;    // warning
+  x = (*s).a;  // warning
+
+Not all dereferences may raise a warning if the value directed by the pointer may not be
+accessed. The following are examples of valid expressions where no warning may be raised:
+
+.. code-block:: c
+
+  int *q;
+  int __attribute__((noderef)) *p;
+  q = &*p;
+  q = *&p;
+
+  struct S {
+    int a;
+  };
+  struct S __attribute__((noderef)) *s;
+  p = &s->a;
+  p = &(*s).a;
+}];
+}
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1727,6 +1727,11 @@
   let Documentation = [RegparmDocs];
 }
 
+def NoDeref : TypeAttr {
+  let Spellings = [Clang<"noderef">];
+  let Documentation = [NoDerefDocs];
+}
+
 def ReqdWorkGroupSize : InheritableAttr {
   // Does not have a [[]] spelling because it is an OpenCL-related attribute.
   let Spellings = [GNU<"reqd_work_group_size">];
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -4206,6 +4206,7 @@
     LastEnumOperandKind = attr_pcs_vfp,
 
     // No operand.
+    attr_noderef,
     attr_noreturn,
     attr_nocf_check,
     attr_cdecl,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to