leonardchan updated this revision to Diff 161785. leonardchan marked 6 inline comments as done and an inline comment as not done.
Repository: rC Clang https://reviews.llvm.org/D49511 Files: 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/TypePrinter.cpp lib/Parse/ParseExpr.cpp lib/Sema/SemaExpr.cpp lib/Sema/SemaExprMember.cpp lib/Sema/SemaType.cpp test/Frontend/noderef.c test/Frontend/noderef_on_non_pointers.cpp test/Frontend/noderef_on_non_pointers.m test/Frontend/noderef_templates.cpp
Index: test/Frontend/noderef_templates.cpp =================================================================== --- /dev/null +++ test/Frontend/noderef_templates.cpp @@ -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}} +} Index: test/Frontend/noderef_on_non_pointers.m =================================================================== --- /dev/null +++ test/Frontend/noderef_on_non_pointers.m @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -verify %s + +#define NODEREF __attribute__((noderef)) + +@interface NSObject ++ (id)new; +@end + +void func() { + id NODEREF obj = [NSObject new]; // expected-warning{{'noderef' can only be used on an array or pointer type}} +} Index: test/Frontend/noderef_on_non_pointers.cpp =================================================================== --- /dev/null +++ test/Frontend/noderef_on_non_pointers.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -fblocks -verify %s + +/** + * Test 'noderef' attribute against other pointer-like types. + */ + +#define NODEREF __attribute__((noderef)) + +void Normal() { + int NODEREF i; // expected-warning{{'noderef' can only be used on an array or pointer type}} + int NODEREF *i_ptr; // ok + int NODEREF **i_ptr2; // ok + int *NODEREF i_ptr3; // expected-warning{{'noderef' can only be used on an array or pointer type}} + int *NODEREF *i_ptr4; // ok + + auto NODEREF *auto_i_ptr = i_ptr; + auto NODEREF auto_i = i; // expected-warning{{'noderef' can only be used on an array or pointer type}} + + struct { + int x; + int y; + } NODEREF *s; + + int __attribute__((noderef(10))) * no_args; // expected-error{{'noderef' attribute takes no arguments}} +} + +const int NODEREF *const_i_ptr; +static int NODEREF *static_i_ptr; + +void ParenTypes() { + int NODEREF(*i_ptr); // ok (same as `int NODEREF *`) + int NODEREF *(*i_ptr2); // ok (same as `int NODEREF **`) +} + +// Function declarations +int NODEREF func(); // expected-warning{{'noderef' can only be used on an array or pointer type}} +int NODEREF *func2(); // ok (returning pointer) + +typedef int NODEREF (*func3)(int); // expected-warning{{'noderef' can only be used on an array or pointer type}} +typedef int NODEREF *(*func4)(int); + +void Arrays() { + int NODEREF i_arr[10]; // ok + int NODEREF i_arr2[10][10]; // ok + int NODEREF *i_arr3[10]; // ok + int NODEREF i_arr4[] = {1, 2}; +} + +void ParenArrays() { + int NODEREF(i_ptr[10]); + int NODEREF(i_ptr2[10])[10]; +} + +typedef int NODEREF *(*func5[10])(int); + +// Arguments +void func6(int NODEREF x); // expected-warning{{'noderef' can only be used on an array or pointer type}} +void func7(int NODEREF *x); +void func8() NODEREF; + +void References() { + int x = 2; + int NODEREF &y = x; // expected-warning{{'noderef' can only be used on an array or pointer type}} + int *xp = &x; + int NODEREF *&a = xp; // ok (reference to a NODEREF *) + int *NODEREF &b = xp; // expected-warning{{'noderef' can only be used on an array or pointer type}} +} + +void BlockPointers() { + typedef int NODEREF (^IntBlock)(); // expected-warning{{'noderef' can only be used on an array or pointer type}} +} + +class A { +public: + int member; + int NODEREF *member2; + int NODEREF member3; // expected-warning{{'noderef' can only be used on an array or pointer type}} +}; + +void MemberPointer() { + int NODEREF A::*var = &A::member; // expected-warning{{'noderef' can only be used on an array or pointer type}} +} + +template <class Ty> +class B { + Ty NODEREF *member; + Ty NODEREF member2; // expected-warning{{'noderef' can only be used on an array or pointer type}} +}; Index: test/Frontend/noderef.c =================================================================== --- /dev/null +++ test/Frontend/noderef.c @@ -0,0 +1,206 @@ +// 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 37 {{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 + p2 = *q; // ok + x = **q; // expected-warning{{dereferencing expression marked as 'noderef'}} + p2 = *q2; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}} + + **q; // expected-warning{{dereferencing expression marked as 'noderef'}} + + p = *&*q; + p = **&q; + q = &**&q; + p = &*p; + p = *&p; + p = &(*p); + p = *(&p); + x = **&p; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + + *p = 2; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + *q = p; // ok + **q = 2; // expected-warning{{dereferencing expression marked as 'noderef'}} + *q2 = p2; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}} + + p2 = p; + p = p + 1; + p = &*(p + 1); + + // Struct member access + struct S NODEREF *s; // expected-note 2 {{s declared here}} + x = s->a; // expected-warning{{dereferencing s; was declared with a 'noderef' type}} + x = (*s).b; // expected-warning{{dereferencing s; was declared with a 'noderef' type}} + p = &s->a; + p = &(*s).b; + + // Nested struct access + struct S2 NODEREF *s2_noderef; // expected-note 5 {{s2_noderef declared here}} + p = s2_noderef->a; // ok since result is an array in a struct + p = s2_noderef->a2; // ok + p = s2_noderef->b; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + p = s2_noderef->b2; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + s = s2_noderef->s; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + s = s2_noderef->s2; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + p = s2_noderef->a + 1; + + struct S2 *s2; + p = s2->a; + p = s2->a2; + p = s2->b; + p = s2->b2; + s = s2->s; + s = s2->s2; + &(*(*s2).s2).b; + + // Subscript access + x = p[1]; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + p2 = q[0]; // ok + x = q[0][0]; // expected-warning{{dereferencing expression marked as 'noderef'}} + p2 = q2[0]; // expected-warning{{dereferencing q2; was declared with a 'noderef' type}} + p = q[*p]; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + x = p[*p]; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + + int NODEREF arr[10]; // expected-note 1 {{arr declared here}} + x = arr[1]; // expected-warning{{dereferencing arr; was declared with a 'noderef' type}} + + int NODEREF *(arr2[10]); + int NODEREF *elem = *arr2; + + int NODEREF(*arr3)[10]; + elem = *arr3; + + // Combinations between indirection, subscript, and member access + struct S2 NODEREF *s2_arr[10]; + s2 = s2_arr[1]; + struct S2 NODEREF *s2_arr2[10][10]; + s2 = s2_arr2[1][1]; + + p = s2_arr[1]->a; + p = s2_arr[1]->b; // expected-warning{{dereferencing expression marked as 'noderef'}} + int **bptr = &s2_arr[1]->b; + + x = s2->s2->a; // expected-warning{{dereferencing expression marked as 'noderef'}} + x = s2_noderef->a[1]; // expected-warning{{dereferencing s2_noderef; was declared with a 'noderef' type}} + p = &s2_noderef->a[1]; + + // 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 p; was declared with a 'noderef' type}} + + (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@-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 p; was declared with a 'noderef' type}} + + // Casting is ok + q = (int NODEREF **)&p; + q = (int NODEREF **)&p2; + q = &p; + q = &p2; + x = s2->s2->a; // expected-warning{{dereferencing expression marked as 'noderef'}} + + // Other expressions + func2(*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + func2(*p + 1); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + func2(!*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + func2((x = *p)); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + func2((char)(*p)); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + + // Other statements + if (*p) {} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + else if (*p) {} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + switch (*p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + for (*p; *p; *p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-2{{dereferencing p; was declared with a 'noderef' type}} + for (*p; *p;){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + for (*p;; *p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + for (; *p; *p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + // expected-warning@-1{{dereferencing p; was declared with a 'noderef' type}} + for (*p;;){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + for (;*p;){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + for (;;*p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + while (*p){} // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + do {} while (*p); // expected-warning{{dereferencing p; was declared with a 'noderef' type}} + return *p; // expected-warning{{dereferencing p; was declared with a 'noderef' type}} +} Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -182,11 +182,15 @@ 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; @@ -277,6 +281,10 @@ llvm_unreachable("no Attr* for AttributedType*"); } + void setParsedNoDeref(bool parsed) { parsedNoDeref = parsed; } + + bool didParseNoDeref() const { return parsedNoDeref; } + ~TypeProcessingState() { if (trivial) return; @@ -3879,6 +3887,11 @@ 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(); @@ -4272,6 +4285,9 @@ } } + 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 :). @@ -4874,8 +4890,22 @@ // 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 @@ -7220,6 +7250,9 @@ // 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. @@ -7317,6 +7350,14 @@ HandleLifetimeBoundAttr(state, type, attr); break; + case ParsedAttr::AT_NoDeref: + type = state.getAttributedType( + createSimpleAttr<NoDerefAttr>(state.getSema().Context, attr), type, + type); + attr.setUsedAsTypeAttr(); + state.setParsedNoDeref(true); + 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 auto *Ptr = dyn_cast<PointerType>( + E->getBase()->getType().getDesugaredType(Context))) { + if (TypeHasNoDeref(Ptr->getPointeeType())) + ExprEvalContexts.back().PossibleDerefs.insert(E); + } + } } ExprResult Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -4161,6 +4161,21 @@ return isa<MSPropertySubscriptExpr>(BaseNoParens); } +bool Sema::TypeHasNoDeref(QualType Ty) { + // Strip off everything but attributes. + QualType OldTy; + while (Ty != OldTy && !isa<AttributedType>(Ty)) { + OldTy = Ty; + Ty = Ty.getSingleStepDesugaredType(Context); + } + + if (const auto *AT = dyn_cast<AttributedType>(Ty)) { + AttributedType::Kind AttrKind = AT->getAttrKind(); + return AttrKind == attr::NoDeref; + } + return false; +} + ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc, Expr *idx, SourceLocation rbLoc) { @@ -4234,7 +4249,63 @@ return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, idx); } - return CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc); + ExprResult Res = CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc); + + if (!Res.isInvalid() && isa<ArraySubscriptExpr>(Res.get())) + CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(Res.get())); + + return Res; +} + +void Sema::CheckAddressOfNoDeref(const Expr *E) { + ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back(); + const Expr *StrippedExpr = E->IgnoreParenImpCasts(); + auto FoundExpr = LastRecord.PossibleDerefs.find(StrippedExpr); + if (FoundExpr != LastRecord.PossibleDerefs.end()) { + LastRecord.PossibleDerefs.erase(*FoundExpr); + } + + const Expr *Base = nullptr; + while (const auto *Member = dyn_cast<MemberExpr>(StrippedExpr)) { + Base = Member->getBase()->IgnoreParenImpCasts(); + StrippedExpr = Base; + } + + if (Base) { + // Getting the address of a member expr in the form &(*s).b + const QualType &BaseTy = Base->getType(); + auto FoundBase = LastRecord.PossibleDerefs.find(Base); + if (TypeHasNoDeref(BaseTy) && + FoundBase != LastRecord.PossibleDerefs.end()) { + LastRecord.PossibleDerefs.erase(*FoundBase); + } + } +} + +void Sema::CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E) { + const QualType &ResultTy = E->getType(); + ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back(); + + if (TypeHasNoDeref(ResultTy)) { + LastRecord.PossibleDerefs.insert(E); + return; + } + + // Check if the base type is a pointer to a member access of a struct + // marked with noderef. + const Expr *Base = E->getBase(); + const QualType &BaseTy = Base->getType(); + if (!(isa<ArrayType>(BaseTy) || isa<PointerType>(BaseTy))) + // Not a pointer access + return; + + if (const auto *Member = dyn_cast<MemberExpr>(Base->IgnoreParenCasts())) { + const QualType &MemberBaseTy = Member->getBase()->getType(); + if (const auto *Ptr = dyn_cast<PointerType>(MemberBaseTy)) { + if (TypeHasNoDeref(Ptr->getPointeeType())) + LastRecord.PossibleDerefs.insert(E); + } + } } ExprResult Sema::ActOnOMPArraySectionExpr(Expr *Base, SourceLocation LBLoc, @@ -12625,6 +12696,7 @@ break; case UO_AddrOf: resultType = CheckAddressOfOperand(Input, OpLoc); + CheckAddressOfNoDeref(InputExpr); RecordModifiableNonNullParam(*this, InputExpr); break; case UO_Deref: { @@ -12789,6 +12861,10 @@ auto *UO = new (Context) UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow); + + if (Opc == UO_Deref && TypeHasNoDeref(UO->getType())) + ExprEvalContexts.back().PossibleDerefs.insert(UO); + // Convert the result back to a half vector. if (ConvertHalfVec) return convertVector(UO, Context.HalfTy, *this); @@ -14148,6 +14224,65 @@ PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext); } +namespace { + +class DeclRefFinder : public ConstEvaluatedExprVisitor<DeclRefFinder> { +public: + typedef ConstEvaluatedExprVisitor<DeclRefFinder> Inherited; + + DeclRefFinder(Sema &S) : Inherited(S.Context), S(S) {} + + void VisitDeclRefExpr(const DeclRefExpr *E) { + if (DeclRef) + return; // Found already + + QualType Inner; + QualType Ty = E->getType().getDesugaredType(Context); + if (const auto *Ptr = dyn_cast<PointerType>(Ty)) + Inner = Ptr->getPointeeType(); + else if (const auto *Arr = dyn_cast<ArrayType>(Ty)) + Inner = Arr->getElementType(); + else + return; + + if (S.TypeHasNoDeref(Inner)) + DeclRef = E; + } + + const DeclRefExpr *GetDeclRef() const { return DeclRef; } + +private: + Sema &S; + const DeclRefExpr *DeclRef = nullptr; +}; + +} // namespace + +void Sema::StopCheckingNoDerefAndWarn() { + ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back(); + LastRecord.NoDerefCallCount--; + if (LastRecord.NoDerefCallCount == 0) + WarnOnPendingNoDerefs(LastRecord); +} + +void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) { + for (const Expr *E : Rec.PossibleDerefs) { + DeclRefFinder Finder(*this); + Finder.Visit(E); + const DeclRefExpr *DeclRef = Finder.GetDeclRef(); + if (DeclRef) { + const ValueDecl *Decl = DeclRef->getDecl(); + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type) + << Decl->getName() << E->getSourceRange(); + Diag(Decl->getLocation(), diag::note_previous_decl) << Decl->getName(); + } else { + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type_no_decl) + << E->getSourceRange(); + } + } + Rec.PossibleDerefs.clear(); +} + void Sema::PopExpressionEvaluationContext() { ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); unsigned NumTypos = Rec.NumTypos; @@ -14187,6 +14322,8 @@ } } + 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 Index: lib/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -1135,9 +1135,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; } @@ -1149,9 +1155,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; } @@ -1573,8 +1585,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(); @@ -1799,11 +1815,16 @@ LHS = ExprError(); } - if (!LHS.isInvalid()) + if (!LHS.isInvalid()) { + Actions.StartCheckingNoDeref(); + LHS = Actions.ActOnMemberAccessExpr(getCurScope(), LHS.get(), OpLoc, OpKind, SS, TemplateKWLoc, Name, CurParsedObjCImpl ? CurParsedObjCImpl->Dcl : nullptr); + + Actions.StopCheckingNoDerefAndWarn(); + } if (!LHS.isInvalid() && Tok.is(tok::less)) checkPotentialAngleBracket(LHS); break; Index: lib/AST/TypePrinter.cpp =================================================================== --- lib/AST/TypePrinter.cpp +++ lib/AST/TypePrinter.cpp @@ -1494,6 +1494,9 @@ case attr::PreserveAll: OS << "preserve_all"; break; + case attr::NoDeref: + OS << "noderef"; + break; } OS << "))"; } Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -983,6 +983,9 @@ /// expressions for which we have deferred checking the destructor. SmallVector<CXXBindTemporaryExpr *, 8> DelayedDecltypeBinds; + unsigned NoDerefCallCount; + llvm::SmallPtrSet<const Expr *, 8> PossibleDerefs; + /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { @@ -997,7 +1000,7 @@ : Context(Context), ParentCleanup(ParentCleanup), NumCleanupObjects(NumCleanupObjects), NumTypos(0), ManglingContextDecl(ManglingContextDecl), MangleNumbering(), - ExprContext(ExprContext) {} + NoDerefCallCount(0), ExprContext(ExprContext) {} /// Retrieve the mangling numbering context, used to consistently /// number constructs like lambdas for mangling. @@ -1016,6 +1019,13 @@ /// A stack of expression evaluation contexts. SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts; + /// Not all expressions need to be checked, so we only need to start keeping + /// track of noderef expressions on expressions that involve dereferencing + /// (indirection, member access, array subscript, ...). + void StartCheckingNoDeref() { ExprEvalContexts.back().NoDerefCallCount++; } + void StopCheckingNoDerefAndWarn(); + void WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec); + /// Compute the mangling number context for a lambda expression or /// block literal. /// @@ -1527,7 +1537,20 @@ } }; + bool TypeHasNoDeref(QualType Ty); + private: + /// Methods for marking which expressions involve dereferencing a pointer + /// marked with the 'noderef' attribute. Expressions are checked bottom up as + /// they are parsed, meaning that a noderef pointer may not be accessed. For + /// example, in `&*p` where `p` is a noderef pointer, we will first parse the + /// `*p`, but need to check that `address of` is called on it. This requires + /// keeping a container of all pending expressions and checking if the address + /// of them are eventually taken. + void CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E); + void CheckAddressOfNoDeref(const Expr *E); + void CheckMemberAccessOfNoDeref(const MemberExpr *E); + bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, TypeDiagnoser *Diagnoser); Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -9440,4 +9440,11 @@ "member '%2' is missing|" "the type is not trivially copyable|" "the type does not have the expected form}1">; + +def warn_dereference_of_noderef_type : Warning< + "dereferencing %0; was declared with a 'noderef' type">, InGroup<NoDeref>; +def warn_dereference_of_noderef_type_no_decl : Warning< + "dereferencing expression marked as 'noderef'">, InGroup<NoDeref>; +def warn_noderef_on_non_pointer_or_array : Warning< + "'noderef' can only be used on an array or pointer type">, InGroup<NoDeref>; } // end of sema component. Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -1030,3 +1030,5 @@ // A warning group specifically for warnings related to function // multiversioning. def FunctionMultiVersioning : DiagGroup<"function-multiversion">; + +def NoDeref : DiagGroup<"noderef">; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -3459,6 +3459,63 @@ }]; } +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 &y = x; // warning: 'noderef' can only be used on an array or pointer type + +.. code-block: objc + + id NODEREF obj = [NSObject new]; // warning: 'noderef' can only be used on an array or pointer type +}]; +} + def ReinitializesDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1794,6 +1794,11 @@ 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">];
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits