[llvm-branch-commits] [clang] 9cd2413 - [clang] Add a new nullability annotation for swift async: _Nullable_result
Erik Pilkington via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Dec 7 14:24:23 PST 2020
Author: Erik Pilkington
Date: 2020-12-07T17:19:20-05:00
New Revision: 9cd2413f1ca7a7f40132ea3c44f251851aa325c2
URL: https://github.com/llvm/llvm-project/commit/9cd2413f1ca7a7f40132ea3c44f251851aa325c2
DIFF: https://github.com/llvm/llvm-project/commit/9cd2413f1ca7a7f40132ea3c44f251851aa325c2.diff
LOG: [clang] Add a new nullability annotation for swift async: _Nullable_result
_Nullable_result generally like _Nullable, except when being imported into a
swift async method. rdar://70106409
Differential revision: https://reviews.llvm.org/D92495
Added:
clang/test/SemaObjC/nullable-result.m
Modified:
clang/include/clang-c/Index.h
clang/include/clang/AST/Type.h
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/Features.def
clang/include/clang/Basic/Specifiers.h
clang/include/clang/Basic/TokenKinds.def
clang/include/clang/Sema/Sema.h
clang/lib/APINotes/APINotesYAMLCompiler.cpp
clang/lib/AST/Type.cpp
clang/lib/AST/TypePrinter.cpp
clang/lib/Basic/Diagnostic.cpp
clang/lib/Basic/IdentifierTable.cpp
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseTentative.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaCodeComplete.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaExprObjC.cpp
clang/lib/Sema/SemaType.cpp
clang/test/Index/nullability.c
clang/test/SemaObjC/nullability.m
clang/tools/c-index-test/c-index-test.c
clang/tools/libclang/CXType.cpp
Removed:
################################################################################
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 1c91e059c3222..e305283bbaf19 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -3861,7 +3861,15 @@ enum CXTypeNullabilityKind {
/**
* Nullability is not applicable to this type.
*/
- CXTypeNullability_Invalid = 3
+ CXTypeNullability_Invalid = 3,
+
+ /**
+ * Generally behaves like Nullable, except when used in a block parameter that
+ * was imported into a swift async method. There, swift will assume that the
+ * parameter can get null even if no error occured. _Nullable parameters are
+ * assumed to only get null on error.
+ */
+ CXTypeNullability_NullableResult = 4
};
/**
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 6dedd097ff891..60b8ee0f16149 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -4716,6 +4716,9 @@ class AttributedType : public Type, public llvm::FoldingSetNode {
case NullabilityKind::Nullable:
return attr::TypeNullable;
+ case NullabilityKind::NullableResult:
+ return attr::TypeNullableResult;
+
case NullabilityKind::Unspecified:
return attr::TypeNullUnspecified;
}
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 123313079d286..8e12aba5dad36 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1805,6 +1805,11 @@ def TypeNullable : TypeAttr {
let Documentation = [TypeNullableDocs];
}
+def TypeNullableResult : TypeAttr {
+ let Spellings = [Keyword<"_Nullable_result">];
+ let Documentation = [TypeNullableResultDocs];
+}
+
def TypeNullUnspecified : TypeAttr {
let Spellings = [Keyword<"_Null_unspecified">];
let Documentation = [TypeNullUnspecifiedDocs];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index a6f31e7e7f463..1cc8d9995b857 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3508,6 +3508,29 @@ a caller of ``fetch_or_zero`` can provide null.
}];
}
+def TypeNullableResultDocs : Documentation {
+ let Category = NullabilityDocs;
+ let Content = [{
+The ``_Nullable_result`` nullability qualifier means that a value of the
+``_Nullable_result`` pointer can be ``nil``, just like ``_Nullable``. Where this
+attribute
diff ers from ``_Nullable`` is when it's used on a parameter to a
+completion handler in a Swift async method. For instance, here:
+
+ .. code-block:: objc
+
+ -(void)fetchSomeDataWithID:(int)identifier
+ completionHandler:(void (^)(Data *_Nullable_result result, NSError *error))completionHandler;
+
+This method asynchronously calls ``completionHandler`` when the data is
+available, or calls it with an error. ``_Nullable_result`` indicates to the
+Swift importer that this is the uncommon case where ``result`` can get ``nil``
+even if no error has occured, and will therefore import it as a Swift optional
+type. Otherwise, if ``result`` was annotated with ``_Nullable``, the Swift
+importer will assume that ``result`` will always be non-nil unless an error
+occured.
+}];
+}
+
def TypeNullUnspecifiedDocs : Documentation {
let Category = NullabilityDocs;
let Content = [{
diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index 302c56763c39a..5424da67b62da 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -85,6 +85,7 @@ FEATURE(cxx_rtti, LangOpts.RTTI &&LangOpts.RTTIData)
FEATURE(enumerator_attributes, true)
FEATURE(nullability, true)
FEATURE(nullability_on_arrays, true)
+FEATURE(nullability_nullable_result, true)
FEATURE(memory_sanitizer,
LangOpts.Sanitize.hasOneOf(SanitizerKind::Memory |
SanitizerKind::KernelMemory))
diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
index 03de6ea6a434c..07d8177b8ab2f 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -309,7 +309,12 @@ namespace clang {
/// unspecified. This captures a (fairly rare) case where we
/// can't conclude anything about the nullability of the type even
/// though it has been considered.
- Unspecified
+ Unspecified,
+ // Generally behaves like Nullable, except when used in a block parameter
+ // that was imported into a swift async method. There, swift will assume
+ // that the parameter can get null even if no error occured. _Nullable
+ // parameters are assumed to only get null on error.
+ NullableResult,
};
/// Return true if \p L has a weaker nullability annotation than \p R. The
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index d8a2016e9caa2..572ebae6618db 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -650,6 +650,7 @@ ALIAS("__volatile__" , volatile , KEYALL)
// Type nullability.
KEYWORD(_Nonnull , KEYALL)
KEYWORD(_Nullable , KEYALL)
+KEYWORD(_Nullable_result , KEYALL)
KEYWORD(_Null_unspecified , KEYALL)
// Microsoft extensions which should be disabled in strict conformance mode
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4eddd07f66d06..9c838e5742839 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12448,6 +12448,7 @@ class Sema final {
/// Nullability type specifiers.
IdentifierInfo *Ident__Nonnull = nullptr;
IdentifierInfo *Ident__Nullable = nullptr;
+ IdentifierInfo *Ident__Nullable_result = nullptr;
IdentifierInfo *Ident__Null_unspecified = nullptr;
IdentifierInfo *Ident_NSError = nullptr;
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 997929a9bd220..a4120120a01c4 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -89,6 +89,7 @@ template <> struct ScalarEnumerationTraits<NullabilityKind> {
IO.enumCase(NK, "Nonnull", NullabilityKind::NonNull);
IO.enumCase(NK, "Optional", NullabilityKind::Nullable);
IO.enumCase(NK, "Unspecified", NullabilityKind::Unspecified);
+ IO.enumCase(NK, "NullableResult", NullabilityKind::NullableResult);
// TODO: Mapping this to it's own value would allow for better cross
// checking. Also the default should be Unknown.
IO.enumCase(NK, "Scalar", NullabilityKind::Unspecified);
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 1c29d3a385ae1..c07cab9a4006c 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3512,6 +3512,7 @@ bool AttributedType::isQualifier() const {
case attr::ObjCInertUnsafeUnretained:
case attr::TypeNonNull:
case attr::TypeNullable:
+ case attr::TypeNullableResult:
case attr::TypeNullUnspecified:
case attr::LifetimeBound:
case attr::AddressSpace:
@@ -4159,6 +4160,8 @@ AttributedType::getImmediateNullability() const {
return NullabilityKind::Nullable;
if (getAttrKind() == attr::TypeNullUnspecified)
return NullabilityKind::Unspecified;
+ if (getAttrKind() == attr::TypeNullableResult)
+ return NullabilityKind::NullableResult;
return None;
}
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index b42ffde4a0696..54c451291a077 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1579,6 +1579,8 @@ void TypePrinter::printAttributedBefore(const AttributedType *T,
OS << " _Nullable";
else if (T->getAttrKind() == attr::TypeNullUnspecified)
OS << " _Null_unspecified";
+ else if (T->getAttrKind() == attr::TypeNullableResult)
+ OS << " _Nullable_result";
else
llvm_unreachable("unhandled nullability");
spaceBeforePlaceHolder(OS);
@@ -1649,6 +1651,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::LifetimeBound:
case attr::TypeNonNull:
case attr::TypeNullable:
+ case attr::TypeNullableResult:
case attr::TypeNullUnspecified:
case attr::ObjCGC:
case attr::ObjCInertUnsafeUnretained:
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index 30a4c51b134bc..d3b2122e9c59f 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -55,6 +55,12 @@ const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB,
case NullabilityKind::Unspecified:
string = nullability.second ? "'null_unspecified'" : "'_Null_unspecified'";
break;
+
+ case NullabilityKind::NullableResult:
+ assert(!nullability.second &&
+ "_Nullable_result isn't supported as context-sensitive keyword");
+ string = "_Nullable_result";
+ break;
}
DB.AddString(string);
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 36b26d9b7c687..51c6e02e2e2e0 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -714,6 +714,11 @@ StringRef clang::getNullabilitySpelling(NullabilityKind kind,
case NullabilityKind::Nullable:
return isContextSensitive ? "nullable" : "_Nullable";
+ case NullabilityKind::NullableResult:
+ assert(!isContextSensitive &&
+ "_Nullable_result isn't supported as context-sensitive keyword");
+ return "_Nullable_result";
+
case NullabilityKind::Unspecified:
return isContextSensitive ? "null_unspecified" : "_Null_unspecified";
}
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index c3c56ddccb6b5..7ebf06a04fa76 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -833,6 +833,7 @@ void Parser::ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs) {
switch (Tok.getKind()) {
case tok::kw__Nonnull:
case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
case tok::kw__Null_unspecified: {
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
SourceLocation AttrNameLoc = ConsumeToken();
@@ -3536,6 +3537,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// Nullability type specifiers.
case tok::kw__Nonnull:
case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
case tok::kw__Null_unspecified:
ParseNullabilityTypeSpecifiers(DS.getAttributes());
continue;
@@ -5022,6 +5024,7 @@ bool Parser::isTypeSpecifierQualifier() {
case tok::kw__Nonnull:
case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
case tok::kw__Null_unspecified:
case tok::kw___kindof:
@@ -5249,6 +5252,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
case tok::kw__Nonnull:
case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
case tok::kw__Null_unspecified:
case tok::kw___kindof:
@@ -5524,6 +5528,7 @@ void Parser::ParseTypeQualifierListOpt(
// Nullability type specifiers.
case tok::kw__Nonnull:
case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
case tok::kw__Null_unspecified:
ParseNullabilityTypeSpecifiers(DS.getAttributes());
continue;
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index d0f1d2e09a872..3bf2bc455bfe8 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -842,7 +842,8 @@ Parser::TPResult Parser::TryParsePtrOperatorSeq() {
while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict,
tok::kw__Nonnull, tok::kw__Nullable,
- tok::kw__Null_unspecified, tok::kw__Atomic))
+ tok::kw__Nullable_result, tok::kw__Null_unspecified,
+ tok::kw__Atomic))
ConsumeToken();
} else {
return TPResult::True;
@@ -1437,6 +1438,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw___unaligned:
case tok::kw__Nonnull:
case tok::kw__Nullable:
+ case tok::kw__Nullable_result:
case tok::kw__Null_unspecified:
case tok::kw___kindof:
return TPResult::True;
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index b99dc33d87480..456daab7e0323 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -503,7 +503,8 @@ void Sema::diagnoseNullableToNonnullConversion(QualType DstType,
QualType SrcType,
SourceLocation Loc) {
Optional<NullabilityKind> ExprNullability = SrcType->getNullability(Context);
- if (!ExprNullability || *ExprNullability != NullabilityKind::Nullable)
+ if (!ExprNullability || (*ExprNullability != NullabilityKind::Nullable &&
+ *ExprNullability != NullabilityKind::NullableResult))
return;
Optional<NullabilityKind> TypeNullability = DstType->getNullability(Context);
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 10fd5522d1ad3..243f68d16462f 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -2700,6 +2700,10 @@ static std::string formatObjCParamQualifiers(unsigned ObjCQuals,
case NullabilityKind::Unspecified:
Result += "null_unspecified ";
break;
+
+ case NullabilityKind::NullableResult:
+ llvm_unreachable("Not supported as a context-sensitive keyword!");
+ break;
}
}
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3183b09402caf..738fe87d37b4f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -8562,8 +8562,12 @@ static QualType computeConditionalNullability(QualType ResTy, bool IsBin,
auto GetNullability = [&Ctx](QualType Ty) {
Optional<NullabilityKind> Kind = Ty->getNullability(Ctx);
- if (Kind)
+ if (Kind) {
+ // For our purposes, treat _Nullable_result as _Nullable.
+ if (*Kind == NullabilityKind::NullableResult)
+ return NullabilityKind::Nullable;
return *Kind;
+ }
return NullabilityKind::Unspecified;
};
diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp
index ced0952f57534..f5456ee0711e5 100644
--- a/clang/lib/Sema/SemaExprObjC.cpp
+++ b/clang/lib/Sema/SemaExprObjC.cpp
@@ -1563,12 +1563,20 @@ QualType Sema::getMessageSendResultType(const Expr *Receiver,
// Map the nullability of the result into a table index.
unsigned receiverNullabilityIdx = 0;
- if (auto nullability = ReceiverType->getNullability(Context))
+ if (Optional<NullabilityKind> nullability =
+ ReceiverType->getNullability(Context)) {
+ if (*nullability == NullabilityKind::NullableResult)
+ nullability = NullabilityKind::Nullable;
receiverNullabilityIdx = 1 + static_cast<unsigned>(*nullability);
+ }
unsigned resultNullabilityIdx = 0;
- if (auto nullability = resultType->getNullability(Context))
+ if (Optional<NullabilityKind> nullability =
+ resultType->getNullability(Context)) {
+ if (*nullability == NullabilityKind::NullableResult)
+ nullability = NullabilityKind::Nullable;
resultNullabilityIdx = 1 + static_cast<unsigned>(*nullability);
+ }
// The table of nullability mappings, indexed by the receiver's nullability
// and then the result type's nullability.
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 0d80bce10ffdd..df46aa3bfc1fc 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -147,6 +147,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
#define NULLABILITY_TYPE_ATTRS_CASELIST \
case ParsedAttr::AT_TypeNonNull: \
case ParsedAttr::AT_TypeNullable: \
+ case ParsedAttr::AT_TypeNullableResult: \
case ParsedAttr::AT_TypeNullUnspecified
namespace {
@@ -3893,6 +3894,11 @@ IdentifierInfo *Sema::getNullabilityKeyword(NullabilityKind nullability) {
Ident__Nullable = PP.getIdentifierInfo("_Nullable");
return Ident__Nullable;
+ case NullabilityKind::NullableResult:
+ if (!Ident__Nullable_result)
+ Ident__Nullable_result = PP.getIdentifierInfo("_Nullable_result");
+ return Ident__Nullable_result;
+
case NullabilityKind::Unspecified:
if (!Ident__Null_unspecified)
Ident__Null_unspecified = PP.getIdentifierInfo("_Null_unspecified");
@@ -3915,6 +3921,7 @@ static bool hasNullabilityAttr(const ParsedAttributesView &attrs) {
for (const ParsedAttr &AL : attrs) {
if (AL.getKind() == ParsedAttr::AT_TypeNonNull ||
AL.getKind() == ParsedAttr::AT_TypeNullable ||
+ AL.getKind() == ParsedAttr::AT_TypeNullableResult ||
AL.getKind() == ParsedAttr::AT_TypeNullUnspecified)
return true;
}
@@ -4333,6 +4340,9 @@ static Attr *createNullabilityAttr(ASTContext &Ctx, ParsedAttr &Attr,
case NullabilityKind::Nullable:
return createSimpleAttr<TypeNullableAttr>(Ctx, Attr);
+ case NullabilityKind::NullableResult:
+ return createSimpleAttr<TypeNullableResultAttr>(Ctx, Attr);
+
case NullabilityKind::Unspecified:
return createSimpleAttr<TypeNullUnspecifiedAttr>(Ctx, Attr);
}
@@ -7008,6 +7018,9 @@ static NullabilityKind mapNullabilityAttrKind(ParsedAttr::Kind kind) {
case ParsedAttr::AT_TypeNullable:
return NullabilityKind::Nullable;
+ case ParsedAttr::AT_TypeNullableResult:
+ return NullabilityKind::NullableResult;
+
case ParsedAttr::AT_TypeNullUnspecified:
return NullabilityKind::Unspecified;
diff --git a/clang/test/Index/nullability.c b/clang/test/Index/nullability.c
index 26e55cb65f476..2eeb685c6fcd0 100644
--- a/clang/test/Index/nullability.c
+++ b/clang/test/Index/nullability.c
@@ -2,9 +2,11 @@ int *a;
int * _Nonnull b;
int * _Nullable c;
int * _Null_unspecified d;
+int * _Nullable_result e;
// RUN: env CINDEXTEST_INCLUDE_ATTRIBUTED_TYPES=1 c-index-test -test-print-type %s | FileCheck %s
// CHECK: VarDecl=a:1:6 [type=int *] [typekind=Pointer] [isPOD=1] [pointeetype=int] [pointeekind=Int]
// CHECK: VarDecl=b:2:16 [type=int * _Nonnull] [typekind=Attributed] [nullability=nonnull] [canonicaltype=int *] [canonicaltypekind=Pointer] [modifiedtype=int *] [modifiedtypekind=Pointer] [isPOD=1]
// CHECK: VarDecl=c:3:17 [type=int * _Nullable] [typekind=Attributed] [nullability=nullable] [canonicaltype=int *] [canonicaltypekind=Pointer] [modifiedtype=int *] [modifiedtypekind=Pointer] [isPOD=1]
// CHECK: VarDecl=d:4:25 [type=int * _Null_unspecified] [typekind=Attributed] [nullability=unspecified] [canonicaltype=int *] [canonicaltypekind=Pointer] [modifiedtype=int *] [modifiedtypekind=Pointer] [isPOD=1]
+// CHECK: VarDecl=e:5:24 [type=int * _Nullable_result] [typekind=Attributed] [nullability=nullable_result] [canonicaltype=int *] [canonicaltypekind=Pointer] [modifiedtype=int *] [modifiedtypekind=Pointer] [isPOD=1]
diff --git a/clang/test/SemaObjC/nullability.m b/clang/test/SemaObjC/nullability.m
index 885ced692ebcc..93834942be799 100644
--- a/clang/test/SemaObjC/nullability.m
+++ b/clang/test/SemaObjC/nullability.m
@@ -116,11 +116,13 @@ - (id)returnsNone;
- (nonnull id)returnsNonNull;
- (nullable id)returnsNullable;
- (null_unspecified id)returnsNullUnspecified;
+- (_Nullable_result id)returnsNullableResult;
@end
void test_receiver_merge(NSMergeReceiver *none,
_Nonnull NSMergeReceiver *nonnull,
_Nullable NSMergeReceiver *nullable,
+ _Nullable_result NSMergeReceiver *nullable_result,
_Null_unspecified NSMergeReceiver *null_unspecified) {
int *ptr;
@@ -129,6 +131,12 @@ void test_receiver_merge(NSMergeReceiver *none,
ptr = [nullable returnsNonNull]; // expected-warning{{'id _Nullable'}}
ptr = [nullable returnsNone]; // expected-warning{{'id _Nullable'}}
+ ptr = [nullable_result returnsNullable]; // expected-warning{{'id _Nullable'}}
+ ptr = [nullable_result returnsNullUnspecified]; // expected-warning{{'id _Nullable'}}
+ ptr = [nullable_result returnsNonNull]; // expected-warning{{'id _Nullable'}}
+ ptr = [nullable_result returnsNone]; // expected-warning{{'id _Nullable'}}
+ ptr = [nullable_result returnsNullableResult]; // expected-warning{{'id _Nullable_result'}}
+
ptr = [null_unspecified returnsNullable]; // expected-warning{{'id _Nullable'}}
ptr = [null_unspecified returnsNullUnspecified]; // expected-warning{{'id _Null_unspecified'}}
ptr = [null_unspecified returnsNonNull]; // expected-warning{{'id _Null_unspecified'}}
@@ -237,6 +245,7 @@ void conditional_expr(int c) {
NSFoo * _Nonnull nonnullP;
NSFoo * _Nullable nullableP;
NSFoo * _Null_unspecified unspecifiedP;
+ NSFoo * _Nullable_result nullableResultP;
NSFoo *noneP;
p = c ? nonnullP : nonnullP;
@@ -255,6 +264,10 @@ void conditional_expr(int c) {
p = c ? noneP : nullableP; // expected-warning{{implicit conversion from nullable pointer 'NSFoo * _Nullable' to non-nullable pointer type 'NSFoo * _Nonnull'}}
p = c ? noneP : unspecifiedP;
p = c ? noneP : noneP;
+ p = c ? noneP : nullableResultP; // expected-warning{{implicit conversion from nullable pointer 'NSFoo * _Nullable' to non-nullable pointer type 'NSFoo * _Nonnull'}}
+ p = c ? nonnullP : nullableResultP; // expected-warning{{implicit conversion from nullable pointer 'NSFoo * _Nullable' to non-nullable pointer type 'NSFoo * _Nonnull'}}
+ p = c ? nullableP : nullableResultP; // expected-warning{{implicit conversion from nullable pointer 'NSFoo * _Nullable' to non-nullable pointer type 'NSFoo * _Nonnull'}}
+ p = c ? nullableResultP : nullableResultP; // expected-warning{{implicit conversion from nullable pointer 'NSFoo * _Nullable_result' to non-nullable pointer type 'NSFoo * _Nonnull'}}
}
typedef int INTS[4];
diff --git a/clang/test/SemaObjC/nullable-result.m b/clang/test/SemaObjC/nullable-result.m
new file mode 100644
index 0000000000000..9958531c2b7ae
--- /dev/null
+++ b/clang/test/SemaObjC/nullable-result.m
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -fblocks -Wnullable-to-nonnull-conversion %s
+// RUN: %clang_cc1 -xobjective-c++ -verify -fsyntax-only -fblocks -Wnullable-to-nonnull-conversion %s
+
+ at class X;
+ at class NSError;
+
+_Static_assert(__has_feature(nullability_nullable_result), "");
+
+ at interface SomeClass
+-(void)async_get:(void (^)(X *_Nullable_result rptr, NSError *err))completionHandler;
+ at end
+
+void call(SomeClass *sc) {
+ [sc async_get:^(X *_Nullable_result rptr, NSError *err) {}];
+ [sc async_get:^(X *_Nullable rptr, NSError *err) {}];
+}
+
+void test_conversion() {
+ X *_Nullable_result nr;
+ X *_Nonnull l = nr; // expected-warning{{implicit conversion from nullable pointer 'X * _Nullable_result' to non-nullable pointer type 'X * _Nonnull'}}
+
+ (void)^(X * _Nullable_result p) {
+ X *_Nonnull l = p; // expected-warning{{implicit conversion from nullable pointer 'X * _Nullable_result' to non-nullable pointer type 'X * _Nonnull'}}
+ };
+}
+
+void test_dup() {
+ id _Nullable_result _Nullable_result a; // expected-warning {{duplicate nullability specifier _Nullable_result}}
+ id _Nullable _Nullable_result b; // expected-error{{nullability specifier _Nullable_result conflicts with existing specifier '_Nullable'}}
+ id _Nullable_result _Nonnull c; // expected-error{{nullability specifier '_Nonnull' conflicts with existing specifier _Nullable_result}}
+}
+
+ at interface NoContextSensitive
+-(nullable_result id)m; // expected-error {{expected a type}}
+ at property(assign, nullable_result) id p; // expected-error{{unknown property attribute 'nullable_result'}}
+ at end
diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c
index 6e82bf9999f64..3eb63492071f4 100644
--- a/clang/tools/c-index-test/c-index-test.c
+++ b/clang/tools/c-index-test/c-index-test.c
@@ -1539,10 +1539,20 @@ static void PrintNullabilityKind(CXType T, const char *Format) {
const char *nullability = 0;
switch (N) {
- case CXTypeNullability_NonNull: nullability = "nonnull"; break;
- case CXTypeNullability_Nullable: nullability = "nullable"; break;
- case CXTypeNullability_Unspecified: nullability = "unspecified"; break;
- case CXTypeNullability_Invalid: break;
+ case CXTypeNullability_NonNull:
+ nullability = "nonnull";
+ break;
+ case CXTypeNullability_Nullable:
+ nullability = "nullable";
+ break;
+ case CXTypeNullability_NullableResult:
+ nullability = "nullable_result";
+ break;
+ case CXTypeNullability_Unspecified:
+ nullability = "unspecified";
+ break;
+ case CXTypeNullability_Invalid:
+ break;
}
if (nullability) {
diff --git a/clang/tools/libclang/CXType.cpp b/clang/tools/libclang/CXType.cpp
index 2d19e13f161a8..4b9620827002c 100644
--- a/clang/tools/libclang/CXType.cpp
+++ b/clang/tools/libclang/CXType.cpp
@@ -1315,6 +1315,8 @@ enum CXTypeNullabilityKind clang_Type_getNullability(CXType CT) {
return CXTypeNullability_NonNull;
case NullabilityKind::Nullable:
return CXTypeNullability_Nullable;
+ case NullabilityKind::NullableResult:
+ return CXTypeNullability_NullableResult;
case NullabilityKind::Unspecified:
return CXTypeNullability_Unspecified;
}
More information about the llvm-branch-commits
mailing list