https://github.com/PiJoules created 
https://github.com/llvm/llvm-project/pull/190100

This refactors the `noderef` attribute implementation to correctly associate 
the attribute with pointer and array types rather than the pointee type, 
fulfilling the requirements for the bracket attribute syntax 
`[[clang::noderef]]`.

Changes include:
- Remove the `parsedNoDeref` logic which would apply noderef to the right 
(pointee) type rather than the left (pointer) type.
- Use `getAs<Type>` instead of `dyn_cast<Type>` where appropriate to strip type 
sugar.
- Fixes for some of the tests where we incorrectly associate a noderef with a 
pointee type rather than a pointer.
- Add regression tests

Note the bulk of this PR was made by gemini, but it has been thoroughly 
reviewed and edited by me to the best of my ability.

Fixes: #55790

>From 6867a43c47a97506b1f45708e7367fa2661b463a Mon Sep 17 00:00:00 2001
From: Leonard Chan <[email protected]>
Date: Wed, 1 Apr 2026 17:31:23 +0000
Subject: [PATCH] [clang] Support [[]] spelling for noderef

This refactors the `noderef` attribute implementation to
correctly associate the attribute with pointer and array types rather
than the pointee type, fulfilling the requirements for the bracket
attribute syntax `[[clang::noderef]]`.

Changes include:
- Remove the `parsedNoDeref` logic which would apply noderef to the
  right (pointee) type rather than the left (pointer) type.
- Use `getAs<Type>` instead of `dyn_cast<Type>` where appropriate to
  strip type sugar.
- Fixes for some of the tests where we incorrectly associate a noderef
  with a pointee type rather than a pointer.
- Add regression tests

