r348442 - [Sema/Attribute] Check for noderef attribute

Leonard Chan via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 5 17:05:54 PST 2018


Author: leonardchan
Date: Wed Dec  5 17:05:54 2018
New Revision: 348442

URL: http://llvm.org/viewvc/llvm-project?rev=348442&view=rev
Log:
[Sema/Attribute] Check for noderef attribute

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.

Differential Revision: https://reviews.llvm.org/D49511

Added:
    cfe/trunk/test/Frontend/noderef.c
    cfe/trunk/test/Frontend/noderef.cpp
    cfe/trunk/test/Frontend/noderef_on_non_pointers.m
    cfe/trunk/test/Frontend/noderef_templates.cpp
Modified:
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/AttrDocs.td
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/TypePrinter.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaExprMember.cpp
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/lib/Sema/SemaType.cpp

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Wed Dec  5 17:05:54 2018
@@ -1822,6 +1822,11 @@ def Regparm : TypeAttr {
   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">];

Modified: cfe/trunk/include/clang/Basic/AttrDocs.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrDocs.td?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/AttrDocs.td (original)
+++ cfe/trunk/include/clang/Basic/AttrDocs.td Wed Dec  5 17:05:54 2018
@@ -3593,6 +3593,63 @@ corresponding line within the inlined ca
   }];
 }
 
+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 __attribute__((noderef)) &y = x;  // warning: 'noderef' can only be used on an array or pointer type
+
+.. code-block: objc
+
+  id __attribute__((noderef)) obj = [NSObject new]; // warning: 'noderef' can only be used on an array or pointer type
+}];
+}
+
 def ReinitializesDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Wed Dec  5 17:05:54 2018
@@ -1039,3 +1039,5 @@ def ExperimentalISel : DiagGroup<"experi
 // A warning group specifically for warnings related to function
 // multiversioning.
 def FunctionMultiVersioning : DiagGroup<"function-multiversion">;
+
+def NoDeref : DiagGroup<"noderef">;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Dec  5 17:05:54 2018
@@ -9548,4 +9548,13 @@ def err_std_compare_type_not_supported :
    "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<IgnoredAttributes>;
+def warn_noderef_to_dereferenceable_pointer : Warning<
+  "casting to dereferenceable pointer removes 'noderef' attribute">, InGroup<NoDeref>;
 } // end of sema component.

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Wed Dec  5 17:05:54 2018
@@ -999,6 +999,8 @@ public:
     /// expressions for which we have deferred checking the destructor.
     SmallVector<CXXBindTemporaryExpr *, 8> DelayedDecltypeBinds;
 
+    llvm::SmallPtrSet<const Expr *, 8> PossibleDerefs;
+
     /// \brief Describes whether we are in an expression constext which we have
     /// to handle differently.
     enum ExpressionKind {
@@ -1032,6 +1034,9 @@ public:
   /// A stack of expression evaluation contexts.
   SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
 
+  /// Emit a warning for all pending noderef expressions that we recorded.
+  void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec);
+
   /// Compute the mangling number context for a lambda expression or
   /// block literal.
   ///
@@ -1544,6 +1549,17 @@ public:
   };
 
 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);
 

