Skip to content

Commit bbbcc1d

Browse files
committed
Reapply "[clang][nullability] allow _Nonnull etc on nullable class types (#82705)"
This reverts commit ca4c4a6. This was intended not to introduce new consistency diagnostics for smart pointer types, but failed to ignore sugar around types when detecting this. Fixed and test added.
1 parent eee8c61 commit bbbcc1d

21 files changed

+226
-29
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,21 @@ Attribute Changes in Clang
253253
added a new extension query ``__has_extension(swiftcc)`` corresponding to the
254254
``__attribute__((swiftcc))`` attribute.
255255

256+
- The ``_Nullable`` and ``_Nonnull`` family of type attributes can now apply
257+
to certain C++ class types, such as smart pointers:
258+
``void useObject(std::unique_ptr<Object> _Nonnull obj);``.
259+
260+
This works for standard library types including ``unique_ptr``, ``shared_ptr``,
261+
and ``function``. See
262+
`the attribute reference documentation <https://llvm.org/docs/AttributeReference.html#nullability-attributes>`_
263+
for the full list.
264+
265+
- The ``_Nullable`` attribute can be applied to C++ class declarations:
266+
``template <class T> class _Nullable MySmartPointer {};``.
267+
268+
This allows the ``_Nullable`` and ``_Nonnull`` family of type attributes to
269+
apply to this class.
270+
256271
Improvements to Clang's diagnostics
257272
-----------------------------------
258273
- Clang now applies syntax highlighting to the code snippets it

clang/include/clang/Basic/Attr.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2178,9 +2178,10 @@ def TypeNonNull : TypeAttr {
21782178
let Documentation = [TypeNonNullDocs];
21792179
}
21802180

2181-
def TypeNullable : TypeAttr {
2181+
def TypeNullable : DeclOrTypeAttr {
21822182
let Spellings = [CustomKeyword<"_Nullable">];
21832183
let Documentation = [TypeNullableDocs];
2184+
// let Subjects = SubjectList<[CXXRecord], ErrorDiag>;
21842185
}
21852186

21862187
def TypeNullableResult : TypeAttr {

clang/include/clang/Basic/AttrDocs.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4151,6 +4151,20 @@ non-underscored keywords. For example:
41514151
@property (assign, nullable) NSView *superview;
41524152
@property (readonly, nonnull) NSArray *subviews;
41534153
@end
4154+
4155+
As well as built-in pointer types, the nullability attributes can be attached
4156+
to C++ classes marked with the ``_Nullable`` attribute.
4157+
4158+
The following C++ standard library types are considered nullable:
4159+
``unique_ptr``, ``shared_ptr``, ``auto_ptr``, ``exception_ptr``, ``function``,
4160+
``move_only_function`` and ``coroutine_handle``.
4161+
4162+
Types should be marked nullable only where the type itself leaves nullability
4163+
ambiguous. For example, ``std::optional`` is not marked ``_Nullable``, because
4164+
``optional<int> _Nullable`` is redundant and ``optional<int> _Nonnull`` is
4165+
not a useful type. ``std::weak_ptr`` is not nullable, because its nullability
4166+
can change with no visible modification, so static annotation is unlikely to be
4167+
unhelpful.
41544168
}];
41554169
}
41564170

@@ -4185,6 +4199,17 @@ The ``_Nullable`` nullability qualifier indicates that a value of the
41854199
int fetch_or_zero(int * _Nullable ptr);
41864200

41874201
a caller of ``fetch_or_zero`` can provide null.
4202+
4203+
The ``_Nullable`` attribute on classes indicates that the given class can
4204+
represent null values, and so the ``_Nullable``, ``_Nonnull`` etc qualifiers
4205+
make sense for this type. For example:
4206+
4207+
.. code-block:: c
4208+
4209+
class _Nullable ArenaPointer { ... };
4210+
4211+
ArenaPointer _Nonnull x = ...;
4212+
ArenaPointer _Nullable y = nullptr;
41884213
}];
41894214
}
41904215

