leonardchan updated this revision to Diff 156634.
leonardchan marked 2 inline comments as done.
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
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}}
+}
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: lib/Sema/SemaExprMember.cpp
===================================================================
--- lib/Sema/SemaExprMember.cpp
+++ lib/Sema/SemaExprMember.cpp
@@ -1708,9 +1708,30 @@
}
ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl};
- return BuildMemberReferenceExpr(Base, Base->getType(), OpLoc, IsArrow, SS,
- TemplateKWLoc, FirstQualifierInScope,
- NameInfo, TemplateArgs, S, &ExtraArgs);
+ ExprResult Res = BuildMemberReferenceExpr(
+ Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc,
+ FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs);
+
+ if (!Res.isInvalid() && isa<MemberExpr>(Res.get()))
+ CheckMemberAccessOfNoDeref(cast<MemberExpr>(Res.get()));
+
+ return Res;
+}
+
+void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) {
+ QualType ResultTy = E->getType();
+
+ // Do not warn on member accesses to arrays
+ if (isa<ArrayType>(ResultTy))
+ return;
+
+ if (E->isArrow()) {
+ if (const PointerType *Ptr =
+ dyn_cast<PointerType>(E->getBase()->getType())) {
+ if (TypeHasNoDeref(Ptr->getPointeeType()))
+ PossibleDerefs.insert(E);
+ }
+ }
}
ExprResult
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -4164,6 +4164,14 @@
return isa<MSPropertySubscriptExpr>(BaseNoParens);
}
+bool Sema::TypeHasNoDeref(const QualType &Ty) {
+ if (const AttributedType *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) {
@@ -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<ArraySubscriptExpr>(Res.get()))
+ CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(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<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();
+ 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<ArrayType>(BaseTy)) {
+ ElemTy = ArrayTy->getElementType();
+ } else if (const PointerType *PointerTy = dyn_cast<PointerType>(BaseTy)) {
+ ElemTy = PointerTy->getPointeeType();
+ } else {
+ // Not a pointer access
+ return;
+ }
+
+ if (const MemberExpr *Member =
+ dyn_cast<MemberExpr>(Base->IgnoreParenCasts())) {
+ const QualType &MemberBaseTy = Member->getBase()->getType();
+ if (const PointerType *Ptr = dyn_cast<PointerType>(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/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/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/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: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -58,6 +58,7 @@
#include <deque>
#include <memory>
#include <string>
+#include <unordered_set>
#include <vector>
namespace llvm {
@@ -983,7 +984,7 @@
/// expressions for which we have deferred checking the destructor.
SmallVector<CXXBindTemporaryExpr *, 8> 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<ExpressionEvaluationContextRecord, 8> 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<const Expr *> PossibleDerefs;
+
+public:
/// Compute the mangling number context for a lambda expression or
/// block literal.
///
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<DereferencedNoDeref>;
} // end of sema component.
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/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/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/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,
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits