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