[clang] Fix noderef for array member of deref expr
authorLeonard Chan <leonardchan@google.com>
Mon, 7 Dec 2020 22:39:42 +0000 (14:39 -0800)
committerLeonard Chan <leonardchan@google.com>
Mon, 7 Dec 2020 22:39:42 +0000 (14:39 -0800)
    Committing on behalf of thejh (Jann Horn).

    Given an attribute((noderef)) pointer "p" to the struct

    struct s { int a[2]; };
    ensure that the following expressions are treated the same way by the
    noderef logic:

    p->a
    (*p).a
    Until now, the first expression would be treated correctly (nothing is
    added to PossibleDerefs because CheckMemberAccessOfNoDeref() bails out
    on array members), but the second expression would incorrectly warn
    because "*p" creates a PossibleDerefs entry.

    Handle this case the same way as for the AddrOf operator.

    Differential Revision: https://reviews.llvm.org/D92140

clang/lib/Sema/SemaExprMember.cpp
clang/test/Frontend/noderef.c

index 23cfae8..3e9d2a0 100644 (file)
@@ -1739,12 +1739,23 @@ void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) {
 
   QualType ResultTy = E->getType();
 
-  // Do not warn on member accesses to arrays since this returns an array
-  // lvalue and does not actually dereference memory.
-  if (isa<ArrayType>(ResultTy))
-    return;
-
-  if (E->isArrow()) {
+  // Member accesses have four cases:
+  // 1: non-array member via "->": dereferences
+  // 2: non-array member via ".": nothing interesting happens
+  // 3: array member access via "->": nothing interesting happens
+  //    (this returns an array lvalue and does not actually dereference memory)
+  // 4: array member access via ".": *adds* a layer of indirection
+  if (ResultTy->isArrayType()) {
+    if (!E->isArrow()) {
+      // This might be something like:
+      //     (*structPtr).arrayMember
+      // which behaves roughly like:
+      //     &(*structPtr).pointerMember
+      // in that the apparent dereference in the base expression does not
+      // actually happen.
+      CheckAddressOfNoDeref(E->getBase());
+    }
+  } else if (E->isArrow()) {
     if (const auto *Ptr = dyn_cast<PointerType>(
             E->getBase()->getType().getDesugaredType(Context))) {
       if (Ptr->getPointeeType()->hasAttr(attr::NoDeref))
index 3388f2a..b548ffa 100644 (file)
@@ -73,6 +73,7 @@ int test() {
   // 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).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}}