leonardchan updated this revision to Diff 161785.
leonardchan marked 6 inline comments as done and an inline comment as not done.

Repository:
  rC Clang

https://reviews.llvm.org/D49511

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/AST/TypePrinter.cpp
  lib/Parse/ParseExpr.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaExprMember.cpp
  lib/Sema/SemaType.cpp
  test/Frontend/noderef.c
  test/Frontend/noderef_on_non_pointers.cpp
  test/Frontend/noderef_on_non_pointers.m
  test/Frontend/noderef_templates.cpp

Index: test/Frontend/noderef_templates.cpp
===================================================================
--- /dev/null
+++ test/Frontend/noderef_templates.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -verify %s
+
+#define NODEREF __attribute__((noderef))
+
+template <typename T>
+int func(T NODEREF *a) { // expected-note 2 {{a declared here}}
+  return *a + 1;         // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}}
+}
+
+void func() {
+  float NODEREF *f;
+  int NODEREF *i;
+  func(f); // expected-note{{in instantiation of function template specialization 'func<float>' requested here}}
+  func(i); // expected-note{{in instantiation of function template specialization 'func<int>' requested here}}
+}
Index: test/Frontend/noderef_on_non_pointers.m
===================================================================
--- /dev/null
+++ test/Frontend/noderef_on_non_pointers.m
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -verify %s
+
+#define NODEREF __attribute__((noderef))
+
+@interface NSObject
++ (id)new;
+@end
+
+void func() {
+  id NODEREF obj = [NSObject new]; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+}
Index: test/Frontend/noderef_on_non_pointers.cpp
===================================================================
--- /dev/null
+++ test/Frontend/noderef_on_non_pointers.cpp
@@ -0,0 +1,88 @@
+// RUN: %clang_cc1 -fblocks -verify %s
+
+/**
+ * Test 'noderef' attribute against other pointer-like types.
+ */
+
+#define NODEREF __attribute__((noderef))
+
+void Normal() {
+  int NODEREF i;        // expected-warning{{'noderef' can only be used on an array or pointer type}}
+  int NODEREF *i_ptr;   // ok
+  int NODEREF **i_ptr2; // ok
+  int *NODEREF i_ptr3;  // expected-warning{{'noderef' can only be used on an array or pointer type}}
+  int *NODEREF *i_ptr4; // ok
+
+  auto NODEREF *auto_i_ptr = i_ptr;
+  auto NODEREF auto_i = i; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+
+  struct {
+    int x;
+    int y;
+  } NODEREF *s;
+
+  int __attribute__((noderef(10))) * no_args; // expected-error{{'noderef' attribute takes no arguments}}
+}
+
+const int NODEREF *const_i_ptr;
+static int NODEREF *static_i_ptr;
+
+void ParenTypes() {
+  int NODEREF(*i_ptr);    // ok (same as `int NODEREF *`)
+  int NODEREF *(*i_ptr2); // ok (same as `int NODEREF **`)
+}
+
+// Function declarations
+int NODEREF func();   // expected-warning{{'noderef' can only be used on an array or pointer type}}
+int NODEREF *func2(); // ok (returning pointer)
+
+typedef int NODEREF (*func3)(int); // expected-warning{{'noderef' can only be used on an array or pointer type}}
+typedef int NODEREF *(*func4)(int);
+
+void Arrays() {
+  int NODEREF i_arr[10];      // ok
+  int NODEREF i_arr2[10][10]; // ok
+  int NODEREF *i_arr3[10];    // ok
+  int NODEREF i_arr4[] = {1, 2};
+}
+
+void ParenArrays() {
+  int NODEREF(i_ptr[10]);
+  int NODEREF(i_ptr2[10])[10];
+}
+
+typedef int NODEREF *(*func5[10])(int);
+
+// Arguments
+void func6(int NODEREF x); // expected-warning{{'noderef' can only be used on an array or pointer type}}
+void func7(int NODEREF *x);
+void func8() NODEREF;
+
+void References() {
+  int x = 2;
+  int NODEREF &y = x; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+  int *xp = &x;
+  int NODEREF *&a = xp; // ok (reference to a NODEREF *)
+  int *NODEREF &b = xp; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+}
+
+void BlockPointers() {
+  typedef int NODEREF (^IntBlock)(); // expected-warning{{'noderef' can only be used on an array or pointer type}}
+}
+
+class A {
+public:
+  int member;
+  int NODEREF *member2;
+  int NODEREF member3; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+};
+
+void MemberPointer() {
+  int NODEREF A::*var = &A::member; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+}
+
+template <class Ty>
+class B {
+  Ty NODEREF *member;
+  Ty NODEREF member2; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+};
Index: test/Frontend/noderef.c
===================================================================
--- /dev/null
+++ test/Frontend/noderef.c
@@ -0,0 +1,206 @@
+// RUN: %clang_cc1 -Wno-unused-value -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) {  // expected-note{{arg declared here}}
+  int y = *arg; // expected-warning{{dereferencing arg; was declared with a 'noderef' type}}
+  return arg;
+}
+
+void func2(int x) {}
+
+int test() {
+  int NODEREF *p; // expected-note 37 {{p declared here}}
+  int *p2;
+
+  int x = *p;               // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  x = *((int NODEREF *)p2); // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+  int NODEREF **q;
+  int *NODEREF *q2; // expected-note 4 {{q2 declared here}}
+
+  // Indirection
+  p2 = *q;  // ok
+  x = **q;  // expected-warning{{dereferencing expression marked as 'noderef'}}
+  p2 = *q2; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}}
+
+  **q; // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+  p = *&*q;
+  p = **&q;
+  q = &**&q;
+  p = &*p;
+  p = *&p;
+  p = &(*p);
+  p = *(&p);
+  x = **&p; // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+
+  *p = 2;   // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  *q = p;   // ok
+  **q = 2;  // expected-warning{{dereferencing expression marked as 'noderef'}}
+  *q2 = p2; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}}
+
+  p2 = p;
+  p = p + 1;
+  p = &*(p + 1);
+
+  // Struct member access
+  struct S NODEREF *s;  // expected-note 2 {{s declared here}}
+  x = s->a;   // expected-warning{{dereferencing s; was declared with a 'noderef' type}}
+  x = (*s).b; // expected-warning{{dereferencing s; was declared with a 'noderef' type}}
+  p = &s->a;
+  p = &(*s).b;
+
+  // Nested struct access
+  struct S2 NODEREF *s2_noderef;    // expected-note 5 {{s2_noderef declared here}}
+  p = s2_noderef->a;  // ok since result is an array in a struct
+  p = s2_noderef->a2; // ok
+  p = s2_noderef->b;  // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+  p = s2_noderef->b2; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+  s = s2_noderef->s;  // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+  s = s2_noderef->s2; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+  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;
+  &(*(*s2).s2).b;
+
+  // Subscript access
+  x = p[1];    // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  p2 = q[0];   // ok
+  x = q[0][0]; // expected-warning{{dereferencing expression marked as 'noderef'}}
+  p2 = q2[0];  // expected-warning{{dereferencing q2; was declared with a 'noderef' type}}
+  p = q[*p];   // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  x = p[*p];   // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+               // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+
+  int NODEREF arr[10];    // expected-note 1 {{arr declared here}}
+  x = arr[1]; // expected-warning{{dereferencing arr; was declared with a 'noderef' type}}
+
+  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{{dereferencing expression marked as 'noderef'}}
+  int **bptr = &s2_arr[1]->b;
+
+  x = s2->s2->a;        // expected-warning{{dereferencing expression marked as 'noderef'}}
+  x = s2_noderef->a[1]; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}}
+  p = &s2_noderef->a[1];
+
+  // typedefs
+  typedef int NODEREF *ptr_t;
+  ptr_t ptr; // expected-note 2 {{ptr declared here}}
+  ptr_t *ptr2;
+  *ptr; // expected-warning{{dereferencing ptr; was declared with a 'noderef' type}}
+  *ptr2;
+  **ptr2; // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+  typedef struct S2 NODEREF *s2_ptr_t;
+  s2_ptr_t s2_ptr; // expected-note 4 {{s2_ptr declared here}}
+  s2_ptr->a;       // ok since result is an array in a struct
+  s2_ptr->a2;      // ok
+  s2_ptr->b;       // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}}
+  s2_ptr->b2;      // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}}
+  s2_ptr->s;       // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}}
+  s2_ptr->s2;      // expected-warning{{dereferencing s2_ptr; was declared with a 'noderef' type}}
+  s2_ptr->a + 1;
+
+  typedef int(int_t);
+  typedef int_t NODEREF *(noderef_int_t);
+  typedef noderef_int_t *noderef_int_nested_t;
+  noderef_int_nested_t noderef_int_nested_ptr;
+  *noderef_int_nested_ptr;
+  **noderef_int_nested_ptr; // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+  typedef int_t *(NODEREF noderef_int2_t);
+  typedef noderef_int2_t *noderef_int2_nested_t;
+  noderef_int2_nested_t noderef_int2_nested_ptr; // expected-note{{noderef_int2_nested_ptr declared here}}
+  *noderef_int2_nested_ptr;                      // expected-warning{{dereferencing noderef_int2_nested_ptr; was declared with a 'noderef' type}}
+
+  typedef int_t *(noderef_int3_t);
+  typedef noderef_int3_t(NODEREF(*(noderef_int3_nested_t)));
+  noderef_int3_nested_t noderef_int3_nested_ptr; // expected-note{{noderef_int3_nested_ptr declared here}}
+  *noderef_int3_nested_ptr;                      // expected-warning{{dereferencing noderef_int3_nested_ptr; was declared with a 'noderef' type}}
+
+  // Parentheses
+  (((*((p))))); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  (*(*(&(p)))); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+
+  (p[1]);      // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  (q[0]);      // ok
+  (q[0][0]);   // expected-warning{{dereferencing expression marked as 'noderef'}}
+  (q2[0]);     // expected-warning{{dereferencing q2; was declared with a 'noderef' type}}
+  (q[(*(p))]); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  (p[(*(p))]); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+               // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+
+  (*(ptr)); // expected-warning{{dereferencing ptr; was declared with a 'noderef' type}}
+  (*(ptr2));
+  (*(*(ptr2))); // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+  // Functions
+  x = *(func(p)); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+
+  // Casting is ok
+  q = (int NODEREF **)&p;
+  q = (int NODEREF **)&p2;
+  q = &p;
+  q = &p2;
+  x = s2->s2->a; // expected-warning{{dereferencing expression marked as 'noderef'}}
+
+  // Other expressions
+  func2(*p);         // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  func2(*p + 1);     // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  func2(!*p);        // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  func2((x = *p));   // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  func2((char)(*p)); // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+
+  // Other statements
+  if (*p) {}          // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  else if (*p) {}     // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  switch (*p){}       // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  for (*p; *p; *p){}  // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+                      // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+                      // expected-warning@-2{{dereferencing p; was declared with a 'noderef' type}}
+  for (*p; *p;){}     // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+                      // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+  for (*p;; *p){}     // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+                      // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+  for (; *p; *p){}    // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+                      // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}}
+  for (*p;;){}        // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  for (;*p;){}        // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  for (;;*p){}        // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  while (*p){}        // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  do {} while (*p);   // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+  return *p;          // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+}
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -182,11 +182,15 @@
     SmallVector<TypeAttrPair, 8> AttrsForTypes;
     bool AttrsForTypesSorted = true;
 
