[clang] [clang] Support [[]] spelling for noderef (PR #190100)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 1 21:02:22 PDT 2026
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 <leonardchan at google.com>
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 at -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 at -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}}
}
More information about the cfe-commits
mailing list