Fixes: #55790
---
 clang/include/clang/Sema/Sema.h             |   6 +-
 clang/lib/Sema/SemaCast.cpp                 |  14 +--
 clang/lib/Sema/SemaDeclAttr.cpp             |   7 --
 clang/lib/Sema/SemaExpr.cpp                 |  57 ++++++-----
 clang/lib/Sema/SemaExprMember.cpp           |   7 +-
 clang/lib/Sema/SemaInit.cpp                 |   9 +-
 clang/lib/Sema/SemaType.cpp                 | 103 ++++++++++++++------
 clang/test/Frontend/noderef.c               |  61 +++++++-----
 clang/test/Frontend/noderef.cpp             |  31 +++++-
 clang/test/Sema/address_space_print_macro.c |   6 +-
 10 files changed, 188 insertions(+), 113 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1b8a9803be472..9e17c5ba254eb 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8417,7 +8417,11 @@ class Sema final : public SemaBase {
   /// 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);
+
+  /// Check if the address of a `noderef` expression is taken. Return true if
+  /// the expression was marked with `noderef` and the attribute should be
+  /// propagated to the resulting pointer type.
+  bool CheckAddressOfNoDeref(const Expr *E);
 
   ///@}
 
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 0040f3aa1a891..d3f1259a3f15a 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -225,15 +225,11 @@ namespace {
 
   void CheckNoDeref(Sema &S, const QualType FromType, const QualType ToType,
                     SourceLocation OpLoc) {
-    if (const auto *PtrType = dyn_cast<PointerType>(FromType)) {
-      if (PtrType->getPointeeType()->hasAttr(attr::NoDeref)) {
-        if (const auto *DestType = dyn_cast<PointerType>(ToType)) {
-          if (!DestType->getPointeeType()->hasAttr(attr::NoDeref)) {
-            S.Diag(OpLoc, diag::warn_noderef_to_dereferenceable_pointer);
-          }
-        }
-      }
-    }
+    // Note `getAs` strips the sugar to ensure it's a real pointer, but we 
still
+    // use the original type since the attribute is part of the sugar.
+    if (FromType->getAs<PointerType>() && FromType->hasAttr(attr::NoDeref) &&
+        ToType->getAs<PointerType>() && !ToType->hasAttr(attr::NoDeref))
+      S.Diag(OpLoc, diag::warn_noderef_to_dereferenceable_pointer);
   }
 
   struct CheckNoDerefRAII {
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 63a70e4b77bc4..30111b912dd36 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7445,13 +7445,6 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 
const ParsedAttr &AL,
         // processTypeAttr().
         break;
       }
-
-      if (AL.getKind() == ParsedAttr::AT_NoDeref) {
-        // FIXME: `noderef` currently doesn't work correctly in [[]] syntax.
-        // See https://github.com/llvm/llvm-project/issues/55790 for details.
-        // We allow processTypeAttrs() to emit a warning and silently move on.
-        break;
-      }
     }
     // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
     // statement attribute is not written on a declaration, but this code is
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c9642ed298bf3..2758103fe9af5 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -549,8 +549,14 @@ ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, 
bool Diagnose) {
     // T" can be converted to an rvalue of type "pointer to T".
     //
     if (getLangOpts().C99 || getLangOpts().CPlusPlus || E->isLValue()) {
-      ExprResult Res = ImpCastExprToType(E, Context.getArrayDecayedType(Ty),
-                                         CK_ArrayToPointerDecay);
+      QualType PtrTy = Context.getArrayDecayedType(Ty);
+
+      // When the array is `noderef` and we promote to a pointer, also apply
+      // `noderef` on the pointer.
+      if (Ty->hasAttr(attr::NoDeref))
+        PtrTy = Context.getAttributedType(attr::NoDeref, PtrTy, PtrTy);
+
+      ExprResult Res = ImpCastExprToType(E, PtrTy, CK_ArrayToPointerDecay);
       if (Res.isInvalid())
         return ExprError();
       E = Res.get();
@@ -5327,7 +5333,7 @@ ExprResult Sema::CreateBuiltinMatrixSubscriptExpr(Expr 
*Base, Expr *RowIdx,
                                            MTy->getElementType(), RBLoc);
 }
 
-void Sema::CheckAddressOfNoDeref(const Expr *E) {
+bool Sema::CheckAddressOfNoDeref(const Expr *E) {
   ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back();
   const Expr *StrippedExpr = E->IgnoreParenImpCasts();
 
@@ -5337,7 +5343,7 @@ void Sema::CheckAddressOfNoDeref(const Expr *E) {
   while ((Member = dyn_cast<MemberExpr>(StrippedExpr)) && !Member->isArrow())
     StrippedExpr = Member->getBase()->IgnoreParenImpCasts();
 
-  LastRecord.PossibleDerefs.erase(StrippedExpr);
+  return LastRecord.PossibleDerefs.erase(StrippedExpr);
 }
 
 void Sema::CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E) {
@@ -5351,15 +5357,16 @@ void Sema::CheckSubscriptAccessOfNoDeref(const 
ArraySubscriptExpr *E) {
   if (isa<ArrayType>(ResultTy))
     return;
 
-  if (ResultTy->hasAttr(attr::NoDeref)) {
+  const Expr *Base = E->getBase();
+  QualType BaseTy = Base->getType();
+
+  if (BaseTy->hasAttr(attr::NoDeref)) {
     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();
-  QualType BaseTy = Base->getType();
   if (!(isa<ArrayType>(BaseTy) || isa<PointerType>(BaseTy)))
     // Not a pointer access
     return;
@@ -5369,8 +5376,8 @@ void Sema::CheckSubscriptAccessOfNoDeref(const 
ArraySubscriptExpr *E) {
          Member->isArrow())
     Base = Member->getBase();
 
-  if (const auto *Ptr = dyn_cast<PointerType>(Base->getType())) {
-    if (Ptr->getPointeeType()->hasAttr(attr::NoDeref))
+  if (Base->getType()->getAs<PointerType>()) {
+    if (Base->getType()->hasAttr(attr::NoDeref))
       LastRecord.PossibleDerefs.insert(E);
   }
 }
@@ -10127,10 +10134,10 @@ AssignConvertType 
Sema::CheckSingleAssignmentConstraints(QualType LHSType,
   ExprResult LocalRHS = CallerRHS;
   ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS;
 
-  if (const auto *LHSPtrType = LHSType->getAs<PointerType>()) {
-    if (const auto *RHSPtrType = RHS.get()->getType()->getAs<PointerType>()) {
-      if (RHSPtrType->getPointeeType()->hasAttr(attr::NoDeref) &&
-          !LHSPtrType->getPointeeType()->hasAttr(attr::NoDeref)) {
+  if (LHSType->getAs<PointerType>()) {
+    if (RHS.get()->getType()->getAs<PointerType>()) {
+      if (RHS.get()->getType()->hasAttr(attr::NoDeref) &&
+          !LHSType->hasAttr(attr::NoDeref)) {
         Diag(RHS.get()->getExprLoc(),
              diag::warn_noderef_to_dereferenceable_pointer)
             << RHS.get()->getSourceRange();
@@ -16203,7 +16210,13 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation 
OpLoc,
       break;
     case UO_AddrOf:
       resultType = CheckAddressOfOperand(Input, OpLoc);
-      CheckAddressOfNoDeref(InputExpr);
+
+      // Since the expression is noderef, the resulting type should also be
+      // noderef.
+      if (CheckAddressOfNoDeref(InputExpr))
+        resultType =
+            Context.getAttributedType(attr::NoDeref, resultType, resultType);
+
       RecordModifiableNonNullParam(*this, InputExpr);
       break;
     case UO_Deref: {
@@ -16404,7 +16417,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation 
OpLoc,
       UnaryOperator::Create(Context, Input.get(), Opc, resultType, VK, OK,
                             OpLoc, CanOverflow, CurFPFeatureOverrides());
 
-  if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) &&
+  if (Opc == UO_Deref && Input.get()->getType()->hasAttr(attr::NoDeref) &&
       !isa<ArrayType>(UO->getType().getDesugaredType(Context)) &&
       !isUnevaluatedContext())
     ExprEvalContexts.back().PossibleDerefs.insert(UO);
@@ -18199,17 +18212,11 @@ const DeclRefExpr *CheckPossibleDeref(Sema &S, const 
Expr *PossibleDeref) {
   } else if (const auto *E = dyn_cast<MemberExpr>(PossibleDeref)) {
     return CheckPossibleDeref(S, E->getBase());
   } else if (const auto E = dyn_cast<DeclRefExpr>(PossibleDeref)) {
-    QualType Inner;
     QualType Ty = E->getType();
-    if (const auto *Ptr = Ty->getAs<PointerType>())
-      Inner = Ptr->getPointeeType();
-    else if (const auto *Arr = S.Context.getAsArrayType(Ty))
-      Inner = Arr->getElementType();
-    else
-      return nullptr;
-
-    if (Inner->hasAttr(attr::NoDeref))
-      return E;
+    if (Ty->getAs<PointerType>() || S.Context.getAsArrayType(Ty)) {
+      if (Ty->hasAttr(attr::NoDeref))
+        return E;
+    }
   }
   return nullptr;
 }
diff --git a/clang/lib/Sema/SemaExprMember.cpp 
b/clang/lib/Sema/SemaExprMember.cpp
index ff7c844f66123..f06aaad0663fb 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1766,9 +1766,8 @@ void Sema::CheckMemberAccessOfNoDeref(const MemberExpr 
*E) {
       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))
+    if (E->getBase()->getType()->getAs<PointerType>()) {
+      if (E->getBase()->getType()->hasAttr(attr::NoDeref))
         ExprEvalContexts.back().PossibleDerefs.insert(E);
     }
   }
@@ -1831,7 +1830,7 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool 
IsArrow,
     // result. E.g. the expression
     //     &someNoDerefPtr->pointerMember
     // should be a noderef pointer again.
-    if (BaseType->hasAttr(attr::NoDeref))
+    if (BaseExpr->getType()->hasAttr(attr::NoDeref))
       MemberType =
           Context.getAttributedType(attr::NoDeref, MemberType, MemberType);
   }
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 6c929de9418ed..acbc547060fef 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8332,11 +8332,10 @@ ExprResult InitializationSequence::Perform(Sema &S,
 
     case SK_ConversionSequence:
     case SK_ConversionSequenceNoNarrowing: {
-      if (const auto *FromPtrType =
-              CurInit.get()->getType()->getAs<PointerType>()) {
-        if (const auto *ToPtrType = Step->Type->getAs<PointerType>()) {
-          if (FromPtrType->getPointeeType()->hasAttr(attr::NoDeref) &&
-              !ToPtrType->getPointeeType()->hasAttr(attr::NoDeref)) {
+      if (CurInit.get()->getType()->getAs<PointerType>()) {
+        if (Step->Type->getAs<PointerType>()) {
+          if (CurInit.get()->getType()->hasAttr(attr::NoDeref) &&
+              !Step->Type->hasAttr(attr::NoDeref)) {
             // Do not check static casts here because they are checked earlier
             // in Sema::ActOnCXXNamedCast()
             if (!Kind.isStaticCast()) {
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 846474fe94adf..799fc100eb27d 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -47,6 +47,7 @@
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/Support/ErrorHandling.h"
+#include <algorithm>
 #include <bitset>
 #include <optional>
 
@@ -217,10 +218,6 @@ namespace {
     /// stored in a MacroQualifiedTypeLoc.
     llvm::DenseMap<const MacroQualifiedType *, SourceLocation> LocsForMacros;
 
-    /// Flag to indicate we parsed a noderef attribute. This is used for
-    /// validating that noderef was used on a pointer or array.
-    bool parsedNoDeref;
-
     // Flag to indicate that we already parsed a HLSL parameter modifier
     // attribute. This prevents double-mutating the type.
     bool ParsedHLSLParamMod;
@@ -228,7 +225,7 @@ namespace {
   public:
     TypeProcessingState(Sema &sema, Declarator &declarator)
         : sema(sema), declarator(declarator),
-          chunkIndex(declarator.getNumTypeObjects()), parsedNoDeref(false),
+          chunkIndex(declarator.getNumTypeObjects()),
           ParsedHLSLParamMod(false) {}
 
     Sema &getSema() const {
@@ -360,10 +357,6 @@ namespace {
       LocsForMacros[MQT] = Loc;
     }
 
-    void setParsedNoDeref(bool parsed) { parsedNoDeref = parsed; }
-
-    bool didParseNoDeref() const { return parsedNoDeref; }
-
     void setParsedHLSLParamMod(bool Parsed) { ParsedHLSLParamMod = Parsed; }
 
     bool didParseHLSLParamMod() const { return ParsedHLSLParamMod; }
@@ -4688,8 +4681,20 @@ static TypeSourceInfo 
*GetFullTypeForDeclarator(TypeProcessingState &state,
     }
   }
 
+  // GNU attributes on a declaration (e.g., __attribute__((noderef)) int *p;)
+  // are parsed as part of the DeclSpec, but they are intended to apply to
+  // the first pointer or array declarator chunk encountered. Standard
+  // C++11 attribute syntax `[[clang::noderef]]` has stricter placement
+  // rules and should not "float" to the pointer. If a standard noderef
+  // attribute is found on the DeclSpec, we skip setting `ExpectNoDerefChunk`
+  // to ensure it is diagnosed for being applied to a non-pointer type,
+  // rather than silently moving it to the pointer.
   bool ExpectNoDerefChunk =
-      state.getCurrentAttributes().hasAttribute(ParsedAttr::AT_NoDeref);
+      std::any_of(state.getCurrentAttributes().begin(),
+                  state.getCurrentAttributes().end(), [](const ParsedAttr &AL) 
{
+                    return AL.getKind() == ParsedAttr::AT_NoDeref &&
+                           !AL.isStandardAttributeSyntax();
+                  });
 
   // Walk the DeclTypeInfo, building the recursive type as we go.
   // DeclTypeInfos are ordered from the identifier out, which is
@@ -5471,17 +5476,58 @@ static TypeSourceInfo 
*GetFullTypeForDeclarator(TypeProcessingState &state,
     processTypeAttrs(state, T, TAL_DeclChunk, DeclType.getAttrs(),
                      S.CUDA().IdentifyTarget(D.getAttributes()));
 
-    if (DeclType.Kind != DeclaratorChunk::Paren) {
-      if (ExpectNoDerefChunk && !IsNoDerefableChunk(DeclType))
+    if (DeclType.Kind != DeclaratorChunk::Paren && ExpectNoDerefChunk) {
+      // This block handles explicit indirection in the declaration
+      //
+      //   __attribute__((noderef)) int *p;
+      //
+      // If we are looking for a GNU-style noderef attribute that was declared
+      // on the DeclSpec, we try to apply it to the first pointer or array
+      // declarator chunk encountered. Once we apply it (or diagnose a 
warning),
+      // we set ExpectNoDerefChunk to false so that it only applies to the
+      // outermost pointer or array chunk and doesn't "bleed" into deeper
+      // indirection levels as this loop continues.
+      if (!IsNoDerefableChunk(DeclType)) {
         S.Diag(DeclType.Loc, diag::warn_noderef_on_non_pointer_or_array);
-
-      ExpectNoDerefChunk = state.didParseNoDeref();
+      } else {
+        for (ParsedAttr &AL :
+             state.getDeclarator().getMutableDeclSpec().getAttributes()) {
+          if (AL.getKind() == ParsedAttr::AT_NoDeref) {
+            T = state.getAttributedType(
+                createSimpleAttr<NoDerefAttr>(S.Context, AL), T, T);
+            break;
+          }
+        }
+      }
+      ExpectNoDerefChunk = false;
     }
   }
 
-  if (ExpectNoDerefChunk)
-    S.Diag(state.getDeclarator().getBeginLoc(),
-           diag::warn_noderef_on_non_pointer_or_array);
+  // This block handles implicit indirection, such as through a typedef or when
+  // the base type is already a pointer
+  //
+  //   typedef int *IntPtr;
+  //   __attribute__((noderef)) IntPtr p;
+  //
+  // If the loop above finished without finding an explicit pointer or array
+  // chunk to attach the attribute to, we check the final resolved type and
+  // apply it if it's a pointer or array.
+  if (ExpectNoDerefChunk) {
+    if (T->isPointerType() || T->isArrayType()) {
+      for (ParsedAttr &AL :
+           state.getDeclarator().getMutableDeclSpec().getAttributes()) {
+        if (AL.getKind() == ParsedAttr::AT_NoDeref) {
+          ASTContext &Ctx = S.Context;
+          T = state.getAttributedType(createSimpleAttr<NoDerefAttr>(Ctx, AL), 
T,
+                                      T);
+          break;
+        }
+      }
+    } else {
+      S.Diag(state.getDeclarator().getBeginLoc(),
+             diag::warn_noderef_on_non_pointer_or_array);
+    }
+  }
 
   // GNU warning -Wstrict-prototypes
   //   Warn if a function declaration or definition is without a prototype.
@@ -9008,7 +9054,6 @@ static void processTypeAttrs(TypeProcessingState &state, 
QualType &type,
                              const ParsedAttributesView &attrs,
                              CUDAFunctionTarget CFT) {
 
-  state.setParsedNoDeref(false);
   if (attrs.empty())
     return;
 
@@ -9175,20 +9220,16 @@ static void processTypeAttrs(TypeProcessingState 
&state, QualType &type,
       break;
 
     case ParsedAttr::AT_NoDeref: {
-      // FIXME: `noderef` currently doesn't work correctly in [[]] syntax.
-      // See https://github.com/llvm/llvm-project/issues/55790 for details.
-      // For the time being, we simply emit a warning that the attribute is
-      // ignored.
-      if (attr.isStandardAttributeSyntax()) {
-        state.getSema().Diag(attr.getLoc(), diag::warn_attribute_ignored)
-            << attr;
-        break;
-      }
-      ASTContext &Ctx = state.getSema().Context;
-      type = state.getAttributedType(createSimpleAttr<NoDerefAttr>(Ctx, attr),
-                                     type, type);
       attr.setUsedAsTypeAttr();
-      state.setParsedNoDeref(true);
+      if (!state.isProcessingDeclSpec() || attr.isStandardAttributeSyntax()) {
+        if (!type->isPointerType() && !type->isArrayType()) {
+          state.getSema().Diag(attr.getLoc(),
+                               diag::warn_noderef_on_non_pointer_or_array);
+        }
+        ASTContext &Ctx = state.getSema().Context;
+        type = state.getAttributedType(createSimpleAttr<NoDerefAttr>(Ctx, 
attr),
+                                       type, type);
+      }
       break;
     }
 
diff --git a/clang/test/Frontend/noderef.c b/clang/test/Frontend/noderef.c
index 3f0d8269ca88d..dbbc39e66d75b 100644
--- a/clang/test/Frontend/noderef.c
+++ b/clang/test/Frontend/noderef.c
@@ -31,11 +31,11 @@ int test(void) {
   x = *((int NODEREF *)p2); // expected-warning{{dereferencing expression 
marked as 'noderef'}}
 
   int NODEREF **q;
-  int *NODEREF *q2; // expected-note 4 {{q2 declared here}}
+  int *NODEREF *q2;
 
   // Indirection
   x = **q;  // expected-warning{{dereferencing expression marked as 'noderef'}}
-  p2 = *q2; // expected-warning{{dereferencing q2; was declared with a 
'noderef' type}}
+  p2 = *q2; // expected-warning{{casting to dereferenceable pointer removes 
'noderef' attribute}}
 
   **q; // expected-warning{{dereferencing expression marked as 'noderef'}}
 
@@ -51,7 +51,7 @@ int test(void) {
   *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}}
+  *q2 = p2; // ok - casting from normal int pointer to a NODEREF int pointer
 
   p = p + 1;
   p = &*(p + 1);
@@ -99,7 +99,7 @@ int test(void) {
   // Subscript access
   x = p[1];    // expected-warning{{dereferencing p; was declared with a 
'noderef' type}}
   x = q[0][0]; // expected-warning{{dereferencing expression marked as 
'noderef'}}
-  p2 = q2[0];  // expected-warning{{dereferencing q2; was declared with a 
'noderef' type}}
+  p2 = q2[0];  // expected-warning{{casting to dereferenceable pointer removes 
'noderef' attribute}}
   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}}
@@ -119,7 +119,11 @@ int test(void) {
 
   p = s2_arr[1]->a;
   p = s2_arr[1]->b; // expected-warning{{dereferencing expression marked as 
'noderef'}}
-  int *NODEREF *bptr = &s2_arr[1]->b;
+
+  // `&s2_arr[1]->b` is a NODEREF pointer to a normal int pointer.
+  // `bptr` is a normal pointer to a NODEREF int pointer.
+  // Assigning the former to the latter strips NODEREF from the outer pointer.
+  int *NODEREF *bptr = &s2_arr[1]->b; // expected-warning{{casting to 
dereferenceable pointer removes 'noderef' attribute}}
 
   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}}
@@ -155,18 +159,25 @@ int test(void) {
   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; // ok - dereferencing the outer normal pointer
   **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}}
+  noderef_int2_nested_t noderef_int2_nested_ptr;
+  // `noderef_int2_nested_ptr` is a normal pointer to a NODEREF pointer.
+  // Dereferencing it yields a NODEREF pointer, which is safe.
+  // Dereferencing the result should warn.
+  *noderef_int2_nested_ptr;
+  **noderef_int2_nested_ptr; // expected-warning{{dereferencing expression 
marked as 'noderef'}}
 
   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}}
+  noderef_int3_nested_t noderef_int3_nested_ptr;
+  // `noderef_int3_nested_ptr` is also a normal pointer to a NODEREF pointer
+  // because NODEREF is inside the parentheses but before the '*'.
+  *noderef_int3_nested_ptr;
+  **noderef_int3_nested_ptr; // expected-warning{{dereferencing expression 
marked as 'noderef'}}
 
   // Parentheses
   (((*((p))))); // expected-warning{{dereferencing p; was declared with a 
'noderef' type}}
@@ -175,7 +186,7 @@ int test(void) {
   (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}}
+  (q2[0]);     // ok - q2 is a pointer to a NODEREF pointer, so the first 
deref is safe.
   (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}}
@@ -222,23 +233,25 @@ int test(void) {
   return *p;          // expected-warning{{dereferencing p; was declared with 
a 'noderef' type}}
 }
 
-// FIXME: Currently, [[]] syntax does not work for the `noderef` atribute.
-// For the time being, test that we consistently diagnose the attribute as
-// ignored.
-// For details see https://github.com/llvm/llvm-project/issues/55790
-void test_standard_syntax() {
-  [[clang::noderef]] int i; // expected-warning {{'clang::noderef' attribute 
ignored}}
+void test_standard_syntax(void) {
+  [[clang::noderef]] int i; // expected-error{{'clang::noderef' attribute 
cannot be applied to a declaration}} expected-warning{{'noderef' can only be 
used on an array or pointer type}}
 
-  [[clang::noderef]] int *p1; // expected-warning {{'clang::noderef' attribute 
ignored}}
-  *p1;
+  [[clang::noderef]] int *p1; // expected-error{{'clang::noderef' attribute 
cannot be applied to a declaration}} expected-note{{p1 declared here}}
+  *p1; // expected-warning{{dereferencing p1; was declared with a 'noderef' 
type}}
 
-  int *p2 [[clang::noderef]]; // expected-warning {{'clang::noderef' attribute 
ignored}}
-  *p2;
+  int *p2 [[clang::noderef]]; // expected-error{{'clang::noderef' attribute 
cannot be applied to a declaration}} expected-note{{p2 declared here}}
+  *p2; // expected-warning{{dereferencing p2; was declared with a 'noderef' 
type}}
 
-  int * [[clang::noderef]] p3; // expected-warning {{'clang::noderef' 
attribute ignored}}
+  int [[clang::noderef]] *p3; // expected-warning{{'noderef' can only be used 
on an array or pointer type}}
   *p3;
 
+  int * [[clang::noderef]] p4; // expected-note{{p4 declared here}}
+  *p4; // expected-warning{{dereferencing p4; was declared with a 'noderef' 
type}}
+
   typedef int* IntPtr;
-  [[clang::noderef]] IntPtr p4; // expected-warning {{'clang::noderef' 
attribute ignored}}
-  *p4;
+  [[clang::noderef]] IntPtr p5; // expected-error{{'clang::noderef' attribute 
cannot be applied to a declaration}} expected-note{{p5 declared here}}
+  *p5; // expected-warning{{dereferencing p5; was declared with a 'noderef' 
type}}
+
+  IntPtr [[clang::noderef]] p6; // expected-note{{p6 declared here}}
+  *p6; // expected-warning{{dereferencing p6; was declared with a 'noderef' 
type}}
 }