+    /// Flag to indicate we parsed a noderef attribute. This is used for
+    /// validating that noderef was used on a pointer or array.
+    bool parsedNoDeref;
+
   public:
     TypeProcessingState(Sema &sema, Declarator &declarator)
-      : sema(sema), declarator(declarator),
-        chunkIndex(declarator.getNumTypeObjects()),
-        trivial(true), hasSavedAttrs(false) {}
+        : sema(sema), declarator(declarator),
+          chunkIndex(declarator.getNumTypeObjects()), trivial(true),
+          hasSavedAttrs(false), parsedNoDeref(false) {}
 
     Sema &getSema() const {
       return sema;
@@ -277,6 +281,10 @@
       llvm_unreachable("no Attr* for AttributedType*");
     }
 
+    void setParsedNoDeref(bool parsed) { parsedNoDeref = parsed; }
+
+    bool didParseNoDeref() const { return parsedNoDeref; }
+
     ~TypeProcessingState() {
       if (trivial) return;
 
@@ -3879,6 +3887,11 @@
   return false;
 }
 
+static bool IsNoDerefableChunk(DeclaratorChunk Chunk) {
+  return (Chunk.Kind == DeclaratorChunk::Pointer ||
+          Chunk.Kind == DeclaratorChunk::Array);
+}
+
 template<typename AttrT>
 static AttrT *createSimpleAttr(ASTContext &Ctx, ParsedAttr &Attr) {
   Attr.setUsedAsTypeAttr();
@@ -4272,6 +4285,9 @@
     }
   }
 