clang/include/clang/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ EXTENSION(define_target_os_macros,
9494
FEATURE(enumerator_attributes, true)
9595
FEATURE(nullability, true)
9696
FEATURE(nullability_on_arrays, true)
97+
FEATURE(nullability_on_classes, true)
9798
FEATURE(nullability_nullable_result, true)
9899
FEATURE(memory_sanitizer,
99100
LangOpts.Sanitize.hasOneOf(SanitizerKind::Memory |

clang/include/clang/Parse/Parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3014,6 +3014,7 @@ class Parser : public CodeCompletionHandler {
30143014
void DiagnoseAndSkipExtendedMicrosoftTypeAttributes();
30153015
SourceLocation SkipExtendedMicrosoftTypeAttributes();
30163016
void ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs);
3017+
void ParseNullabilityClassAttributes(ParsedAttributes &attrs);
30173018
void ParseBorlandTypeAttributes(ParsedAttributes &attrs);
30183019
void ParseOpenCLKernelAttributes(ParsedAttributes &attrs);
30193020
void ParseOpenCLQualifiers(ParsedAttributes &Attrs);

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,9 @@ class Sema final {
16551655
/// Add [[gsl::Pointer]] attributes for std:: types.
16561656
void inferGslPointerAttribute(TypedefNameDecl *TD);
16571657

1658+
/// Add _Nullable attributes for std:: types.
1659+
void inferNullableClassAttribute(CXXRecordDecl *CRD);
1660+
16581661
enum PragmaOptionsAlignKind {
16591662
POAK_Native, // #pragma options align=native
16601663
POAK_Natural, // #pragma options align=natural

clang/lib/AST/Type.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4642,16 +4642,15 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
46424642
case Type::Auto:
46434643
return ResultIfUnknown;
46444644

4645-
// Dependent template specializations can instantiate to pointer
4646-
// types unless they're known to be specializations of a class
4647-
// template.
4645+
// Dependent template specializations could instantiate to pointer types.
46484646
case Type::TemplateSpecialization:
4649-
if (TemplateDecl *templateDecl
4650-
= cast<TemplateSpecializationType>(type.getTypePtr())
4651-
->getTemplateName().getAsTemplateDecl()) {
4652-
if (isa<ClassTemplateDecl>(templateDecl))
4653-
return false;
4654-
}
4647+
// If it's a known class template, we can already check if it's nullable.
4648+
if (TemplateDecl *templateDecl =
4649+
cast<TemplateSpecializationType>(type.getTypePtr())
4650+
->getTemplateName()
4651+
.getAsTemplateDecl())
4652+
if (auto *CTD = dyn_cast<ClassTemplateDecl>(templateDecl))
4653+
return CTD->getTemplatedDecl()->hasAttr<TypeNullableAttr>();
46554654
return ResultIfUnknown;
46564655

46574656
case Type::Builtin:
@@ -4708,6 +4707,17 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
47084707
}
47094708
llvm_unreachable("unknown builtin type");
47104709

4710+
case Type::Record: {
4711+
const RecordDecl *RD = cast<RecordType>(type)->getDecl();
4712+
// For template specializations, look only at primary template attributes.
4713+
// This is a consistent regardless of whether the instantiation is known.
4714+
if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
4715+
return CTSD->getSpecializedTemplate()
4716+
->getTemplatedDecl()
4717+
->hasAttr<TypeNullableAttr>();
4718+
return RD->hasAttr<TypeNullableAttr>();
4719+
}
4720+
47114721
// Non-pointer types.
47124722
case Type::Complex:
47134723
case Type::LValueReference:
@@ -4725,7 +4735,6 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
47254735
case Type::DependentAddressSpace:
47264736
case Type::FunctionProto:
47274737
case Type::FunctionNoProto:
4728-
case Type::Record:
47294738
case Type::DeducedTemplateSpecialization:
47304739
case Type::Enum:
47314740
case Type::InjectedClassName:

clang/lib/CodeGen/CGCall.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4379,7 +4379,8 @@ void CodeGenFunction::EmitNonNullArgCheck(RValue RV, QualType ArgType,
43794379
NNAttr = getNonNullAttr(AC.getDecl(), PVD, ArgType, ArgNo);
43804380

43814381
bool CanCheckNullability = false;
4382-
if (SanOpts.has(SanitizerKind::NullabilityArg) && !NNAttr && PVD) {
4382+
if (SanOpts.has(SanitizerKind::NullabilityArg) && !NNAttr && PVD &&
4383+
!PVD->getType()->isRecordType()) {
43834384
auto Nullability = PVD->getType()->getNullability();
43844385
CanCheckNullability = Nullability &&
43854386
*Nullability == NullabilityKind::NonNull &&

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
989989
// return value. Initialize the flag to 'true' and refine it in EmitParmDecl.
990990
if (SanOpts.has(SanitizerKind::NullabilityReturn)) {
991991
auto Nullability = FnRetTy->getNullability();
992-
if (Nullability && *Nullability == NullabilityKind::NonNull) {
992+
if (Nullability && *Nullability == NullabilityKind::NonNull &&
993+
!FnRetTy->isRecordType()) {
993994
if (!(SanOpts.has(SanitizerKind::ReturnsNonnullAttribute) &&
994995
CurCodeDecl && CurCodeDecl->getAttr<ReturnsNonNullAttr>()))
995996
RetValNullabilityPrecondition =

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,6 +1502,15 @@ void Parser::ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs) {
15021502
}
15031503
}
15041504

1505+
void Parser::ParseNullabilityClassAttributes(ParsedAttributes &attrs) {
1506+
while (Tok.is(tok::kw__Nullable)) {
1507+
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
1508+
auto Kind = Tok.getKind();
1509+
SourceLocation AttrNameLoc = ConsumeToken();
1510+
attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0, Kind);
1511+
}
1512+
}
1513+
15051514
/// Determine whether the following tokens are valid after a type-specifier
15061515
/// which could be a standalone declaration. This will conservatively return
15071516
/// true if there's any doubt, and is appropriate for insert-';' fixits.
@@ -1683,15 +1692,21 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
16831692

16841693
ParsedAttributes attrs(AttrFactory);
16851694
// If attributes exist after tag, parse them.
1686-
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
1687-
1688-
// Parse inheritance specifiers.
1689-
if (Tok.isOneOf(tok::kw___single_inheritance, tok::kw___multiple_inheritance,
1690-
tok::kw___virtual_inheritance))
1691-
ParseMicrosoftInheritanceClassAttributes(attrs);
1692-
1693-
// Allow attributes to precede or succeed the inheritance specifiers.
1694-
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
1695+
for (;;) {
1696+
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
1697+
// Parse inheritance specifiers.
1698+
if (Tok.isOneOf(tok::kw___single_inheritance,
1699+
tok::kw___multiple_inheritance,
1700+
tok::kw___virtual_inheritance)) {
1701+
ParseMicrosoftInheritanceClassAttributes(attrs);
1702+
continue;
1703+
}
1704+
if (Tok.is(tok::kw__Nullable)) {
1705+
ParseNullabilityClassAttributes(attrs);
1706+
continue;
1707+
}
1708+
break;
1709+
}
16951710

16961711
// Source location used by FIXIT to insert misplaced
16971712
// C++11 attributes

0 commit comments

Comments
 (0)