diff --git a/clang/test/Frontend/noderef.cpp b/clang/test/Frontend/noderef.cpp
index 44c11da0dc56a..92e9fb1d4c9df 100644
--- a/clang/test/Frontend/noderef.cpp
+++ b/clang/test/Frontend/noderef.cpp
@@ -15,7 +15,7 @@ void Normal() {
   int NODEREF i;        // expected-warning{{'noderef' can only be used on an 
array or pointer type}}
   int NODEREF *i_ptr;   // expected-note 2 {{i_ptr declared here}}
   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_ptr3;  // ok
   int *NODEREF *i_ptr4; // ok
 
   auto NODEREF *auto_i_ptr = i_ptr;
@@ -66,14 +66,14 @@ 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 func8() NODEREF; // expected-warning{{'noderef' can only be used on an 
array or pointer type}}
 
 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}}
+  int *NODEREF &b = xp; // ok
 }
 
 void BlockPointers() {
@@ -186,5 +186,28 @@ const int *const_cast_check(NODEREF int *x) {
 
 namespace GH116124 {
 // This declaration would previously cause a failed assertion.
-int *_Atomic a __attribute__((noderef));
+int *_Atomic a __attribute__((noderef)); // expected-warning{{'noderef' can 
only be used on an array or pointer type}}
+}
+
+namespace GH55790 {
+  [[clang::noderef]] int i; // expected-error{{'clang::noderef' attribute 
cannot be applied to a declaration}} expected-warning{{'noderef' can only be 
used on an array or pointer type}}
+
+  [[clang::noderef]] int *p1; // expected-error{{'clang::noderef' attribute 
cannot be applied to a declaration}} expected-note{{p1 declared here}}
+  int i1 = *p1; // expected-warning{{dereferencing p1; was declared with a 
'noderef' type}}
+
+  int *p2 [[clang::noderef]]; // expected-error{{'clang::noderef' attribute 
cannot be applied to a declaration}} expected-note{{p2 declared here}}
+  int i2 = *p2; // expected-warning{{dereferencing p2; was declared with a 
'noderef' type}}
+
+  int [[clang::noderef]] *p3; // expected-warning{{'noderef' can only be used 
on an array or pointer type}}
+  int i3 = *p3;
+
+  int * [[clang::noderef]] p4; // expected-note{{p4 declared here}}
+  int i4 = *p4; // expected-warning{{dereferencing p4; was declared with a 
'noderef' type}}
+
+  typedef int *IntPtr;
+  [[clang::noderef]] IntPtr p5; // expected-error{{'clang::noderef' attribute 
cannot be applied to a declaration}} expected-note{{p5 declared here}}
+  int i5 = *p5; // expected-warning{{dereferencing p5; was declared with a 
'noderef' type}}
+
+  IntPtr [[clang::noderef]] p6; // expected-note{{p6 declared here}}
+  int i6 = *p6; // expected-warning{{dereferencing p6; was declared with a 
'noderef' type}}
 }
