Hi rsmith, dberlin, aaron.ballman,
This patch introduces a warning for undefined behavior introduced by
assignments to a restrict-qualified pointer an expression based on other
restrict qualified pointers. Roughly speaking, this happens when the LHS is
designated by an identifier with a scope contained by the scope of the RHS
designator (you can't assign from an "inner" restrict pointer to an "outer" one
-- or between two at the same level).
For example, if we have:
struct S {
int * restrict x;
int * restrict y;
};
void f6(int * restrict q1, int * restrict q2, struct S s, struct S *t)
{
s.x = q1;
t->y = s.x;
}
you'll get these warnings (with some hopefully-useful notes):
warning: assignment to a restrict-qualified pointer designated by 's' using
an expression based on a restrict-qualified
pointer designated by 'q1' has undefined behavior
[-Wundefined-restrict-assignment]
s.x = q1;
^
note: the block associated with 'q1', declared here
void f6(int * restrict q1, int * restrict q2, struct S s, struct S *t)
^
note: does not begin before the block associated with 's', declared here
void f6(int * restrict q1, int * restrict q2, struct S s, struct S *t)
^
warning: assignment to a restrict-qualified pointer designated by 't' using
an expression based on a restrict-qualified
pointer designated by 's' has undefined behavior
[-Wundefined-restrict-assignment]
t->y = s.x;
^
note: the block associated with 's', declared here
void f6(int * restrict q1, int * restrict q2, struct S s, struct S *t)
^
note: does not begin before the block associated with 't', declared here
void f6(int * restrict q1, int * restrict q2, struct S s, struct S *t)
^
Currently, this only determines the "based on" relationship via a syntactic
analysis of the expression. In the future, I'd like to make this an
analysis-based warning so that some data-flow information can inform the "based
on" determination in case it flows through other non-restrict-qualified
pointers.
Thanks again!
http://reviews.llvm.org/D5889
Files:
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Sema/SemaExpr.cpp
test/Sema/restrict-assignment.c
test/SemaCXX/restrict-assignment.cpp
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -4053,6 +4053,17 @@
"restrict requires a pointer or reference">;
def err_typecheck_invalid_restrict_invalid_pointee : Error<
"pointer to function type %0 may not be 'restrict' qualified">;
+
+def warn_restrict_assignment_undefined : Warning<
+ "assignment to a restrict-qualified pointer designated by '%0' using an "
+ "expression based on a restrict-qualified pointer designated by '%1' has "
+ "undefined behavior">,
+ InGroup<DiagGroup<"undefined-restrict-assignment">>;
+def note_restrict_assignment_undefined_block_rhs : Note<
+ "the block associated with '%0', declared here">;
+def note_restrict_assignment_undefined_block_lhs : Note<
+ "does not begin before the block associated with '%0', declared here">;
+
def ext_typecheck_zero_array_size : Extension<
"zero size arrays are an extension">, InGroup<ZeroLengthArray>;
def err_typecheck_zero_array_size : Error<
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -8569,6 +8569,9 @@
/// template substitution or instantiation.
Scope *getCurScope() const { return CurScope; }
+ /// \brief Retrieve the parser's translation-unit scope.
+ Scope *getTUScope() const { return TUScope; }
+
void incrementMSLocalManglingNumber() const {
return CurScope->incrementMSLocalManglingNumber();
}
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -8611,6 +8611,92 @@
}
}
+namespace {
+ /// A visitor to extract the ordinary identifier (and scope) used to
+ /// designate a restrict-qualified pointer.
+ struct FindRestrictScope : StmtVisitor<FindRestrictScope> {
+ Sema &S;
+ SmallVector<std::pair<DeclRefExpr *, Scope *>, 4> Vars;
+
+ FindRestrictScope(Sema &S) : S(S) {}
+
+ void VisitDeclRefExpr(DeclRefExpr *E) {
+ if (E->getDecl()->hasExternalFormalLinkage())
+ Vars.push_back(std::make_pair(E, S.getTUScope()));
+ else
+ for (Scope *DS = S.getCurScope(); DS; DS = DS->getParent())
+ if (DS->isDeclScope(E->getDecl()))
+ Vars.push_back(std::make_pair(E, DS));
+ }
+
+ void VisitCXXThisExpr(CXXThisExpr *E) {
+ Scope *CS = nullptr;
+ for (Scope *SS = S.getCurScope(); SS; SS = SS->getParent())
+ if (SS->isClassScope()) {
+ CS = SS;
+ break;
+ }
+
+ assert(CS && "No class scope with a 'this' expression?");
+ Vars.push_back(std::make_pair((DeclRefExpr *) nullptr, CS));
+ }
+
+ void VisitChooseExpr(ChooseExpr *E) {
+ // The condition expression cannot contain the designating identifier.
+ Visit(E->getLHS());
+ Visit(E->getRHS());
+ }
+
+ void VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
+ // The array subscript expression cannot contain the designating
+ // identifier.
+ Visit(E->getLHS());
+ }
+
+ void VisitStmt(Stmt *S) {
+ for (Stmt::child_range C = S->children(); C; ++C)
+ if (*C)
+ Visit(*C);
+ }
+ };
+
+ struct FindRestrictBasedOnScope : StmtVisitor<FindRestrictBasedOnScope> {
+ Sema &S;
+ SmallVector<std::pair<DeclRefExpr *, Scope *>, 4> Vars;
+
+ FindRestrictBasedOnScope(Sema &S) : S(S) {}
+
+ void VisitDeclRefExpr(DeclRefExpr *E) {
+ if (E->getType().isRestrictQualified()) {
+ if (E->getDecl()->hasExternalFormalLinkage())
+ Vars.push_back(std::make_pair(E, S.getTUScope()));
+ else
+ for (Scope *DS = S.getCurScope(); DS; DS = DS->getParent())
+ if (DS->isDeclScope(E->getDecl()))
+ Vars.push_back(std::make_pair(E, DS));
+ }
+ }
+
+ void VisitMemberExpr(MemberExpr *E) {
+ if (E->getType().isRestrictQualified()) {
+ // This is a restrict-qualified member; to understand the scope, we
+ // need to find the designating identifier.
+ FindRestrictScope RS(S);
+ RS.Visit(E->getBase());
+ Vars.append(RS.Vars.begin(), RS.Vars.end());
+ } else {
+ return VisitStmt(E);
+ }
+ }
+
+ void VisitStmt(Stmt *S) {
+ for (Stmt::child_range C = S->children(); C; ++C)
+ if (*C)
+ Visit(*C);
+ }
+ };
+}
+
// C99 6.5.16.1
QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
SourceLocation Loc,
@@ -8704,6 +8790,91 @@
CheckForNullPointerDereference(*this, LHSExpr);
+ // C99 6.7.3.1p4: If P is assigned the value of a pointer expression E that
+ // is based on another restricted pointer object P2, associated with block
+ // B2, then either the execution of B2 shall begin before the execution of B,
+ // or the execution of B2 shall end prior to the assignment. If these
+ // requirements are not met, then the behavior is undefined.
+ //
+ // where the block associated with a restrict-qualified pointer is defined by:
+ //
+ // C99 6.7.3.1p1: Let D be a declaration of an ordinary identifier that
+ // provides a means of designating an object P as a restrict-qualified pointer
+ // to type T.
+ // C99 6.7.3.1p2: If D appears inside a block and does not have storage
+ // class extern, let B denote the block. If D appears in the list of
+ // parameter declarations of a function definition, let B denote the
+ // associated block. Otherwise, let B denote the block of main (or the block
+ // of whatever function is called at program startup in a freestanding
+ // environment).
+ if (CompoundType.isNull() && LHSType.isRestrictQualified()) {
+ FindRestrictScope LHSRS(*this);
+ FindRestrictBasedOnScope RHSRS(*this);
+
+ LHSRS.Visit(LHSExpr);
+ RHSRS.Visit(RHS.get());
+
+ for (auto &L : LHSRS.Vars) {
+ for (auto &R : RHSRS.Vars) {
+ // The the LHS scope is a parent of (or equal to) the RHS scope, then
+ // produce a warning.
+ //
+ // This check specifically includes, as undefined, the case where the
+ // LHS is in class scope and the RHS in in function scope because class
+ // scope objects are designated by the implicit 'this' function
+ // parameter, which is taken to have function scope, making an
+ // assignment between a class-scope restrict-qualified
+ // pointer based on a function scope restrict-qualified pointer
+ // undefined.
+
+ // class scope is equivalent to function scope (because member
+ // variables are designated by 'this', which is an implicit function
+ // parameter).
+ Scope *SS = R.second;
+ if (SS->isClassScope() && CurScope->getFnParent())
+ SS = CurScope->getFnParent();
+
+ for (Scope *S = SS; S; S = S->getParent())
+ if (L.second == S) {
+ if (L.second->isClassScope() && R.second->isClassScope()) {
+ Diag(Loc, diag::warn_restrict_assignment_undefined)
+ << "this" << "this";
+ } else if (L.second->isClassScope()) {
+ Diag(Loc, diag::warn_restrict_assignment_undefined)
+ << "this" << R.first;
+ Diag(R.first->getDecl()->getLocation(),
+ diag::note_restrict_assignment_undefined_block_rhs)
+ << R.first;
+ Diag(getCurFunctionDecl()->getLocation(),
+ diag::note_restrict_assignment_undefined_block_lhs)
+ << "this";
+ } else if (R.second->isClassScope()) {
+ Diag(Loc, diag::warn_restrict_assignment_undefined)
+ << L.first << "this";
+ Diag(getCurFunctionDecl()->getLocation(),
+ diag::note_restrict_assignment_undefined_block_rhs)
+ << "this";
+ Diag(L.first->getDecl()->getLocation(),
+ diag::note_restrict_assignment_undefined_block_lhs)
+ << L.first;
+ } else {
+ Diag(Loc, diag::warn_restrict_assignment_undefined)
+ << L.first << R.first;
+ if (R.first->getDecl() != L.first->getDecl()) {
+ Diag(R.first->getDecl()->getLocation(),
+ diag::note_restrict_assignment_undefined_block_rhs)
+ << R.first;
+ Diag(L.first->getDecl()->getLocation(),
+ diag::note_restrict_assignment_undefined_block_lhs)
+ << L.first;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
// C99 6.5.16p3: The type of an assignment expression is the type of the
// left operand unless the left operand has qualified type, in which case
// it is the unqualified version of the type of the left operand.
Index: test/Sema/restrict-assignment.c
===================================================================
--- /dev/null
+++ test/Sema/restrict-assignment.c
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -fsyntax-only -Wignored-qualifiers -verify
+
+// expected-note@+6 {{does not begin before the block associated with 'p1', declared here}}
+// expected-note@+5 {{does not begin before the block associated with 'p1', declared here}}
+// expected-note@+4 {{the block associated with 'p2', declared here}}
+// expected-note@+3 {{does not begin before the block associated with 'p1', declared here}}
+// expected-note@+2 {{the block associated with 'p2', declared here}}
+// expected-note@+1 {{does not begin before the block associated with 'p1', declared here}}
+int * restrict p1, * restrict p2;
+
+struct S {
+ int * restrict x;
+ int * restrict y;
+};
+
+// expected-note@+9 {{the block associated with 'q1', declared here}}
+// expected-note@+8 {{the block associated with 'q2', declared here}}
+// expected-note@+7 {{does not begin before the block associated with 'q1', declared here}}
+// expected-note@+6 {{the block associated with 'q1', declared here}}
+// expected-note@+5 {{does not begin before the block associated with 's', declared here}}
+// expected-note@+4 {{the block associated with 's', declared here}}
+// expected-note@+3 {{does not begin before the block associated with 't', declared here}}
+// expected-note@+2 {{does not begin before the block associated with 'q1', declared here}}
+// expected-note@+1 {{the block associated with 'q2', declared here}}
+void foo(int * restrict q1, int * restrict q2, struct S s, struct S *t)
+{
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'p1' using an expression based on a restrict-qualified pointer designated by 'p2' has undefined behavior}}
+ p1 = p2;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'p1' using an expression based on a restrict-qualified pointer designated by 'p2' has undefined behavior}}
+ p1 = p2+1;
+
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'p1' using an expression based on a restrict-qualified pointer designated by 'q1' has undefined behavior}}
+ p1 = q1;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'q1' using an expression based on a restrict-qualified pointer designated by 'q2' has undefined behavior}}
+ q1 = q2;
+
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 's' using an expression based on a restrict-qualified pointer designated by 'q1' has undefined behavior}}
+ s.x = q1;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 't' using an expression based on a restrict-qualified pointer designated by 's' has undefined behavior}}
+ t->y = s.x;
+ {
+ // expected-note@+4 {{the block associated with 'r2', declared here}}
+ // expected-note@+3 {{does not begin before the block associated with 'r1', declared here}}
+ // expected-note@+2 {{the block associated with 'r1', declared here}}
+ // expected-note@+1 {{the block associated with 'r1', declared here}}
+ int * restrict r1, * restrict r2;
+ // expected-note@+1 {{does not begin before the block associated with 'er1', declared here}}
+ extern int * restrict er1;
+
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'r1' using an expression based on a restrict-qualified pointer designated by 'r2' has undefined behavior}}
+ r1 = r2;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'q1' using an expression based on a restrict-qualified pointer designated by 'r1' has undefined behavior}}
+ q1 = r1;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'p1' using an expression based on a restrict-qualified pointer designated by 'r1' has undefined behavior}}
+ p1 = r1;
+
+ r1 = p2; // ok
+ r1 = q2; // ok
+ r2 = s.y; // ok
+
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'er1' using an expression based on a restrict-qualified pointer designated by 'q2' has undefined behavior}}
+ er1 = q2;
+ }
+}
+
Index: test/SemaCXX/restrict-assignment.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/restrict-assignment.cpp
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 %s -std=c++11 -fsyntax-only -Wignored-qualifiers -verify
+
+class C {
+ // expected-note@+9 {{the block associated with 'x', declared here}}
+ // expected-note@+8 {{does not begin before the block associated with 'this', declared here}}
+ // expected-note@+7 {{the block associated with 'x', declared here}}
+ // expected-note@+6 {{the block associated with 'this', declared here}}
+ // expected-note@+5 {{does not begin before the block associated with 'this', declared here}}
+ // expected-note@+4 {{the block associated with 'x', declared here}}
+ // expected-note@+3 {{does not begin before the block associated with 'this', declared here}}
+ // expected-note@+2 {{the block associated with 'this', declared here}}
+ // expected-note@+1 {{does not begin before the block associated with 'x', declared here}}
+ void foo(float *__restrict__ x) {
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'this' using an expression based on a restrict-qualified pointer designated by 'x' has undefined behavior}}
+ a = x;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'this' using an expression based on a restrict-qualified pointer designated by 'this' has undefined behavior}}
+ a = b;
+
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'sa' using an expression based on a restrict-qualified pointer designated by 'x' has undefined behavior}}
+ sa = x;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'sa' using an expression based on a restrict-qualified pointer designated by 'this' has undefined behavior}}
+ sa = a;
+
+ {
+ // expected-note@+2 {{the block associated with 'q', declared here}}
+ // expected-note@+1 {{the block associated with 'q', declared here}}
+ float * __restrict__ q;
+
+ q = sa; // ok
+ q = a; // ok
+
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'sa' using an expression based on a restrict-qualified pointer designated by 'q' has undefined behavior}}
+ sa = q;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'this' using an expression based on a restrict-qualified pointer designated by 'q' has undefined behavior}}
+ b = q;
+ }
+
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'this' using an expression based on a restrict-qualified pointer designated by 'this' has undefined behavior}}
+ s.a = a;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'this' using an expression based on a restrict-qualified pointer designated by 'this' has undefined behavior}}
+ a = s.a;
+
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'this' using an expression based on a restrict-qualified pointer designated by 'x' has undefined behavior}}
+ s.a = x;
+ // expected-warning@+1 {{assignment to a restrict-qualified pointer designated by 'x' using an expression based on a restrict-qualified pointer designated by 'this' has undefined behavior}}
+ x = s.a;
+ }
+
+ float *__restrict__ a;
+ float *__restrict__ b;
+
+ // expected-note@+3 {{does not begin before the block associated with 'sa', declared here}}
+ // expected-note@+2 {{does not begin before the block associated with 'sa', declared here}}
+ // expected-note@+1 {{does not begin before the block associated with 'sa', declared here}}
+ static float * __restrict__ sa;
+
+ struct {
+ float *__restrict__ a;
+ } s;
+};
+
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits