summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam McCall <sam.mccall@gmail.com>2024-03-28 22:45:55 +0100
committerSam McCall <sam.mccall@gmail.com>2024-03-28 23:57:09 +0100
commitbbbcc1d99d08855069f4501c896c43a6d4d7b598 (patch)
treefbe29ac3a9d9f1009f0418ce44c8d164b1cad0cc
parenteee8c6150a1c3af54542d34d1c084a86feda4d96 (diff)
Reapply "[clang][nullability] allow _Nonnull etc on nullable class types (#82705)"
This reverts commit ca4c4a6758d184f209cb5d88ef42ecc011b11642. 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.
-rw-r--r--clang/docs/ReleaseNotes.rst15
-rw-r--r--clang/include/clang/Basic/Attr.td3
-rw-r--r--clang/include/clang/Basic/AttrDocs.td25
-rw-r--r--clang/include/clang/Basic/Features.def1
-rw-r--r--clang/include/clang/Parse/Parser.h1
-rw-r--r--clang/include/clang/Sema/Sema.h3
-rw-r--r--clang/lib/AST/Type.cpp29
-rw-r--r--clang/lib/CodeGen/CGCall.cpp3
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.cpp3
-rw-r--r--clang/lib/Parse/ParseDeclCXX.cpp33
-rw-r--r--clang/lib/Sema/SemaAttr.cpp12
-rw-r--r--clang/lib/Sema/SemaChecking.cpp9
-rw-r--r--clang/lib/Sema/SemaDecl.cpp4
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp18
-rw-r--r--clang/lib/Sema/SemaInit.cpp5
-rw-r--r--clang/lib/Sema/SemaOverload.cpp7
-rw-r--r--clang/lib/Sema/SemaTemplate.cpp1
-rw-r--r--clang/lib/Sema/SemaType.cpp18
-rw-r--r--clang/test/Sema/nullability.c2
-rw-r--r--clang/test/SemaCXX/nullability.cpp62
-rw-r--r--clang/test/SemaObjCXX/nullability-consistency.mm1
21 files changed, 226 insertions, 29 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 232de0d7d8bb..c303eee7be79 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -253,6 +253,21 @@ Attribute Changes in Clang
added a new extension query ``__has_extension(swiftcc)`` corresponding to the
``__attribute__((swiftcc))`` attribute.
+- The ``_Nullable`` and ``_Nonnull`` family of type attributes can now apply
+ to certain C++ class types, such as smart pointers:
+ ``void useObject(std::unique_ptr<Object> _Nonnull obj);``.
+
+ This works for standard library types including ``unique_ptr``, ``shared_ptr``,
+ and ``function``. See
+ `the attribute reference documentation <https://llvm.org/docs/AttributeReference.html#nullability-attributes>`_
+ for the full list.
+
+- The ``_Nullable`` attribute can be applied to C++ class declarations:
+ ``template <class T> class _Nullable MySmartPointer {};``.
+
+ This allows the ``_Nullable`` and ``_Nonnull`` family of type attributes to
+ apply to this class.
+
Improvements to Clang's diagnostics
-----------------------------------
- Clang now applies syntax highlighting to the code snippets it
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 80e607525a0a..6584460cf568 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2178,9 +2178,10 @@ def TypeNonNull : TypeAttr {
let Documentation = [TypeNonNullDocs];
}
-def TypeNullable : TypeAttr {
+def TypeNullable : DeclOrTypeAttr {
let Spellings = [CustomKeyword<"_Nullable">];
let Documentation = [TypeNullableDocs];
+// let Subjects = SubjectList<[CXXRecord], ErrorDiag>;
}
def TypeNullableResult : TypeAttr {
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 3ea4d676b4f8..0ca4ea377fc3 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -4151,6 +4151,20 @@ non-underscored keywords. For example:
@property (assign, nullable) NSView *superview;
@property (readonly, nonnull) NSArray *subviews;
@end
+
+As well as built-in pointer types, the nullability attributes can be attached
+to C++ classes marked with the ``_Nullable`` attribute.
+
+The following C++ standard library types are considered nullable:
+``unique_ptr``, ``shared_ptr``, ``auto_ptr``, ``exception_ptr``, ``function``,
+``move_only_function`` and ``coroutine_handle``.
+
+Types should be marked nullable only where the type itself leaves nullability
+ambiguous. For example, ``std::optional`` is not marked ``_Nullable``, because
+``optional<int> _Nullable`` is redundant and ``optional<int> _Nonnull`` is
+not a useful type. ``std::weak_ptr`` is not nullable, because its nullability
+can change with no visible modification, so static annotation is unlikely to be
+unhelpful.
}];
}
@@ -4185,6 +4199,17 @@ The ``_Nullable`` nullability qualifier indicates that a value of the
int fetch_or_zero(int * _Nullable ptr);
a caller of ``fetch_or_zero`` can provide null.
+
+The ``_Nullable`` attribute on classes indicates that the given class can
+represent null values, and so the ``_Nullable``, ``_Nonnull`` etc qualifiers
+make sense for this type. For example:
+
+ .. code-block:: c
+
+ class _Nullable ArenaPointer { ... };
+
+ ArenaPointer _Nonnull x = ...;
+ ArenaPointer _Nullable y = nullptr;
}];
}
diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index b41aadc73f20..fe4d1c4afcca 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -94,6 +94,7 @@ EXTENSION(define_target_os_macros,
FEATURE(enumerator_attributes, true)
FEATURE(nullability, true)
FEATURE(nullability_on_arrays, true)
+FEATURE(nullability_on_classes, true)
FEATURE(nullability_nullable_result, true)
FEATURE(memory_sanitizer,
LangOpts.Sanitize.hasOneOf(SanitizerKind::Memory |
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index bba8ef4ff017..580bf2a5d79d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3014,6 +3014,7 @@ private:
void DiagnoseAndSkipExtendedMicrosoftTypeAttributes();
SourceLocation SkipExtendedMicrosoftTypeAttributes();
void ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs);
+ void ParseNullabilityClassAttributes(ParsedAttributes &attrs);
void ParseBorlandTypeAttributes(ParsedAttributes &attrs);
void ParseOpenCLKernelAttributes(ParsedAttributes &attrs);
void ParseOpenCLQualifiers(ParsedAttributes &Attrs);
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3a1abd4c7892..3fcb963e752f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1655,6 +1655,9 @@ public:
/// Add [[gsl::Pointer]] attributes for std:: types.
void inferGslPointerAttribute(TypedefNameDecl *TD);
+ /// Add _Nullable attributes for std:: types.
+ void inferNullableClassAttribute(CXXRecordDecl *CRD);
+
enum PragmaOptionsAlignKind {
POAK_Native, // #pragma options align=native
POAK_Natural, // #pragma options align=natural
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 8f3e26d46019..47fdbfe21e58 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -4642,16 +4642,15 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
case Type::Auto:
return ResultIfUnknown;
- // Dependent template specializations can instantiate to pointer
- // types unless they're known to be specializations of a class
- // template.
+ // Dependent template specializations could instantiate to pointer types.
case Type::TemplateSpecialization:
- if (TemplateDecl *templateDecl
- = cast<TemplateSpecializationType>(type.getTypePtr())
- ->getTemplateName().getAsTemplateDecl()) {
- if (isa<ClassTemplateDecl>(templateDecl))
- return false;
- }
+ // If it's a known class template, we can already check if it's nullable.
+ if (TemplateDecl *templateDecl =
+ cast<TemplateSpecializationType>(type.getTypePtr())
+ ->getTemplateName()
+ .getAsTemplateDecl())
+ if (auto *CTD = dyn_cast<ClassTemplateDecl>(templateDecl))
+ return CTD->getTemplatedDecl()->hasAttr<TypeNullableAttr>();
return ResultIfUnknown;
case Type::Builtin:
@@ -4708,6 +4707,17 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
}
llvm_unreachable("unknown builtin type");
+ case Type::Record: {
+ const RecordDecl *RD = cast<RecordType>(type)->getDecl();
+ // For template specializations, look only at primary template attributes.
+ // This is a consistent regardless of whether the instantiation is known.
+ if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+ return CTSD->getSpecializedTemplate()
+ ->getTemplatedDecl()
+ ->hasAttr<TypeNullableAttr>();
+ return RD->hasAttr<TypeNullableAttr>();
+ }
+
// Non-pointer types.
case Type::Complex:
case Type::LValueReference:
@@ -4725,7 +4735,6 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
case Type::DependentAddressSpace:
case Type::FunctionProto:
case Type::FunctionNoProto:
- case Type::Record:
case Type::DeducedTemplateSpecialization:
case Type::Enum:
case Type::InjectedClassName:
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index a5fe39633679..4a4426d13e7b 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4379,7 +4379,8 @@ void CodeGenFunction::EmitNonNullArgCheck(RValue RV, QualType ArgType,
NNAttr = getNonNullAttr(AC.getDecl(), PVD, ArgType, ArgNo);
bool CanCheckNullability = false;
- if (SanOpts.has(SanitizerKind::NullabilityArg) && !NNAttr && PVD) {
+ if (SanOpts.has(SanitizerKind::NullabilityArg) && !NNAttr && PVD &&
+ !PVD->getType()->isRecordType()) {
auto Nullability = PVD->getType()->getNullability();
CanCheckNullability = Nullability &&
*Nullability == NullabilityKind::NonNull &&
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 44103884940f..fa3f29724589 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -989,7 +989,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
// return value. Initialize the flag to 'true' and refine it in EmitParmDecl.
if (SanOpts.has(SanitizerKind::NullabilityReturn)) {
auto Nullability = FnRetTy->getNullability();
- if (Nullability && *Nullability == NullabilityKind::NonNull) {
+ if (Nullability && *Nullability == NullabilityKind::NonNull &&
+ !FnRetTy->isRecordType()) {
if (!(SanOpts.has(SanitizerKind::ReturnsNonnullAttribute) &&
CurCodeDecl && CurCodeDecl->getAttr<ReturnsNonNullAttr>()))
RetValNullabilityPrecondition =
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 63fe678cbb29..861a25dc5103 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1502,6 +1502,15 @@ void Parser::ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs) {
}
}
+void Parser::ParseNullabilityClassAttributes(ParsedAttributes &attrs) {
+ while (Tok.is(tok::kw__Nullable)) {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ auto Kind = Tok.getKind();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0, Kind);
+ }
+}
+
/// Determine whether the following tokens are valid after a type-specifier
/// which could be a standalone declaration. This will conservatively return
/// true if there's any doubt, and is appropriate for insert-';' fixits.
@@ -1683,15 +1692,21 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
ParsedAttributes attrs(AttrFactory);
// If attributes exist after tag, parse them.
- MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
-
- // Parse inheritance specifiers.
- if (Tok.isOneOf(tok::kw___single_inheritance, tok::kw___multiple_inheritance,
- tok::kw___virtual_inheritance))
- ParseMicrosoftInheritanceClassAttributes(attrs);
-
- // Allow attributes to precede or succeed the inheritance specifiers.
- MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
+ for (;;) {
+ MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
+ // Parse inheritance specifiers.
+ if (Tok.isOneOf(tok::kw___single_inheritance,
+ tok::kw___multiple_inheritance,
+ tok::kw___virtual_inheritance)) {
+ ParseMicrosoftInheritanceClassAttributes(attrs);
+ continue;
+ }
+ if (Tok.is(tok::kw__Nullable)) {
+ ParseNullabilityClassAttributes(attrs);
+ continue;
+ }
+ break;
+ }
// Source location used by FIXIT to insert misplaced
// C++11 attributes
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 0dcf42e48997..a5dd158808f2 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -215,6 +215,18 @@ void Sema::inferGslOwnerPointerAttribute(CXXRecordDecl *Record) {
inferGslPointerAttribute(Record, Record);
}
+void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) {
+ static llvm::StringSet<> Nullable{
+ "auto_ptr", "shared_ptr", "unique_ptr", "exception_ptr",
+ "coroutine_handle", "function", "move_only_function",
+ };
+
+ if (CRD->isInStdNamespace() && Nullable.count(CRD->getName()) &&
+ !CRD->hasAttr<TypeNullableAttr>())
+ for (Decl *Redecl : CRD->redecls())
+ Redecl->addAttr(TypeNullableAttr::CreateImplicit(Context));
+}
+
void Sema::ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind,
SourceLocation PragmaLoc) {
PragmaMsStackAction Action = Sema::PSK_Reset;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2e4e18a3ebf7..a1ce867248a4 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -27,6 +27,7 @@
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.h"
#include "clang/AST/FormatString.h"
+#include "clang/AST/IgnoreExpr.h"
#include "clang/AST/NSAPI.h"
#include "clang/AST/NonTrivialTypeVisitor.h"
#include "clang/AST/OperationKinds.h"
@@ -7609,6 +7610,14 @@ bool Sema::getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember,
///
/// Returns true if the value evaluates to null.
static bool CheckNonNullExpr(Sema &S, const Expr *Expr) {
+ // Treat (smart) pointers constructed from nullptr as null, whether we can
+ // const-evaluate them or not.
+ // This must happen first: the smart pointer expr might have _Nonnull type!
+ if (isa<CXXNullPtrLiteralExpr>(
+ IgnoreExprNodes(Expr, IgnoreImplicitAsWrittenSingleStep,
+ IgnoreElidableImplicitConstructorSingleStep)))
+ return true;
+
// If the expression has non-null type, it doesn't evaluate to null.
if (auto nullability = Expr->IgnoreImplicit()->getType()->getNullability()) {
if (*nullability == NullabilityKind::NonNull)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 0bd88ece2aa5..19a52a2d7037 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18317,8 +18317,10 @@ CreateNewDecl:
if (PrevDecl)
mergeDeclAttributes(New, PrevDecl);
- if (auto *CXXRD = dyn_cast<CXXRecordDecl>(New))
+ if (auto *CXXRD = dyn_cast<CXXRecordDecl>(New)) {
inferGslOwnerPointerAttribute(CXXRD);
+ inferNullableClassAttribute(CXXRD);
+ }
// If there's a #pragma GCC visibility in scope, set the visibility of this
// record.
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index f25f3afd0f4a..8bce04640e74 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5982,6 +5982,20 @@ static void handleBuiltinAliasAttr(Sema &S, Decl *D,
D->addAttr(::new (S.Context) BuiltinAliasAttr(S.Context, AL, Ident));
}
+static void handleNullableTypeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ if (AL.isUsedAsTypeAttr())
+ return;
+
+ if (auto *CRD = dyn_cast<CXXRecordDecl>(D);
+ !CRD || !(CRD->isClass() || CRD->isStruct())) {
+ S.Diag(AL.getRange().getBegin(), diag::err_attribute_wrong_decl_type_str)
+ << AL << AL.isRegularKeywordAttribute() << "classes";
+ return;
+ }
+
+ handleSimpleAttribute<TypeNullableAttr>(S, D, AL);
+}
+
static void handlePreferredTypeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (!AL.hasParsedType()) {
S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1;
@@ -9933,6 +9947,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_UsingIfExists:
handleSimpleAttribute<UsingIfExistsAttr>(S, D, AL);
break;
+
+ case ParsedAttr::AT_TypeNullable:
+ handleNullableTypeAttr(S, D, AL);
+ break;
}
}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index dce225a7204d..3382d56303d6 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7079,6 +7079,11 @@ PerformConstructorInitialization(Sema &S,
hasCopyOrMoveCtorParam(S.Context,
getConstructorInfo(Step.Function.FoundDecl));
+ // A smart pointer constructed from a nullable pointer is nullable.
+ if (NumArgs == 1 && !Kind.isExplicitCast())
+ S.diagnoseNullableToNonnullConversion(
+ Entity.getType(), Args.front()->getType(), Kind.getLocation());
+
// Determine the arguments required to actually perform the constructor
// call.
if (S.CompleteConstructorCall(Constructor, Step.Type, Args, Loc,
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 51450e486eae..de0c2e739963 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14811,6 +14811,13 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
}
}
+ // Check for nonnull = nullable.
+ // This won't be caught in the arg's initialization: the parameter to
+ // the assignment operator is not marked nonnull.
+ if (Op == OO_Equal)
+ diagnoseNullableToNonnullConversion(Args[0]->getType(),
+ Args[1]->getType(), OpLoc);
+
// Convert the arguments.
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
// Best->Access is only meaningful for class members.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index e575bb2df97f..de728305d55a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2171,6 +2171,7 @@ DeclResult Sema::CheckClassTemplate(
AddPushedVisibilityAttribute(NewClass);
inferGslOwnerPointerAttribute(NewClass);
+ inferNullableClassAttribute(NewClass);
if (TUK != TUK_Friend) {
// Per C++ [basic.scope.temp]p2, skip the template parameter scopes.
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index fd94caa4e1d4..2ddc9c0cf5fb 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -4717,6 +4717,18 @@ static bool DiagnoseMultipleAddrSpaceAttributes(Sema &S, LangAS ASOld,
return false;
}
+// Whether this is a type broadly expected to have nullability attached.
+// These types are affected by `#pragma assume_nonnull`, and missing nullability
+// will be diagnosed with -Wnullability-completeness.
+static bool shouldHaveNullability(QualType T) {
+ return T->canHaveNullability(/*ResultIfUnknown=*/false) &&
+ // For now, do not infer/require nullability on C++ smart pointers.
+ // It's unclear whether the pragma's behavior is useful for C++.
+ // e.g. treating type-aliases and template-type-parameters differently
+ // from types of declarations can be surprising.
+ !isa<RecordType>(T->getCanonicalTypeInternal());
+}
+
static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
QualType declSpecType,
TypeSourceInfo *TInfo) {
@@ -4835,8 +4847,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// inner pointers.
complainAboutMissingNullability = CAMN_InnerPointers;
- if (T->canHaveNullability(/*ResultIfUnknown*/ false) &&
- !T->getNullability()) {
+ if (shouldHaveNullability(T) && !T->getNullability()) {
// Note that we allow but don't require nullability on dependent types.
++NumPointersRemaining;
}
@@ -5059,8 +5070,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// If the type itself could have nullability but does not, infer pointer
// nullability and perform consistency checking.
if (S.CodeSynthesisContexts.empty()) {
- if (T->canHaveNullability(/*ResultIfUnknown*/ false) &&
- !T->getNullability()) {
+ if (shouldHaveNullability(T) && !T->getNullability()) {
if (isVaList(T)) {
// Record that we've seen a pointer, but do nothing else.
if (NumPointersRemaining > 0)
diff --git a/clang/test/Sema/nullability.c b/clang/test/Sema/nullability.c
index 7d193bea4677..0401516233b6 100644
--- a/clang/test/Sema/nullability.c
+++ b/clang/test/Sema/nullability.c
@@ -248,3 +248,5 @@ void arraysInBlocks(void) {
void (^withTypedefBad)(INTS _Nonnull [2]) = // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int[4]')}}
^(INTS _Nonnull x[2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int[4]')}}
}
+
+struct _Nullable NotCplusplusClass {}; // expected-error {{'_Nullable' attribute only applies to classes}}
diff --git a/clang/test/SemaCXX/nullability.cpp b/clang/test/SemaCXX/nullability.cpp
index 8d0c4dc195a6..d52ba4efaccd 100644
--- a/clang/test/SemaCXX/nullability.cpp
+++ b/clang/test/SemaCXX/nullability.cpp
@@ -4,6 +4,10 @@
#else
# error nullability feature should be defined
#endif
+#if __has_feature(nullability_on_classes)
+#else
+# error smart-pointer feature should be defined
+#endif
#include "nullability-completeness.h"
@@ -27,6 +31,7 @@ template<typename T>
struct AddNonNull {
typedef _Nonnull T type; // expected-error{{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int'}}
// expected-error@-1{{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'std::nullptr_t'}}
+ // expected-error@-2{{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'NotPtr'}}
};
typedef AddNonNull<int *>::type nonnull_int_ptr_1;
@@ -35,6 +40,33 @@ typedef AddNonNull<nullptr_t>::type nonnull_int_ptr_3; // expected-note{{in inst
typedef AddNonNull<int>::type nonnull_non_pointer_1; // expected-note{{in instantiation of template class 'AddNonNull<int>' requested here}}
+// Nullability on C++ class types (smart pointers).
+struct NotPtr{};
+typedef AddNonNull<NotPtr>::type nonnull_non_pointer_2; // expected-note{{in instantiation}}
+struct _Nullable SmartPtr{
+ SmartPtr();
+ SmartPtr(nullptr_t);
+ SmartPtr(const SmartPtr&);
+ SmartPtr(SmartPtr&&);
+ SmartPtr &operator=(const SmartPtr&);
+ SmartPtr &operator=(SmartPtr&&);
+};
+typedef AddNonNull<SmartPtr>::type nonnull_smart_pointer_1;
+template<class> struct _Nullable SmartPtrTemplate{};
+typedef AddNonNull<SmartPtrTemplate<int>>::type nonnull_smart_pointer_2;
+namespace std { inline namespace __1 {
+ template <class> class unique_ptr {};
+ template <class> class function;
+ template <class Ret, class... Args> class function<Ret(Args...)> {};
+} }
+typedef AddNonNull<std::unique_ptr<int>>::type nonnull_smart_pointer_3;
+typedef AddNonNull<std::function<int()>>::type nonnull_smart_pointer_4;
+
+class Derived : public SmartPtr {};
+Derived _Nullable x; // expected-error {{'_Nullable' cannot be applied}}
+class DerivedPrivate : private SmartPtr {};
+DerivedPrivate _Nullable y; // expected-error {{'_Nullable' cannot be applied}}
+
// Non-null checking within a template.
template<typename T>
struct AddNonNull2 {
@@ -54,6 +86,7 @@ void (*& accepts_nonnull_2)(_Nonnull int *ptr) = accepts_nonnull_1;
void (X::* accepts_nonnull_3)(_Nonnull int *ptr);
void accepts_nonnull_4(_Nonnull int *ptr);
void (&accepts_nonnull_5)(_Nonnull int *ptr) = accepts_nonnull_4;
+void accepts_nonnull_6(SmartPtr _Nonnull);
void test_accepts_nonnull_null_pointer_literal(X *x) {
accepts_nonnull_1(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
@@ -61,6 +94,8 @@ void test_accepts_nonnull_null_pointer_literal(X *x) {
(x->*accepts_nonnull_3)(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
accepts_nonnull_4(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
accepts_nonnull_5(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ accepts_nonnull_6(nullptr); // expected-warning{{null passed to a callee that requires a non-null argument}}
}
template<void FP(_Nonnull int*)>
@@ -71,6 +106,7 @@ void test_accepts_nonnull_null_pointer_literal_template() {
template void test_accepts_nonnull_null_pointer_literal_template<&accepts_nonnull_4>(); // expected-note{{instantiation of function template specialization}}
void TakeNonnull(void *_Nonnull);
+void TakeSmartNonnull(SmartPtr _Nonnull);
// Check different forms of assignment to a nonull type from a nullable one.
void AssignAndInitNonNull() {
void *_Nullable nullable;
@@ -81,12 +117,26 @@ void AssignAndInitNonNull() {
void *_Nonnull nonnull;
nonnull = nullable; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
nonnull = {nullable}; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
-
TakeNonnull(nullable); //expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull}}
TakeNonnull(nonnull); // OK
+ nonnull = (void *_Nonnull)nullable; // explicit cast OK
+
+ SmartPtr _Nullable s_nullable;
+ SmartPtr _Nonnull s(s_nullable); // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ SmartPtr _Nonnull s2{s_nullable}; // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ SmartPtr _Nonnull s3 = {s_nullable}; // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ SmartPtr _Nonnull s4 = s_nullable; // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ SmartPtr _Nonnull s_nonnull;
+ s_nonnull = s_nullable; // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ s_nonnull = {s_nullable}; // no warning here - might be nice?
+ TakeSmartNonnull(s_nullable); //expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull}}
+ TakeSmartNonnull(s_nonnull); // OK
+ s_nonnull = (SmartPtr _Nonnull)s_nullable; // explicit cast OK
+ s_nonnull = static_cast<SmartPtr _Nonnull>(s_nullable); // explicit cast OK
}
void *_Nullable ReturnNullable();
+SmartPtr _Nullable ReturnSmartNullable();
void AssignAndInitNonNullFromFn() {
void *_Nonnull p(ReturnNullable()); // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
@@ -96,8 +146,16 @@ void AssignAndInitNonNullFromFn() {
void *_Nonnull nonnull;
nonnull = ReturnNullable(); // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
nonnull = {ReturnNullable()}; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
-
TakeNonnull(ReturnNullable()); //expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull}}
+
+ SmartPtr _Nonnull s(ReturnSmartNullable()); // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ SmartPtr _Nonnull s2{ReturnSmartNullable()}; // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ SmartPtr _Nonnull s3 = {ReturnSmartNullable()}; // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ SmartPtr _Nonnull s4 = ReturnSmartNullable(); // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ SmartPtr _Nonnull s_nonnull;
+ s_nonnull = ReturnSmartNullable(); // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
+ s_nonnull = {ReturnSmartNullable()};
+ TakeSmartNonnull(ReturnSmartNullable()); // expected-warning{{implicit conversion from nullable pointer 'SmartPtr _Nullable' to non-nullable pointer type 'SmartPtr _Nonnull'}}
}
void ConditionalExpr(bool c) {
diff --git a/clang/test/SemaObjCXX/nullability-consistency.mm b/clang/test/SemaObjCXX/nullability-consistency.mm
index 6921d8b9d3dd..09c9a84475a9 100644
--- a/clang/test/SemaObjCXX/nullability-consistency.mm
+++ b/clang/test/SemaObjCXX/nullability-consistency.mm
@@ -9,6 +9,7 @@
#include "nullability-consistency-6.h"
#include "nullability-consistency-7.h"
#include "nullability-consistency-8.h"
+#include "nullability-consistency-smart.h"
#include "nullability-consistency-system.h"
void h1(int *ptr) { } // don't warn