diff --git a/clang/test/Sema/address_space_print_macro.c 
b/clang/test/Sema/address_space_print_macro.c
index e01fcf428270b..74640f3941b78 100644
--- a/clang/test/Sema/address_space_print_macro.c
+++ b/clang/test/Sema/address_space_print_macro.c
@@ -50,18 +50,18 @@ void override_macro_name(void) {
   ATTRS
   int *x;
 
-  int AS_ND *y = x; // expected-error{{initializing 'AS_ND int *' with an 
expression of type 'ATTRS int *' changes address space of pointer}}
+  int AS_ND *y = x; // expected-error{{initializing 'AS_ND int * 
__attribute__((noderef))' with an expression of type 'ATTRS int * 
__attribute__((noderef))' changes address space of pointer}}
 }
 
 void partial_macro_declaration(void) {
 #define ATTRS2 __attribute__((noderef))
   ATTRS2 __attribute__((address_space(1))) int *x;
 
-  int AS_ND *y = x; // expected-error{{initializing 'AS_ND int *' with an 
expression of type 'ATTRS2 int __attribute__((address_space(1))) *' changes 
address space of pointer}}
+  int AS_ND *y = x; // expected-error{{initializing 'AS_ND int * 
__attribute__((noderef))' with an expression of type 
'__attribute__((address_space(1))) int * __attribute__((noderef))' changes 
address space of pointer}}
 
   // The attribute not wrapped with a macro should be printed regularly.
 #define ATTRS3 __attribute__((address_space(1)))
   ATTRS3 __attribute__((noderef)) int *x2;
 
-  int AS_ND *y2 = x2; // expected-error{{initializing 'AS_ND int *' with an 
expression of type 'ATTRS3 int * __attribute__((noderef))' changes address 
space of pointer}}
+  int AS_ND *y2 = x2; // expected-error{{initializing 'AS_ND int * 
__attribute__((noderef))' with an expression of type 'ATTRS3 int * 
__attribute__((noderef))' changes address space of pointer}}
 }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to