Modified: cfe/trunk/lib/AST/TypePrinter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TypePrinter.cpp?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/lib/AST/TypePrinter.cpp (original)
+++ cfe/trunk/lib/AST/TypePrinter.cpp Wed Dec  5 17:05:54 2018
@@ -1508,6 +1508,9 @@ void TypePrinter::printAttributedAfter(c
   case attr::PreserveAll:
     OS << "preserve_all";
     break;
+  case attr::NoDeref:
+    OS << "noderef";
+    break;
   }
   OS << "))";
 }

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Wed Dec  5 17:05:54 2018
@@ -4288,7 +4288,57 @@ Sema::ActOnArraySubscriptExpr(Scope *S,
     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();
+
+  // For expressions like `&(*s).b`, the base is recorded and what should be
+  // checked.
+  const MemberExpr *Member = nullptr;
+  while ((Member = dyn_cast<MemberExpr>(StrippedExpr)) && !Member->isArrow())
+    StrippedExpr = Member->getBase()->IgnoreParenImpCasts();
+
+  LastRecord.PossibleDerefs.erase(StrippedExpr);
+}
+
+void Sema::CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E) {
+  QualType ResultTy = E->getType();
+  ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back();
+
+  // Bail if the element is an array since it is not memory access.
+  if (isa<ArrayType>(ResultTy))
+    return;
+
+  if (ResultTy->hasAttr(attr::NoDeref)) {
+    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();
+  QualType BaseTy = Base->getType();
+  if (!(isa<ArrayType>(BaseTy) || isa<PointerType>(BaseTy)))
+    // Not a pointer access
+    return;
+
+  const MemberExpr *Member = nullptr;
+  while ((Member = dyn_cast<MemberExpr>(Base->IgnoreParenCasts())) &&
+         Member->isArrow())
+    Base = Member->getBase();
+
+  if (const auto *Ptr = dyn_cast<PointerType>(Base->getType())) {
+    if (Ptr->getPointeeType()->hasAttr(attr::NoDeref))
+      LastRecord.PossibleDerefs.insert(E);
+  }
 }
 
 ExprResult Sema::ActOnOMPArraySectionExpr(Expr *Base, SourceLocation LBLoc,
@@ -8157,6 +8207,17 @@ Sema::CheckSingleAssignmentConstraints(Q
   ExprResult LocalRHS = CallerRHS;
   ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS;
 
+  if (const auto *LHSPtrType = LHSType->getAs<PointerType>()) {
+    if (const auto *RHSPtrType = RHS.get()->getType()->getAs<PointerType>()) {
+      if (RHSPtrType->getPointeeType()->hasAttr(attr::NoDeref) &&
+          !LHSPtrType->getPointeeType()->hasAttr(attr::NoDeref)) {
+        Diag(RHS.get()->getExprLoc(),
+             diag::warn_noderef_to_dereferenceable_pointer)
+            << RHS.get()->getSourceRange();
+      }
+    }
+  }
+
   if (getLangOpts().CPlusPlus) {
     if (!LHSType->isRecordType() && !LHSType->isAtomicType()) {
       // C++ 5.17p3: If the left operand is not of class type, the
@@ -8277,6 +8338,7 @@ Sema::CheckSingleAssignmentConstraints(Q
     if (ConvertRHS)
       RHS = ImpCastExprToType(E, Ty, Kind);
   }
+
   return result;
 }
 
@@ -12825,6 +12887,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(So
     break;
   case UO_AddrOf:
     resultType = CheckAddressOfOperand(Input, OpLoc);
+    CheckAddressOfNoDeref(InputExpr);
     RecordModifiableNonNullParam(*this, InputExpr);
     break;
   case UO_Deref: {
@@ -12989,6 +13052,11 @@ ExprResult Sema::CreateBuiltinUnaryOp(So
 
   auto *UO = new (Context)
       UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow);
+
+  if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) &&
+      !isa<ArrayType>(UO->getType().getDesugaredType(Context)))
+    ExprEvalContexts.back().PossibleDerefs.insert(UO);
+
   // Convert the result back to a half vector.
   if (ConvertHalfVec)
     return convertVector(UO, Context.HalfTy, *this);
@@ -14355,6 +14423,51 @@ Sema::PushExpressionEvaluationContext(
   PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext);
 }
 
+namespace {
+
+const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) {
+  PossibleDeref = PossibleDeref->IgnoreParenImpCasts();
+  if (const auto *E = dyn_cast<UnaryOperator>(PossibleDeref)) {
+    if (E->getOpcode() == UO_Deref)
+      return CheckPossibleDeref(S, E->getSubExpr());
+  } else if (const auto *E = dyn_cast<ArraySubscriptExpr>(PossibleDeref)) {
+    return CheckPossibleDeref(S, E->getBase());
+  } else if (const auto *E = dyn_cast<MemberExpr>(PossibleDeref)) {
+    return CheckPossibleDeref(S, E->getBase());
+  } else if (const auto E = dyn_cast<DeclRefExpr>(PossibleDeref)) {
+    QualType Inner;
+    QualType Ty = E->getType();
+    if (const auto *Ptr = Ty->getAs<PointerType>())
+      Inner = Ptr->getPointeeType();
+    else if (const auto *Arr = S.Context.getAsArrayType(Ty))
+      Inner = Arr->getElementType();
+    else
+      return nullptr;
+
+    if (Inner->hasAttr(attr::NoDeref))
+      return E;
+  }
+  return nullptr;
+}
+
+} // namespace
+
+void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) {
+  for (const Expr *E : Rec.PossibleDerefs) {
+    const DeclRefExpr *DeclRef = CheckPossibleDeref(*this, E);
+    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;
@@ -14394,6 +14507,8 @@ void Sema::PopExpressionEvaluationContex
     }
   }
 
+  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

Modified: cfe/trunk/lib/Sema/SemaExprMember.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprMember.cpp?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprMember.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprMember.cpp Wed Dec  5 17:05:54 2018
@@ -1708,9 +1708,31 @@ ExprResult Sema::ActOnMemberAccessExpr(S
   }
 
   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 since this returns an array
+  // lvalue and does not actually dereference memory.
+  if (isa<ArrayType>(ResultTy))
+    return;
+
+  if (E->isArrow()) {
+    if (const auto *Ptr = dyn_cast<PointerType>(
+            E->getBase()->getType().getDesugaredType(Context))) {
+      if (Ptr->getPointeeType()->hasAttr(attr::NoDeref))
+        ExprEvalContexts.back().PossibleDerefs.insert(E);
+    }
+  }
 }
 
 ExprResult

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Wed Dec  5 17:05:54 2018
@@ -7687,6 +7687,18 @@ ExprResult InitializationSequence::Perfo
 
     case SK_ConversionSequence:
     case SK_ConversionSequenceNoNarrowing: {
+      if (const auto *FromPtrType =
+              CurInit.get()->getType()->getAs<PointerType>()) {
+        if (const auto *ToPtrType = Step->Type->getAs<PointerType>()) {
+          if (FromPtrType->getPointeeType()->hasAttr(attr::NoDeref) &&
+              !ToPtrType->getPointeeType()->hasAttr(attr::NoDeref)) {
+            S.Diag(CurInit.get()->getExprLoc(),
+                   diag::warn_noderef_to_dereferenceable_pointer)
+                << CurInit.get()->getSourceRange();
+          }
+        }
+      }
+
       Sema::CheckedConversionKind CCK
         = Kind.isCStyleCast()? Sema::CCK_CStyleCast
         : Kind.isFunctionalCast()? Sema::CCK_FunctionalCast
@@ -7853,6 +7865,7 @@ ExprResult InitializationSequence::Perfo
 
     case SK_CAssignment: {
       QualType SourceType = CurInit.get()->getType();
+
       // Save off the initial CurInit in case we need to emit a diagnostic
       ExprResult InitialCurInit = CurInit;
       ExprResult Result = CurInit;

Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=348442&r1=348441&r2=348442&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Wed Dec  5 17:05:54 2018
@@ -183,11 +183,15 @@ namespace {
     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;
@@ -278,6 +282,10 @@ namespace {
       llvm_unreachable("no Attr* for AttributedType*");
     }
 
+    void setParsedNoDeref(bool parsed) { parsedNoDeref = parsed; }
+
+    bool didParseNoDeref() const { return parsedNoDeref; }
+
     ~TypeProcessingState() {
       if (trivial) return;
 
@@ -3887,6 +3895,11 @@ static bool hasOuterPointerLikeChunk(con
   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();
@@ -4280,6 +4293,9 @@ static TypeSourceInfo *GetFullTypeForDec
     }
   }
 
+  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 :).
@@ -4889,8 +4905,22 @@ static TypeSourceInfo *GetFullTypeForDec
 
     // 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
@@ -7271,6 +7301,9 @@ static void processTypeAttrs(TypeProcess
   // 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.
@@ -7368,6 +7401,15 @@ static void processTypeAttrs(TypeProcess
         HandleLifetimeBoundAttr(state, type, attr);
       break;
 
+    case ParsedAttr::AT_NoDeref: {
+      ASTContext &Ctx = state.getSema().Context;
+      type = state.getAttributedType(createSimpleAttr<NoDerefAttr>(Ctx, attr),
+                                     type, type);
+      attr.setUsedAsTypeAttr();
+      state.setParsedNoDeref(true);
+      break;
+    }
+
     MS_TYPE_ATTRS_CASELIST:
       if (!handleMSPointerTypeQualifierAttr(state, attr, type))
         attr.setUsedAsTypeAttr();

Added: cfe/trunk/test/Frontend/noderef.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Frontend/noderef.c?rev=348442&view=auto
==============================================================================
--- cfe/trunk/test/Frontend/noderef.c (added)
+++ cfe/trunk/test/Frontend/noderef.c Wed Dec  5 17:05:54 2018
@@ -0,0 +1,209 @@
+// 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 34 {{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
+  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 expression marked as 'noderef'}}
+
+  *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}}
+
+  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}}
+  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 at -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];
+  struct S2 NODEREF *s2_arr2[10][10];
+
+  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];
+
+  // Casting to dereferenceable pointer
+  p2 = p;             // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+  p2 = *q;            // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+  p2 = q[0];          // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+  s2 = s2_arr[1];     // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+  s2 = s2_arr2[1][1]; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+  p2 = p, p2 = *q;    // expected-warning 2 {{casting to dereferenceable pointer removes 'noderef' attribute}}
+
+  // 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 expression marked as 'noderef'}}
+
+  (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 at -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 expression marked as 'noderef'}}
+
+  // 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 at -1{{dereferencing p; was declared with a 'noderef' type}}
+                      // expected-warning at -2{{dereferencing p; was declared with a 'noderef' type}}
+  for (*p; *p;){}     // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+                      // expected-warning at -1{{dereferencing p; was declared with a 'noderef' type}}
+  for (*p;; *p){}     // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+                      // expected-warning at -1{{dereferencing p; was declared with a 'noderef' type}}
+  for (; *p; *p){}    // expected-warning{{dereferencing p; was declared with a 'noderef' type}}
+                      // expected-warning at -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}}
+}