+  bool ExpectNoDerefChunk =
+      state.getCurrentAttributes().hasAttribute(ParsedAttr::AT_NoDeref);
+
   // Walk the DeclTypeInfo, building the recursive type as we go.
   // DeclTypeInfos are ordered from the identifier out, which is
   // opposite of what we want :).
@@ -4874,8 +4890,22 @@
 
     // See if there are any attributes on this declarator chunk.
     processTypeAttrs(state, T, TAL_DeclChunk, DeclType.getAttrs());
+
+    if (DeclType.Kind != DeclaratorChunk::Paren) {
+      if (ExpectNoDerefChunk) {
+        if (!IsNoDerefableChunk(DeclType))
+          S.Diag(DeclType.Loc, diag::warn_noderef_on_non_pointer_or_array);
+        ExpectNoDerefChunk = false;
+      }
+
+      ExpectNoDerefChunk = state.didParseNoDeref();
+    }
   }
 
+  if (ExpectNoDerefChunk)
+    S.Diag(state.getDeclarator().getBeginLoc(),
+           diag::warn_noderef_on_non_pointer_or_array);
+
   // GNU warning -Wstrict-prototypes
   //   Warn if a function declaration is without a prototype.
   //   This warning is issued for all kinds of unprototyped function
@@ -7220,6 +7250,9 @@
   // sure we visit every element once. Copy the attributes list, and iterate
   // over that.
   ParsedAttributesView AttrsCopy{attrs};
