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 @@ -1749,6 +1749,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 @@ -3419,3 +3419,49 @@ corresponding line within the inlined callee. }]; } + +def NoDerefDocs : Documentation { + let Category = DocCatType; + let Content = [{ +The ``noderef`` attribute causes clang to throw 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/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -1026,3 +1026,6 @@ // A warning group specifically for warnings related to function // multiversioning. def FunctionMultiVersioning : DiagGroup<"function-multiversion">; + +// Warning group for warnings related to dereferencing of noderef pointers +def DereferencedNoDeref : DiagGroup<"dereferenced-noderef">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -9415,4 +9415,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 @@ -58,6 +58,7 @@ #include #include #include +#include #include namespace llvm { @@ -983,7 +984,7 @@ /// expressions for which we have deferred checking the destructor. SmallVector DelayedDecltypeBinds; - /// \brief Describes whether we are in an expression constext which we have + /// \brief Describes whether we are in an expression context which we have /// to handle differently. enum ExpressionKind { EK_Decltype, EK_TemplateArgument, EK_Other @@ -1016,6 +1017,30 @@ /// A stack of expression evaluation contexts. SmallVector ExprEvalContexts; + /// 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); + + /// 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() { NoDerefCallCount++; } + void StopCheckingNoDerefAndWarn(); + + static bool TypeHasNoDeref(const QualType &Ty); + +private: + unsigned NoDerefCallCount = 0; + std::unordered_set PossibleDerefs; + +public: /// Compute the mangling number context for a lambda expression or /// block literal. /// 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/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -1107,9 +1107,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; } @@ -1121,9 +1127,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; } @@ -1545,8 +1557,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(); @@ -1770,12 +1786,17 @@ (void)Actions.CorrectDelayedTyposInExpr(LHS); 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/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -4164,6 +4164,14 @@ return isa(BaseNoParens); } +bool Sema::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) { @@ -4237,7 +4245,60 @@ return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, idx); } - return CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc); + ExprResult Res = CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc); + + if (!Res.isInvalid() && isa(Res.get())) + CheckSubscriptAccessOfNoDeref(cast(Res.get())); + + return Res; +} + +void Sema::CheckAddressOfNoDeref(const Expr *E) { + const Expr *StrippedExpr = E->IgnoreParenImpCasts(); + if (PossibleDerefs.find(StrippedExpr) != PossibleDerefs.end()) { + PossibleDerefs.erase(StrippedExpr); + } else if (const MemberExpr *Member = dyn_cast(StrippedExpr)) { + // Getting the address of a member expr in the form &(*s).b + const Expr *Base = Member->getBase()->IgnoreParenImpCasts(); + const QualType &BaseTy = Base->getType(); + if (TypeHasNoDeref(BaseTy) && + PossibleDerefs.find(Base) != PossibleDerefs.end()) { + PossibleDerefs.erase(Base); + } + } +} + +void Sema::CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E) { + const QualType &ResultTy = E->getType(); + + if (TypeHasNoDeref(ResultTy)) { + PossibleDerefs.insert(E); + return; + } + + // Raise an error 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 ArrayType *ArrayTy = dyn_cast(BaseTy)) { + ElemTy = ArrayTy->getElementType(); + } else if (const PointerType *PointerTy = dyn_cast(BaseTy)) { + ElemTy = PointerTy->getPointeeType(); + } else { + // Not a pointer access + return; + } + + if (const MemberExpr *Member = + dyn_cast(Base->IgnoreParenCasts())) { + const QualType &MemberBaseTy = Member->getBase()->getType(); + if (const PointerType *Ptr = dyn_cast(MemberBaseTy)) { + if (TypeHasNoDeref(Ptr->getPointeeType())) { + PossibleDerefs.insert(E); + } + } + } } ExprResult Sema::ActOnOMPArraySectionExpr(Expr *Base, SourceLocation LBLoc, @@ -12636,6 +12697,7 @@ break; case UO_AddrOf: resultType = CheckAddressOfOperand(Input, OpLoc); + CheckAddressOfNoDeref(InputExpr); RecordModifiableNonNullParam(*this, InputExpr); break; case UO_Deref: { @@ -12800,6 +12862,10 @@ auto *UO = new (Context) UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow); + + if (Opc == UO_Deref && TypeHasNoDeref(UO->getType())) + PossibleDerefs.insert(UO); + // Convert the result back to a half vector. if (ConvertHalfVec) return convertVector(UO, Context.HalfTy, *this); @@ -14161,6 +14227,17 @@ PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext); } +void Sema::StopCheckingNoDerefAndWarn() { + NoDerefCallCount--; + if (NoDerefCallCount == 0) { + for (const Expr *E : PossibleDerefs) { + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type) + << E->getSourceRange(); + } + PossibleDerefs.clear(); + } +} + void Sema::PopExpressionEvaluationContext() { ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); unsigned NumTypos = Rec.NumTypos; 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(Res.get())) + CheckMemberAccessOfNoDeref(cast(Res.get())); + + return Res; +} + +void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) { + QualType ResultTy = E->getType(); + + // Do not warn on member accesses to arrays + if (isa(ResultTy)) + return; + + if (E->isArrow()) { + if (const PointerType *Ptr = + dyn_cast(E->getBase()->getType())) { + if (TypeHasNoDeref(Ptr->getPointeeType())) + PossibleDerefs.insert(E); + } + } } ExprResult 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,164 @@ +// 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}} + p = q[*p]; // expected-warning{{dereference of noderef expression}} + x = p[*p]; // expected-warning{{dereference of noderef expression}} + // expected-warning@-1{{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}} +}