Added: cfe/trunk/test/Frontend/noderef.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Frontend/noderef.cpp?rev=348442&view=auto
==============================================================================
--- cfe/trunk/test/Frontend/noderef.cpp (added)
+++ cfe/trunk/test/Frontend/noderef.cpp Wed Dec  5 17:05:54 2018
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -fblocks -verify %s
+
+/**
+ * Test 'noderef' attribute with c++ constructs.
+ */
+
+#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;   // expected-note 2 {{i_ptr declared here}}
+  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}}
+
+  int i2 = *i_ptr;     // expected-warning{{dereferencing i_ptr; was declared with a 'noderef' type}}
+  int &i3 = *i_ptr;    // expected-warning{{dereferencing i_ptr; was declared with a 'noderef' type}}
+  int *i_ptr5 = i_ptr; // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+  int *i_ptr6(i_ptr);  // expected-warning{{casting to dereferenceable pointer removes 'noderef' attribute}}
+}
+
+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}}
+};
+
+void test_lambdas() {
+  auto l = [](int NODEREF *x){  // expected-note{{x declared here}}
+    return *x;  // expected-warning{{dereferencing x; was declared with a 'noderef' type}}
+  };
+}
+
+int NODEREF *glob_ptr;  // expected-note{{glob_ptr declared here}}
+int glob_int = *glob_ptr;  // expected-warning{{dereferencing glob_ptr; was declared with a 'noderef' type}}

Added: cfe/trunk/test/Frontend/noderef_on_non_pointers.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Frontend/noderef_on_non_pointers.m?rev=348442&view=auto
==============================================================================
--- cfe/trunk/test/Frontend/noderef_on_non_pointers.m (added)
+++ cfe/trunk/test/Frontend/noderef_on_non_pointers.m Wed Dec  5 17:05:54 2018
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -verify %s
+
+#define NODEREF __attribute__((noderef))
+
+ at interface NSObject
++ (id)new;
+ at end
+
+void func() {
+  id NODEREF obj = [NSObject new]; // expected-warning{{'noderef' can only be used on an array or pointer type}}
+}

Added: cfe/trunk/test/Frontend/noderef_templates.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Frontend/noderef_templates.cpp?rev=348442&view=auto
==============================================================================
--- cfe/trunk/test/Frontend/noderef_templates.cpp (added)
+++ cfe/trunk/test/Frontend/noderef_templates.cpp Wed Dec  5 17:05:54 2018
@@ -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}}
+}




More information about the cfe-commits mailing list