+
+  state.setParsedNoDeref(false);
+
   for (ParsedAttr &attr : AttrsCopy) {
 
     // Skip attributes that were marked to be invalid.
@@ -7317,6 +7350,14 @@
         HandleLifetimeBoundAttr(state, type, attr);
       break;
 
+    case ParsedAttr::AT_NoDeref:
+      type = state.getAttributedType(
+          createSimpleAttr<NoDerefAttr>(state.getSema().Context, attr), type,
+          type);
+      attr.setUsedAsTypeAttr();
+      state.setParsedNoDeref(true);
+      break;
+
     MS_TYPE_ATTRS_CASELIST:
       if (!handleMSPointerTypeQualifierAttr(state, attr, type))
         attr.setUsedAsTypeAttr();
Index: lib/Sema/SemaExprMember.cpp
===================================================================
--- lib/Sema/SemaExprMember.cpp
+++ lib/Sema/SemaExprMember.cpp
@@ -1708,9 +1708,30 @@
   }
 
   ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl};
-  return BuildMemberReferenceExpr(Base, Base->getType(), OpLoc, IsArrow, SS,
-                                  TemplateKWLoc, FirstQualifierInScope,
-                                  NameInfo, TemplateArgs, S, &ExtraArgs);
+  ExprResult Res = BuildMemberReferenceExpr(
+      Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc,
+      FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs);
+
+  if (!Res.isInvalid() && isa<MemberExpr>(Res.get()))
+    CheckMemberAccessOfNoDeref(cast<MemberExpr>(Res.get()));
+
+  return Res;
+}
+
+void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) {
+  QualType ResultTy = E->getType();
+
+  // Do not warn on member accesses to arrays.
+  if (isa<ArrayType>(ResultTy))
+    return;
+
+  if (E->isArrow()) {
+    if (const auto *Ptr = dyn_cast<PointerType>(
+            E->getBase()->getType().getDesugaredType(Context))) {
+      if (TypeHasNoDeref(Ptr->getPointeeType()))
+        ExprEvalContexts.back().PossibleDerefs.insert(E);
+    }
+  }
 }
 
 ExprResult
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -4161,6 +4161,21 @@
   return isa<MSPropertySubscriptExpr>(BaseNoParens);
 }
 
