leonardchan updated this revision to Diff 158423.
leonardchan added a comment.

- Moved the counter and set into `ExpressionEvaluationContextRecord`


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/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/AST/Type.cpp
  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

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,154 @@
+// 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 31 {{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 3 {{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;
+
+  // 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];
+
+  // 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
@@ -3834,6 +3834,11 @@
   return false;
 }
 
+static bool IsNoDerefableChunk(DeclaratorChunk Chunk) {
+  return (Chunk.Kind == DeclaratorChunk::Pointer ||
+          Chunk.Kind == DeclaratorChunk::Array);
+}
+
 static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
                                                 QualType declSpecType,
                                                 TypeSourceInfo *TInfo) {
@@ -4202,6 +4207,11 @@
     }
   }
 
+  bool IsNoDeref = false;
+  if (const auto *AT = dyn_cast<AttributedType>(T))
+    IsNoDeref = AT->getAttrKind() == AttributedType::attr_noderef;
+  bool ExpectNoDerefChunk = IsNoDeref;
+
   // Walk the DeclTypeInfo, building the recursive type as we go.
   // DeclTypeInfos are ordered from the identifier out, which is
   // opposite of what we want :).
@@ -4805,8 +4815,27 @@
 
     // See if there are any attributes on this declarator chunk.
     processTypeAttrs(state, T, TAL_DeclChunk, DeclType.getAttrs());
+
+    if (const auto *AT = dyn_cast<AttributedType>(T))
+      IsNoDeref = AT->getAttrKind() == AttributedType::attr_noderef;
+    else
+      IsNoDeref = false;
+
+    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 = IsNoDeref;
+    }
   }
 
+  if (ExpectNoDerefChunk)
+    S.Diag(state.getDeclarator().getLocStart(),
+           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
@@ -5215,6 +5244,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 +7330,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/SemaExprMember.cpp
===================================================================
--- lib/Sema/SemaExprMember.cpp
+++ lib/Sema/SemaExprMember.cpp
@@ -1708,9 +1708,29 @@
   }
 
   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())) {
+      if (TypeHasNoDeref(Ptr->getPointeeType()))
+        ExprEvalContexts.back().PossibleDerefs.insert(E);
+    }
+  }
 }
 
 ExprResult
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -4165,6 +4165,14 @@
   return isa<MSPropertySubscriptExpr>(BaseNoParens);
 }
 
+bool Sema::TypeHasNoDeref(const QualType &Ty) {
+  if (const auto *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) {
@@ -4238,7 +4246,61 @@
     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);
+  } else if (const auto *Member = dyn_cast<MemberExpr>(StrippedExpr)) {
+    // Getting the address of a member expr in the form &(*s).b
+    const Expr *Base = Member->getBase()->IgnoreParenImpCasts();
+    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();
+  QualType ElemTy;
+  if (const auto *ArrayTy = dyn_cast<ArrayType>(BaseTy))
+    ElemTy = ArrayTy->getElementType();
+  else if (const auto *PointerTy = dyn_cast<PointerType>(BaseTy))
+    ElemTy = PointerTy->getPointeeType();
+  else
+    // 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,
@@ -12643,6 +12705,7 @@
     break;
   case UO_AddrOf:
     resultType = CheckAddressOfOperand(Input, OpLoc);
+    CheckAddressOfNoDeref(InputExpr);
     RecordModifiableNonNullParam(*this, InputExpr);
     break;
   case UO_Deref: {
@@ -12807,6 +12870,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);
@@ -14165,6 +14232,61 @@
   PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext);
 }
 
+namespace {
+
+class DeclRefFinder : public ConstEvaluatedExprVisitor<DeclRefFinder> {
+public:
+  typedef ConstEvaluatedExprVisitor<DeclRefFinder> Inherited;
+
+  DeclRefFinder(ASTContext &Ctx) : Inherited(Ctx) {}
+
+  void VisitDeclRefExpr(const DeclRefExpr *E) {
+    if (DeclRef)
+      return; // Found already
+
+    QualType Inner;
+    QualType Ty = E->getType();
+    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 (Sema::TypeHasNoDeref(Inner))
+      DeclRef = E;
+  }
+
+  const DeclRefExpr *GetDeclRef() const { return DeclRef; }
+
+private:
+  const DeclRefExpr *DeclRef = nullptr;
+};
+
+} // namespace
+
+void Sema::StopCheckingNoDerefAndWarn() {
+  ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back();
+  LastRecord.NoDerefCallCount--;
+  if (LastRecord.NoDerefCallCount == 0) {
+    for (const Expr *E : LastRecord.PossibleDerefs) {
+      DeclRefFinder Finder(Context);
+      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();
+      }
+    }
+    LastRecord.PossibleDerefs.clear();
+  }
+}
+
 void Sema::PopExpressionEvaluationContext() {
   ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back();
   unsigned NumTypos = Rec.NumTypos;
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp
+++ lib/Parse/ParseExpr.cpp
@@ -1122,9 +1122,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;
   }
 
@@ -1136,9 +1142,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;
   }
 
@@ -1560,8 +1572,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();
@@ -1786,11 +1802,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
@@ -1527,6 +1527,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
@@ -3202,6 +3202,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:
@@ -3228,6 +3229,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
@@ -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,12 @@
   /// 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();
+
   /// Compute the mangling number context for a lambda expression or
   /// block literal.
   ///
@@ -1529,7 +1538,20 @@
     }
   };
 
+  static bool TypeHasNoDeref(const 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
@@ -9431,4 +9431,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
@@ -3442,3 +3442,60 @@
 corresponding line within the inlined callee.
   }];
 }
+
+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 C style pointers and arrays and not usable for
+references or Objective-C 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
+}];
+}
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1761,6 +1761,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
@@ -4212,6 +4212,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