+bool Sema::TypeHasNoDeref(QualType Ty) {
+  // Strip off everything but attributes.
+  QualType OldTy;
+  while (Ty != OldTy && !isa<AttributedType>(Ty)) {
+    OldTy = Ty;
+    Ty = Ty.getSingleStepDesugaredType(Context);
+  }
+
+  if (const auto *AT = dyn_cast<AttributedType>(Ty)) {
+    AttributedType::Kind AttrKind = AT->getAttrKind();
+    return AttrKind == attr::NoDeref;
+  }
+  return false;
+}
+
 ExprResult
 Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc,
                               Expr *idx, SourceLocation rbLoc) {
@@ -4234,7 +4249,63 @@
     return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, idx);
   }
 
-  return CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc);
+  ExprResult Res = CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc);
+
+  if (!Res.isInvalid() && isa<ArraySubscriptExpr>(Res.get()))
+    CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(Res.get()));
+
+  return Res;
+}
+
+void Sema::CheckAddressOfNoDeref(const Expr *E) {
+  ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back();
+  const Expr *StrippedExpr = E->IgnoreParenImpCasts();
+  auto FoundExpr = LastRecord.PossibleDerefs.find(StrippedExpr);
+  if (FoundExpr != LastRecord.PossibleDerefs.end()) {
+    LastRecord.PossibleDerefs.erase(*FoundExpr);
+  }
+
+  const Expr *Base = nullptr;
+  while (const auto *Member = dyn_cast<MemberExpr>(StrippedExpr)) {
+    Base = Member->getBase()->IgnoreParenImpCasts();
+    StrippedExpr = Base;
+  }
+
+  if (Base) {
+    // Getting the address of a member expr in the form &(*s).b
+    const QualType &BaseTy = Base->getType();
+    auto FoundBase = LastRecord.PossibleDerefs.find(Base);
+    if (TypeHasNoDeref(BaseTy) &&
+        FoundBase != LastRecord.PossibleDerefs.end()) {
+      LastRecord.PossibleDerefs.erase(*FoundBase);
+    }
+  }
+}
+
+void Sema::CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E) {
+  const QualType &ResultTy = E->getType();
+  ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back();
+
+  if (TypeHasNoDeref(ResultTy)) {
+    LastRecord.PossibleDerefs.insert(E);
+    return;
+  }
+
+  // Check if the base type is a pointer to a member access of a struct
+  // marked with noderef.
+  const Expr *Base = E->getBase();
+  const QualType &BaseTy = Base->getType();
+  if (!(isa<ArrayType>(BaseTy) || isa<PointerType>(BaseTy)))
+    // Not a pointer access
+    return;
+
+  if (const auto *Member = dyn_cast<MemberExpr>(Base->IgnoreParenCasts())) {
+    const QualType &MemberBaseTy = Member->getBase()->getType();
+    if (const auto *Ptr = dyn_cast<PointerType>(MemberBaseTy)) {
+      if (TypeHasNoDeref(Ptr->getPointeeType()))
+        LastRecord.PossibleDerefs.insert(E);
+    }
+  }
 }
 
 ExprResult Sema::ActOnOMPArraySectionExpr(Expr *Base, SourceLocation LBLoc,
@@ -12625,6 +12696,7 @@
     break;
   case UO_AddrOf:
     resultType = CheckAddressOfOperand(Input, OpLoc);
+    CheckAddressOfNoDeref(InputExpr);
     RecordModifiableNonNullParam(*this, InputExpr);
     break;
   case UO_Deref: {
@@ -12789,6 +12861,10 @@
 
   auto *UO = new (Context)
       UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow);
+
+  if (Opc == UO_Deref && TypeHasNoDeref(UO->getType()))
+    ExprEvalContexts.back().PossibleDerefs.insert(UO);
+
   // Convert the result back to a half vector.
   if (ConvertHalfVec)
     return convertVector(UO, Context.HalfTy, *this);
@@ -14148,6 +14224,65 @@
   PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext);
 }
 
+namespace {
+
+class DeclRefFinder : public ConstEvaluatedExprVisitor<DeclRefFinder> {
+public:
+  typedef ConstEvaluatedExprVisitor<DeclRefFinder> Inherited;
+
+  DeclRefFinder(Sema &S) : Inherited(S.Context), S(S) {}
+
+  void VisitDeclRefExpr(const DeclRefExpr *E) {
+    if (DeclRef)
+      return; // Found already
+
+    QualType Inner;
+    QualType Ty = E->getType().getDesugaredType(Context);
+    if (const auto *Ptr = dyn_cast<PointerType>(Ty))
+      Inner = Ptr->getPointeeType();
+    else if (const auto *Arr = dyn_cast<ArrayType>(Ty))
+      Inner = Arr->getElementType();
+    else
+      return;
+
+    if (S.TypeHasNoDeref(Inner))
+      DeclRef = E;
+  }
+
+  const DeclRefExpr *GetDeclRef() const { return DeclRef; }
+
+private:
+  Sema &S;
+  const DeclRefExpr *DeclRef = nullptr;
+};
+
+} // namespace
+
+void Sema::StopCheckingNoDerefAndWarn() {
+  ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back();
+  LastRecord.NoDerefCallCount--;
+  if (LastRecord.NoDerefCallCount == 0)
+    WarnOnPendingNoDerefs(LastRecord);
+}
+
+void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) {
+  for (const Expr *E : Rec.PossibleDerefs) {
+    DeclRefFinder Finder(*this);
+    Finder.Visit(E);
+    const DeclRefExpr *DeclRef = Finder.GetDeclRef();
+    if (DeclRef) {
+      const ValueDecl *Decl = DeclRef->getDecl();
+      Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type)
+          << Decl->getName() << E->getSourceRange();
+      Diag(Decl->getLocation(), diag::note_previous_decl) << Decl->getName();
+    } else {
+      Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type_no_decl)
+          << E->getSourceRange();
+    }
+  }
+  Rec.PossibleDerefs.clear();
+}
+
 void Sema::PopExpressionEvaluationContext() {
   ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back();
   unsigned NumTypos = Rec.NumTypos;
@@ -14187,6 +14322,8 @@
     }
   }
 
+  WarnOnPendingNoDerefs(Rec);
+
   // When are coming out of an unevaluated context, clear out any
   // temporaries that we may have created as part of the evaluation of
   // the expression in that context: they aren't relevant because they
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp
+++ lib/Parse/ParseExpr.cpp
@@ -1135,9 +1135,15 @@
   case tok::amp: {         // unary-expression: '&' cast-expression
     // Special treatment because of member pointers
     SourceLocation SavedLoc = ConsumeToken();
+
+    Actions.StartCheckingNoDeref();
+
     Res = ParseCastExpression(false, true);
     if (!Res.isInvalid())
       Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get());
+
+    Actions.StopCheckingNoDerefAndWarn();
+
     return Res;
   }
 
@@ -1149,9 +1155,15 @@
   case tok::kw___real:     // unary-expression: '__real' cast-expression [GNU]
   case tok::kw___imag: {   // unary-expression: '__imag' cast-expression [GNU]
     SourceLocation SavedLoc = ConsumeToken();
+
+    Actions.StartCheckingNoDeref();
+
     Res = ParseCastExpression(false);
     if (!Res.isInvalid())
       Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get());
+
+    Actions.StopCheckingNoDerefAndWarn();
+
     return Res;
   }
 
@@ -1573,8 +1585,12 @@
           LHS = Actions.ActOnOMPArraySectionExpr(LHS.get(), Loc, Idx.get(),
                                                  ColonLoc, Length.get(), RLoc);
         } else {
+          Actions.StartCheckingNoDeref();
+
           LHS = Actions.ActOnArraySubscriptExpr(getCurScope(), LHS.get(), Loc,
                                                 Idx.get(), RLoc);
+
+          Actions.StopCheckingNoDerefAndWarn();
         }
       } else {
         LHS = ExprError();
@@ -1799,11 +1815,16 @@
         LHS = ExprError();
       }
 
-      if (!LHS.isInvalid())
+      if (!LHS.isInvalid()) {
+        Actions.StartCheckingNoDeref();
+
         LHS = Actions.ActOnMemberAccessExpr(getCurScope(), LHS.get(), OpLoc,
                                             OpKind, SS, TemplateKWLoc, Name,
                                  CurParsedObjCImpl ? CurParsedObjCImpl->Dcl
                                                    : nullptr);
+
+        Actions.StopCheckingNoDerefAndWarn();
+      }
       if (!LHS.isInvalid() && Tok.is(tok::less))
         checkPotentialAngleBracket(LHS);
       break;
Index: lib/AST/TypePrinter.cpp
===================================================================
--- lib/AST/TypePrinter.cpp
+++ lib/AST/TypePrinter.cpp
@@ -1494,6 +1494,9 @@
   case attr::PreserveAll:
     OS << "preserve_all";
     break;
+  case attr::NoDeref:
+    OS << "noderef";
+    break;
   }
   OS << "))";
 }
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -983,6 +983,9 @@
     /// expressions for which we have deferred checking the destructor.
     SmallVector<CXXBindTemporaryExpr *, 8> DelayedDecltypeBinds;
 
+    unsigned NoDerefCallCount;
+    llvm::SmallPtrSet<const Expr *, 8> PossibleDerefs;
+
     /// \brief Describes whether we are in an expression constext which we have
     /// to handle differently.
     enum ExpressionKind {
@@ -997,7 +1000,7 @@
         : Context(Context), ParentCleanup(ParentCleanup),
           NumCleanupObjects(NumCleanupObjects), NumTypos(0),
           ManglingContextDecl(ManglingContextDecl), MangleNumbering(),
-          ExprContext(ExprContext) {}
+          NoDerefCallCount(0), ExprContext(ExprContext) {}
 
     /// Retrieve the mangling numbering context, used to consistently
     /// number constructs like lambdas for mangling.
@@ -1016,6 +1019,13 @@
   /// A stack of expression evaluation contexts.
   SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
 
+  /// Not all expressions need to be checked, so we only need to start keeping
+  /// track of noderef expressions on expressions that involve dereferencing
+  /// (indirection, member access, array subscript, ...).
+  void StartCheckingNoDeref() { ExprEvalContexts.back().NoDerefCallCount++; }
+  void StopCheckingNoDerefAndWarn();
+  void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec);
+
   /// Compute the mangling number context for a lambda expression or
   /// block literal.
   ///
@@ -1527,7 +1537,20 @@
     }
   };
 
+  bool TypeHasNoDeref(QualType Ty);
+
 private:
+  /// Methods for marking which expressions involve dereferencing a pointer
+  /// marked with the 'noderef' attribute. Expressions are checked bottom up as
+  /// they are parsed, meaning that a noderef pointer may not be accessed. For
+  /// example, in `&*p` where `p` is a noderef pointer, we will first parse the
+  /// `*p`, but need to check that `address of` is called on it. This requires
+  /// keeping a container of all pending expressions and checking if the address
+  /// of them are eventually taken.
+  void CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E);
+  void CheckAddressOfNoDeref(const Expr *E);
+  void CheckMemberAccessOfNoDeref(const MemberExpr *E);
+
   bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
                                TypeDiagnoser *Diagnoser);
 
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -9440,4 +9440,11 @@
    "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<
+  "dereferencing %0; was declared with a 'noderef' type">, InGroup<NoDeref>;
+def warn_dereference_of_noderef_type_no_decl : Warning<
+  "dereferencing expression marked as 'noderef'">, InGroup<NoDeref>;
+def warn_noderef_on_non_pointer_or_array : Warning<
+  "'noderef' can only be used on an array or pointer type">, InGroup<NoDeref>;
 } // end of sema component.
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -1030,3 +1030,5 @@
 // A warning group specifically for warnings related to function
 // multiversioning.
 def FunctionMultiVersioning : DiagGroup<"function-multiversion">;
+
+def NoDeref : DiagGroup<"noderef">;
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -3459,6 +3459,63 @@
   }];
 }
 
+def NoDerefDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``noderef`` attribute causes clang to diagnose dereferences of annotated pointer types.
+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 dereferences are diagnosed:
+
+.. 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 diagnose a warning if the value directed by the pointer may not be
+accessed. The following are examples of valid expressions where may not be diagnosed:
+
+.. 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;
+
+``noderef`` is currently only supported for pointers and arrays and not usable for
+references or Objective-C object pointers.
+
+.. code-block: c++
+
+  int x = 2;
+  int &y = x;  // warning: 'noderef' can only be used on an array or pointer type
+
+.. code-block: objc
+
+  id NODEREF obj = [NSObject new]; // warning: 'noderef' can only be used on an array or pointer type
+}];
+}
+
 def ReinitializesDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1794,6 +1794,11 @@
   let ASTNode = 0;
 }
 
+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">];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to