summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ASTImporter.cpp7
-rw-r--r--clang/lib/AST/DeclObjC.cpp8
-rw-r--r--clang/lib/AST/DeclPrinter.cpp74
-rw-r--r--clang/lib/Basic/IdentifierTable.cpp14
-rw-r--r--clang/lib/Parse/ParseObjc.cpp177
-rw-r--r--clang/lib/Parse/Parser.cpp4
-rw-r--r--clang/lib/Sema/SemaDecl.cpp8
-rw-r--r--clang/lib/Sema/SemaDeclObjC.cpp165
-rw-r--r--clang/lib/Sema/SemaExprObjC.cpp133
-rw-r--r--clang/lib/Sema/SemaObjCProperty.cpp40
-rw-r--r--clang/lib/Sema/SemaType.cpp127
-rw-r--r--clang/lib/Sema/TreeTransform.h2
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp4
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp3
14 files changed, 637 insertions, 129 deletions
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 911f1681ce6..76e4e119150 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -3922,8 +3922,8 @@ Decl *ASTNodeImporter::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
}
// Import the type.
- TypeSourceInfo *T = Importer.Import(D->getTypeSourceInfo());
- if (!T)
+ TypeSourceInfo *TSI = Importer.Import(D->getTypeSourceInfo());
+ if (!TSI)
return nullptr;
// Create the new property.
@@ -3932,7 +3932,8 @@ Decl *ASTNodeImporter::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
Name.getAsIdentifierInfo(),
Importer.Import(D->getAtLoc()),
Importer.Import(D->getLParenLoc()),
- T,
+ Importer.Import(D->getType()),
+ TSI,
D->getPropertyImplementation());
Importer.Imported(D, ToProperty);
ToProperty->setLexicalDeclContext(LexicalDC);
diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp
index a63ba7e698a..a1600cb1101 100644
--- a/clang/lib/AST/DeclObjC.cpp
+++ b/clang/lib/AST/DeclObjC.cpp
@@ -1862,16 +1862,18 @@ ObjCPropertyDecl *ObjCPropertyDecl::Create(ASTContext &C, DeclContext *DC,
IdentifierInfo *Id,
SourceLocation AtLoc,
SourceLocation LParenLoc,
- TypeSourceInfo *T,
+ QualType T,
+ TypeSourceInfo *TSI,
PropertyControl propControl) {
- return new (C, DC) ObjCPropertyDecl(DC, L, Id, AtLoc, LParenLoc, T);
+ return new (C, DC) ObjCPropertyDecl(DC, L, Id, AtLoc, LParenLoc, T, TSI,
+ propControl);
}
ObjCPropertyDecl *ObjCPropertyDecl::CreateDeserialized(ASTContext &C,
unsigned ID) {
return new (C, ID) ObjCPropertyDecl(nullptr, SourceLocation(), nullptr,
SourceLocation(), SourceLocation(),
- nullptr);
+ QualType(), nullptr, None);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index d8cd40ec9c6..609cc08545a 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -37,6 +37,13 @@ namespace {
void Print(AccessSpecifier AS);
+ /// Print an Objective-C method type in parentheses.
+ ///
+ /// \param Quals The Objective-C declaration qualifiers.
+ /// \param T The type to print.
+ void PrintObjCMethodType(ASTContext &Ctx, Decl::ObjCDeclQualifier Quals,
+ QualType T);
+
public:
DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy,
unsigned Indentation = 0, bool PrintInstantiation = false)
@@ -930,24 +937,64 @@ void DeclPrinter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
// Objective-C declarations
//----------------------------------------------------------------------------
+/// Strip off the top-level nullability annotation, if it's there.
+static Optional<NullabilityKind> stripOuterNullability(QualType &T) {
+ if (auto attributed = dyn_cast<AttributedType>(T.getTypePtr())) {
+ if (auto nullability = attributed->getImmediateNullability()) {
+ T = attributed->getModifiedType();
+ return nullability;
+ }
+ }
+
+ return None;
+ }
+
+void DeclPrinter::PrintObjCMethodType(ASTContext &Ctx,
+ Decl::ObjCDeclQualifier Quals,
+ QualType T) {
+ Out << '(';
+ if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_In)
+ Out << "in ";
+ if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Inout)
+ Out << "inout ";
+ if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Out)
+ Out << "out ";
+ if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Bycopy)
+ Out << "bycopy ";
+ if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Byref)
+ Out << "byref ";
+ if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Oneway)
+ Out << "oneway ";
+ if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_CSNullability) {
+ if (auto nullability = stripOuterNullability(T)) {
+ Out << getNullabilitySpelling(*nullability).substr(2) << ' ';
+ }
+ }
+
+ Out << Ctx.getUnqualifiedObjCPointerType(T).getAsString(Policy);
+ Out << ')';
+}
+
void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) {
if (OMD->isInstanceMethod())
Out << "- ";
else
Out << "+ ";
- if (!OMD->getReturnType().isNull())
- Out << '(' << OMD->getASTContext()
- .getUnqualifiedObjCPointerType(OMD->getReturnType())
- .getAsString(Policy) << ")";
+ if (!OMD->getReturnType().isNull()) {
+ PrintObjCMethodType(OMD->getASTContext(), OMD->getObjCDeclQualifier(),
+ OMD->getReturnType());
+ }
std::string name = OMD->getSelector().getAsString();
std::string::size_type pos, lastPos = 0;
for (const auto *PI : OMD->params()) {
// FIXME: selector is missing here!
pos = name.find_first_of(':', lastPos);
- Out << " " << name.substr(lastPos, pos - lastPos);
- Out << ":(" << PI->getASTContext().getUnqualifiedObjCPointerType(PI->getType()).
- getAsString(Policy) << ')' << *PI;
+ Out << " " << name.substr(lastPos, pos - lastPos) << ':';
+ PrintObjCMethodType(OMD->getASTContext(),
+ PI->getObjCDeclQualifier(),
+ PI->getType());
+ Out << *PI;
lastPos = pos + 1;
}
@@ -1103,6 +1150,8 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) {
else if (PDecl->getPropertyImplementation() == ObjCPropertyDecl::Optional)
Out << "@optional\n";
+ QualType T = PDecl->getType();
+
Out << "@property";
if (PDecl->getPropertyAttributes() != ObjCPropertyDecl::OBJC_PR_noattr) {
bool first = true;
@@ -1161,10 +1210,19 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) {
first = false;
}
+ if (PDecl->getPropertyAttributes() &
+ ObjCPropertyDecl::OBJC_PR_nullability) {
+ if (auto nullability = stripOuterNullability(T)) {
+ Out << (first ? ' ' : ',')
+ << getNullabilitySpelling(*nullability).substr(2);
+ first = false;
+ }
+ }
+
(void) first; // Silence dead store warning due to idiomatic code.
Out << " )";
}
- Out << ' ' << PDecl->getASTContext().getUnqualifiedObjCPointerType(PDecl->getType()).
+ Out << ' ' << PDecl->getASTContext().getUnqualifiedObjCPointerType(T).
getAsString(Policy) << ' ' << *PDecl;
if (Policy.PolishForDeclaration)
Out << ';';
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 4e06352d31d..0349ef463bf 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -16,6 +16,7 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/SmallString.h"
@@ -645,3 +646,16 @@ const char *clang::getOperatorSpelling(OverloadedOperatorKind Operator) {
llvm_unreachable("Invalid OverloadedOperatorKind!");
}
+
+StringRef clang::getNullabilitySpelling(NullabilityKind kind) {
+ switch (kind) {
+ case NullabilityKind::NonNull:
+ return "__nonnull";
+
+ case NullabilityKind::Nullable:
+ return "__nullable";
+
+ case NullabilityKind::Unspecified:
+ return "__null_unspecified";
+ }
+}
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp
index 8baeecb9770..e0f716e82f2 100644
--- a/clang/lib/Parse/ParseObjc.cpp
+++ b/clang/lib/Parse/ParseObjc.cpp
@@ -13,6 +13,7 @@
#include "clang/Parse/Parser.h"
#include "RAIIObjectsForParser.h"
+#include "clang/AST/ASTContext.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Sema/DeclSpec.h"
@@ -307,6 +308,58 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
return ClsType;
}
+IdentifierInfo *Parser::getNullabilityKeyword(NullabilityKind nullability) {
+ switch (nullability) {
+ case NullabilityKind::NonNull:
+ if (!Ident___nonnull)
+ Ident___nonnull = PP.getIdentifierInfo("__nonnull");
+ return Ident___nonnull;
+
+ case NullabilityKind::Nullable:
+ if (!Ident___nullable)
+ Ident___nullable = PP.getIdentifierInfo("__nullable");
+ return Ident___nullable;
+
+ case NullabilityKind::Unspecified:
+ if (!Ident___null_unspecified)
+ Ident___null_unspecified = PP.getIdentifierInfo("__null_unspecified");
+ return Ident___null_unspecified;
+ }
+}
+
+/// Add an attribute for a context-sensitive type nullability to the given
+/// declarator.
+static void addContextSensitiveTypeNullability(Parser &P,
+ Declarator &D,
+ NullabilityKind nullability,
+ SourceLocation nullabilityLoc,
+ bool &addedToDeclSpec) {
+ // Create the attribute.
+ auto getNullabilityAttr = [&]() -> AttributeList * {
+ auto attr = D.getAttributePool().create(
+ P.getNullabilityKeyword(nullability),
+ SourceRange(nullabilityLoc),
+ nullptr, SourceLocation(),
+ nullptr, 0,
+ AttributeList::AS_Keyword);
+ attr->setContextSensitiveKeywordAttribute();
+ return attr;
+ };
+
+ if (D.getNumTypeObjects() > 0) {
+ // Add the attribute to the declarator chunk nearest the declarator.
+ auto nullabilityAttr = getNullabilityAttr();
+ DeclaratorChunk &chunk = D.getTypeObject(0);
+ nullabilityAttr->setNext(chunk.getAttrListRef());
+ chunk.getAttrListRef() = nullabilityAttr;
+ } else if (!addedToDeclSpec) {
+ // Otherwise, just put it on the declaration specifiers (if one
+ // isn't there already).
+ D.getMutableDeclSpec().addAttributes(getNullabilityAttr());
+ addedToDeclSpec = true;
+ }
+}
+
/// objc-interface-decl-list:
/// empty
/// objc-interface-decl-list objc-property-decl [OBJC2]
@@ -445,6 +498,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
ParseObjCPropertyAttribute(OCDS);
}
+ bool addedToDeclSpec = false;
auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) {
if (FD.D.getIdentifier() == nullptr) {
Diag(AtLoc, diag::err_objc_property_requires_field_name)
@@ -457,6 +511,13 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
return;
}
+ // Map a nullability property attribute to a context-sensitive keyword
+ // attribute.
+ if (OCDS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
+ addContextSensitiveTypeNullability(*this, FD.D, OCDS.getNullability(),
+ OCDS.getNullabilityLoc(),
+ addedToDeclSpec);
+
// Install the property declarator into interfaceDecl.
IdentifierInfo *SelName =
OCDS.getGetterName() ? OCDS.getGetterName() : FD.D.getIdentifier();
@@ -510,6 +571,24 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
Actions.ActOnAtEnd(getCurScope(), AtEnd, allMethods, allTUVariables);
}
+/// Diagnose redundant or conflicting nullability information.
+static void diagnoseRedundantPropertyNullability(Parser &P,
+ ObjCDeclSpec &DS,
+ NullabilityKind nullability,
+ SourceLocation nullabilityLoc){
+ if (DS.getNullability() == nullability) {
+ P.Diag(nullabilityLoc, diag::warn_nullability_duplicate)
+ << static_cast<unsigned>(nullability) << true
+ << SourceRange(DS.getNullabilityLoc());
+ return;
+ }
+
+ P.Diag(nullabilityLoc, diag::err_nullability_conflicting)
+ << static_cast<unsigned>(nullability) << true
+ << static_cast<unsigned>(DS.getNullability()) << true
+ << SourceRange(DS.getNullabilityLoc());
+}
+
/// Parse property attribute declarations.
///
/// property-attr-decl: '(' property-attrlist ')'
@@ -529,6 +608,9 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
/// strong
/// weak
/// unsafe_unretained
+/// nonnull
+/// nullable
+/// null_unspecified
///
void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
assert(Tok.getKind() == tok::l_paren);
@@ -614,6 +696,27 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_getter);
DS.setGetterName(SelIdent);
}
+ } else if (II->isStr("nonnull")) {
+ if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
+ diagnoseRedundantPropertyNullability(*this, DS,
+ NullabilityKind::NonNull,
+ Tok.getLocation());
+ DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
+ DS.setNullability(Tok.getLocation(), NullabilityKind::NonNull);
+ } else if (II->isStr("nullable")) {
+ if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
+ diagnoseRedundantPropertyNullability(*this, DS,
+ NullabilityKind::Nullable,
+ Tok.getLocation());
+ DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
+ DS.setNullability(Tok.getLocation(), NullabilityKind::Nullable);
+ } else if (II->isStr("null_unspecified")) {
+ if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
+ diagnoseRedundantPropertyNullability(*this, DS,
+ NullabilityKind::Unspecified,
+ Tok.getLocation());
+ DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
+ DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
} else {
Diag(AttrName, diag::err_objc_expected_property_attr) << II;
SkipUntil(tok::r_paren, StopAtSemi);
@@ -779,6 +882,17 @@ bool Parser::isTokIdentifier_in() const {
/// objc-type-qualifier
/// objc-type-qualifiers objc-type-qualifier
///
+/// objc-type-qualifier:
+/// 'in'
+/// 'out'
+/// 'inout'
+/// 'oneway'
+/// 'bycopy'
+/// 'byref'
+/// 'nonnull'
+/// 'nullable'
+/// 'null_unspecified'
+///
void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS,
Declarator::TheContext Context) {
assert(Context == Declarator::ObjCParameterContext ||
@@ -796,10 +910,13 @@ void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS,
const IdentifierInfo *II = Tok.getIdentifierInfo();
for (unsigned i = 0; i != objc_NumQuals; ++i) {
- if (II != ObjCTypeQuals[i])
+ if (II != ObjCTypeQuals[i] ||
+ NextToken().is(tok::less) ||
+ NextToken().is(tok::coloncolon))
continue;
ObjCDeclSpec::ObjCDeclQualifier Qual;
+ NullabilityKind Nullability;
switch (i) {
default: llvm_unreachable("Unknown decl qualifier");
case objc_in: Qual = ObjCDeclSpec::DQ_In; break;
@@ -808,8 +925,28 @@ void Parser::ParseObjCTypeQualifierList(ObjCDeclSpec &DS,
case objc_oneway: Qual = ObjCDeclSpec::DQ_Oneway; break;
case objc_bycopy: Qual = ObjCDeclSpec::DQ_Bycopy; break;
case objc_byref: Qual = ObjCDeclSpec::DQ_Byref; break;
+
+ case objc_nonnull:
+ Qual = ObjCDeclSpec::DQ_CSNullability;
+ Nullability = NullabilityKind::NonNull;
+ break;
+
+ case objc_nullable:
+ Qual = ObjCDeclSpec::DQ_CSNullability;
+ Nullability = NullabilityKind::Nullable;
+ break;
+
+ case objc_null_unspecified:
+ Qual = ObjCDeclSpec::DQ_CSNullability;
+ Nullability = NullabilityKind::Unspecified;
+ break;
}
+
+ // FIXME: Diagnose redundant specifiers.
DS.setObjCDeclQualifier(Qual);
+ if (Qual == ObjCDeclSpec::DQ_CSNullability)
+ DS.setNullability(Tok.getLocation(), Nullability);
+
ConsumeToken();
II = nullptr;
break;
@@ -889,6 +1026,14 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
// If that's not invalid, extract a type.
if (!declarator.isInvalidType()) {
+ // Map a nullability specifier to a context-sensitive keyword attribute.
+ bool addedToDeclSpec = false;
+ if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability)
+ addContextSensitiveTypeNullability(*this, declarator,
+ DS.getNullability(),
+ DS.getNullabilityLoc(),
+ addedToDeclSpec);
+
TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator);
if (!type.isInvalid())
Ty = type.get();
@@ -904,8 +1049,34 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS,
Ident_instancetype = PP.getIdentifierInfo("instancetype");
if (Tok.getIdentifierInfo() == Ident_instancetype) {
- Ty = Actions.ActOnObjCInstanceType(Tok.getLocation());
- ConsumeToken();
+ SourceLocation loc = ConsumeToken();
+ Ty = Actions.ActOnObjCInstanceType(loc);
+
+ // Map a nullability specifier to a context-sensitive keyword attribute.
+ if (DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability) {
+ // Synthesize an abstract declarator so we can use Sema::ActOnTypeName.
+ bool addedToDeclSpec = false;
+ const char *prevSpec;
+ unsigned diagID;
+ DeclSpec declSpec(AttrFactory);
+ declSpec.setObjCQualifiers(&DS);
+ declSpec.SetTypeSpecType(DeclSpec::TST_typename, loc, prevSpec, diagID,
+ Ty,
+ Actions.getASTContext().getPrintingPolicy());
+ declSpec.SetRangeEnd(loc);
+ Declarator declarator(declSpec, context);
+
+ // Add the context-sensitive keyword attribute.
+ addContextSensitiveTypeNullability(*this, declarator,
+ DS.getNullability(),
+ DS.getNullabilityLoc(),
+ addedToDeclSpec);
+
+
+ TypeResult type = Actions.ActOnTypeName(getCurScope(), declarator);
+ if (!type.isInvalid())
+ Ty = type.get();
+ }
}
}
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index dea7a6998a2..e32df95548d 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -463,6 +463,10 @@ void Parser::Initialize() {
ObjCTypeQuals[objc_oneway] = &PP.getIdentifierTable().get("oneway");
ObjCTypeQuals[objc_bycopy] = &PP.getIdentifierTable().get("bycopy");
ObjCTypeQuals[objc_byref] = &PP.getIdentifierTable().get("byref");
+ ObjCTypeQuals[objc_nonnull] = &PP.getIdentifierTable().get("nonnull");
+ ObjCTypeQuals[objc_nullable] = &PP.getIdentifierTable().get("nullable");
+ ObjCTypeQuals[objc_null_unspecified]
+ = &PP.getIdentifierTable().get("null_unspecified");
}
Ident_instancetype = nullptr;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index f4af3f320a8..aa006b31fc8 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2471,9 +2471,13 @@ static void mergeParamDeclTypes(ParmVarDecl *NewParam,
if (auto Oldnullability = OldParam->getType()->getNullability(S.Context)) {
if (auto Newnullability = NewParam->getType()->getNullability(S.Context)) {
if (*Oldnullability != *Newnullability) {
+ unsigned unsNewnullability = static_cast<unsigned>(*Newnullability);
+ unsigned unsOldnullability = static_cast<unsigned>(*Oldnullability);
S.Diag(NewParam->getLocation(), diag::warn_mismatched_nullability_attr)
- << static_cast<unsigned>(*Newnullability)
- << static_cast<unsigned>(*Oldnullability);
+ << unsNewnullability
+ << ((NewParam->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0)
+ << unsOldnullability
+ << ((OldParam->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0);
S.Diag(OldParam->getLocation(), diag::note_previous_declaration);
}
}
diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp
index 38318791fd7..78c76abd4c7 100644
--- a/clang/lib/Sema/SemaDeclObjC.cpp
+++ b/clang/lib/Sema/SemaDeclObjC.cpp
@@ -1366,6 +1366,13 @@ static SourceRange getTypeRange(TypeSourceInfo *TSI) {
return (TSI ? TSI->getTypeLoc().getSourceRange() : SourceRange());
}
+/// Determine whether two set of Objective-C declaration qualifiers conflict.
+static bool objcModifiersConflict(Decl::ObjCDeclQualifier x,
+ Decl::ObjCDeclQualifier y) {
+ return (x & ~Decl::OBJC_TQ_CSNullability) !=
+ (y & ~Decl::OBJC_TQ_CSNullability);
+}
+
static bool CheckMethodOverrideReturn(Sema &S,
ObjCMethodDecl *MethodImpl,
ObjCMethodDecl *MethodDecl,
@@ -1373,8 +1380,8 @@ static bool CheckMethodOverrideReturn(Sema &S,
bool IsOverridingMode,
bool Warn) {
if (IsProtocolMethodDecl &&
- (MethodDecl->getObjCDeclQualifier() !=
- MethodImpl->getObjCDeclQualifier())) {
+ objcModifiersConflict(MethodDecl->getObjCDeclQualifier(),
+ MethodImpl->getObjCDeclQualifier())) {
if (Warn) {
S.Diag(MethodImpl->getLocation(),
(IsOverridingMode
@@ -1388,7 +1395,24 @@ static bool CheckMethodOverrideReturn(Sema &S,
else
return false;
}
-
+ if (Warn && IsOverridingMode &&
+ !isa<ObjCImplementationDecl>(MethodImpl->getDeclContext()) &&
+ !S.Context.hasSameNullabilityTypeQualifier(MethodImpl->getReturnType(),
+ MethodDecl->getReturnType(),
+ false)) {
+ unsigned unsNullabilityMethodImpl =
+ static_cast<unsigned>(*MethodImpl->getReturnType()->getNullability(S.Context));
+ unsigned unsNullabilityMethodDecl =
+ static_cast<unsigned>(*MethodDecl->getReturnType()->getNullability(S.Context));
+ S.Diag(MethodImpl->getLocation(),
+ diag::warn_conflicting_nullability_attr_overriding_ret_types)
+ << unsNullabilityMethodImpl
+ << ((MethodImpl->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0)
+ << unsNullabilityMethodDecl
+ << ((MethodDecl->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0);
+ S.Diag(MethodDecl->getLocation(), diag::note_previous_declaration);
+ }
+
if (S.Context.hasSameUnqualifiedType(MethodImpl->getReturnType(),
MethodDecl->getReturnType()))
return true;
@@ -1438,8 +1462,8 @@ static bool CheckMethodOverrideParam(Sema &S,
bool IsOverridingMode,
bool Warn) {
if (IsProtocolMethodDecl &&
- (ImplVar->getObjCDeclQualifier() !=
- IfaceVar->getObjCDeclQualifier())) {
+ objcModifiersConflict(ImplVar->getObjCDeclQualifier(),
+ IfaceVar->getObjCDeclQualifier())) {
if (Warn) {
if (IsOverridingMode)
S.Diag(ImplVar->getLocation(),
@@ -1459,7 +1483,19 @@ static bool CheckMethodOverrideParam(Sema &S,
QualType ImplTy = ImplVar->getType();
QualType IfaceTy = IfaceVar->getType();
-
+ if (Warn && IsOverridingMode &&
+ !isa<ObjCImplementationDecl>(MethodImpl->getDeclContext()) &&
+ !S.Context.hasSameNullabilityTypeQualifier(ImplTy, IfaceTy, true)) {
+ unsigned unsImplTy = static_cast<unsigned>(*ImplTy->getNullability(S.Context));
+ unsigned unsIfaceTy = static_cast<unsigned>(*IfaceTy->getNullability(S.Context));
+ S.Diag(ImplVar->getLocation(),
+ diag::warn_conflicting_nullability_attr_overriding_param_types)
+ << unsImplTy
+ << ((ImplVar->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0)
+ << unsIfaceTy
+ << ((IfaceVar->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0);
+ S.Diag(IfaceVar->getLocation(), diag::note_previous_declaration);
+ }
if (S.Context.hasSameUnqualifiedType(ImplTy, IfaceTy))
return true;
@@ -3121,6 +3157,89 @@ void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
ObjCMethod->setOverriding(hasOverriddenMethodsInBaseOrProtocol);
}
+/// Merge type nullability from for a redeclaration of the same entity,
+/// producing the updated type of the redeclared entity.
+static QualType mergeTypeNullabilityForRedecl(Sema &S, SourceLocation loc,
+ QualType type,
+ bool usesCSKeyword,
+ SourceLocation prevLoc,
+ QualType prevType,
+ bool prevUsesCSKeyword) {
+ // Determine the nullability of both types.
+ auto nullability = type->getNullability(S.Context);
+ auto prevNullability = prevType->getNullability(S.Context);
+
+ // Easy case: both have nullability.
+ if (nullability.hasValue() == prevNullability.hasValue()) {
+ // Neither has nullability; continue.
+ if (!nullability)
+ return type;
+
+ // The nullabilities are equivalent; do nothing.
+ if (*nullability == *prevNullability)
+ return type;
+
+ // Complain about mismatched nullability.
+ S.Diag(loc, diag::err_nullability_conflicting)
+ << static_cast<unsigned>(*nullability) << usesCSKeyword
+ << static_cast<unsigned>(*prevNullability) << prevUsesCSKeyword;
+ return type;
+ }
+
+ // If it's the redeclaration that has nullability, don't change anything.
+ if (nullability)
+ return type;
+
+ // Otherwise, provide the result with the same nullability.
+ return S.Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(*prevNullability),
+ type, type);
+}
+
+/// Merge information from the declaration of a method in the @interface
+/// (or a category/extension) into the corresponding method in the
+/// @implementation (for a class or category).
+static void mergeInterfaceMethodToImpl(Sema &S,
+ ObjCMethodDecl *method,
+ ObjCMethodDecl *prevMethod) {
+ // Merge the objc_requires_super attribute.
+ if (prevMethod->hasAttr<ObjCRequiresSuperAttr>() &&
+ !method->hasAttr<ObjCRequiresSuperAttr>()) {
+ // merge the attribute into implementation.
+ method->addAttr(
+ ObjCRequiresSuperAttr::CreateImplicit(S.Context,
+ method->getLocation()));
+ }
+
+ // Merge nullability of the result type.
+ QualType newReturnType
+ = mergeTypeNullabilityForRedecl(
+ S, method->getReturnTypeSourceRange().getBegin(),
+ method->getReturnType(),
+ method->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability,
+ prevMethod->getReturnTypeSourceRange().getBegin(),
+ prevMethod->getReturnType(),
+ prevMethod->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability);
+ method->setReturnType(newReturnType);
+
+ // Handle each of the parameters.
+ unsigned numParams = method->param_size();
+ unsigned numPrevParams = prevMethod->param_size();
+ for (unsigned i = 0, n = std::min(numParams, numPrevParams); i != n; ++i) {
+ ParmVarDecl *param = method->param_begin()[i];
+ ParmVarDecl *prevParam = prevMethod->param_begin()[i];
+
+ // Merge nullability.
+ QualType newParamType
+ = mergeTypeNullabilityForRedecl(
+ S, param->getLocation(), param->getType(),
+ param->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability,
+ prevParam->getLocation(), prevParam->getType(),
+ prevParam->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability);
+ param->setType(newParamType);
+ }
+}
+
Decl *Sema::ActOnMethodDeclaration(
Scope *S,
SourceLocation MethodLoc, SourceLocation EndLoc,
@@ -3151,7 +3270,9 @@ Decl *Sema::ActOnMethodDeclaration(
if (CheckFunctionReturnType(resultDeclType, MethodLoc))
return nullptr;
- HasRelatedResultType = (resultDeclType == Context.getObjCInstanceType());
+ QualType bareResultType = resultDeclType;
+ (void)AttributedType::stripOuterNullability(bareResultType);
+ HasRelatedResultType = (bareResultType == Context.getObjCInstanceType());
} else { // get the type for "id".
resultDeclType = Context.getObjCIdType();
Diag(MethodLoc, diag::warn_missing_method_return_type)
@@ -3252,22 +3373,20 @@ Decl *Sema::ActOnMethodDeclaration(
ImpDecl->addClassMethod(ObjCMethod);
}
- ObjCMethodDecl *IMD = nullptr;
- if (ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface())
- IMD = IDecl->lookupMethod(ObjCMethod->getSelector(),
- ObjCMethod->isInstanceMethod());
- if (IMD && IMD->hasAttr<ObjCRequiresSuperAttr>() &&
- !ObjCMethod->hasAttr<ObjCRequiresSuperAttr>()) {
- // merge the attribute into implementation.
- ObjCMethod->addAttr(ObjCRequiresSuperAttr::CreateImplicit(Context,
- ObjCMethod->getLocation()));
- }
- if (isa<ObjCCategoryImplDecl>(ImpDecl)) {
- ObjCMethodFamily family =
- ObjCMethod->getSelector().getMethodFamily();
- if (family == OMF_dealloc && IMD && IMD->isOverriding())
- Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category)
- << ObjCMethod->getDeclName();
+ // Merge information from the @interface declaration into the
+ // @implementation.
+ if (ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface()) {
+ if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(),
+ ObjCMethod->isInstanceMethod())) {
+ mergeInterfaceMethodToImpl(*this, ObjCMethod, IMD);
+
+ // Warn about defining -dealloc in a category.
+ if (isa<ObjCCategoryImplDecl>(ImpDecl) && IMD->isOverriding() &&
+ ObjCMethod->getSelector().getMethodFamily() == OMF_dealloc) {
+ Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category)
+ << ObjCMethod->getDeclName();
+ }
+ }
}
} else {
cast<DeclContext>(ClassDecl)->addDecl(ObjCMethod);
diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp
index 63b7485c876..72fc47f126c 100644
--- a/clang/lib/Sema/SemaExprObjC.cpp
+++ b/clang/lib/Sema/SemaExprObjC.cpp
@@ -1135,49 +1135,150 @@ ObjCMethodDecl *Sema::tryCaptureObjCSelf(SourceLocation Loc) {
}
static QualType stripObjCInstanceType(ASTContext &Context, QualType T) {
+ QualType origType = T;
+ if (auto nullability = AttributedType::stripOuterNullability(T)) {
+ if (T == Context.getObjCInstanceType()) {
+ return Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(*nullability),
+ Context.getObjCIdType(),
+ Context.getObjCIdType());
+ }
+
+ return origType;
+ }
+
if (T == Context.getObjCInstanceType())
return Context.getObjCIdType();
- return T;
+ return origType;
}
-QualType Sema::getMessageSendResultType(QualType ReceiverType,
- ObjCMethodDecl *Method,
- bool isClassMessage, bool isSuperMessage) {
+/// Determine the result type of a message send based on the receiver type,
+/// method, and the kind of message send.
+///
+/// This is the "base" result type, which will still need to be adjusted
+/// to account for nullability.
+static QualType getBaseMessageSendResultType(Sema &S,
+ QualType ReceiverType,
+ ObjCMethodDecl *Method,
+ bool isClassMessage,
+ bool isSuperMessage) {
assert(Method && "Must have a method");
if (!Method->hasRelatedResultType())
return Method->getSendResultType();
-
+
+ ASTContext &Context = S.Context;
+
+ // Local function that transfers the nullability of the method's
+ // result type to the returned result.
+ auto transferNullability = [&](QualType type) -> QualType {
+ // If the method's result type has nullability, extract it.
+ if (auto nullability = Method->getSendResultType()->getNullability(Context)){
+ // Strip off any outer nullability sugar from the provided type.
+ (void)AttributedType::stripOuterNullability(type);
+
+ // Form a new attributed type using the method result type's nullability.
+ return Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(*nullability),
+ type,
+ type);
+ }
+
+ return type;
+ };
+
// If a method has a related return type:
// - if the method found is an instance method, but the message send
// was a class message send, T is the declared return type of the method
// found
if (Method->isInstanceMethod() && isClassMessage)
return stripObjCInstanceType(Context, Method->getSendResultType());
-
- // - if the receiver is super, T is a pointer to the class of the
+
+ // - if the receiver is super, T is a pointer to the class of the
// enclosing method definition
if (isSuperMessage) {
- if (ObjCMethodDecl *CurMethod = getCurMethodDecl())
- if (ObjCInterfaceDecl *Class = CurMethod->getClassInterface())
- return Context.getObjCObjectPointerType(
- Context.getObjCInterfaceType(Class));
+ if (ObjCMethodDecl *CurMethod = S.getCurMethodDecl())
+ if (ObjCInterfaceDecl *Class = CurMethod->getClassInterface()) {
+ return transferNullability(
+ Context.getObjCObjectPointerType(
+ Context.getObjCInterfaceType(Class)));
+ }
}
-
+
// - if the receiver is the name of a class U, T is a pointer to U
if (ReceiverType->getAs<ObjCInterfaceType>() ||
ReceiverType->isObjCQualifiedInterfaceType())
- return Context.getObjCObjectPointerType(ReceiverType);
- // - if the receiver is of type Class or qualified Class type,
+ return transferNullability(Context.getObjCObjectPointerType(ReceiverType));
+ // - if the receiver is of type Class or qualified Class type,
// T is the declared return type of the method.
if (ReceiverType->isObjCClassType() ||
ReceiverType->isObjCQualifiedClassType())
return stripObjCInstanceType(Context, Method->getSendResultType());
-
+
// - if the receiver is id, qualified id, Class, or qualified Class, T
// is the receiver type, otherwise
// - T is the type of the receiver expression.
- return ReceiverType;
+ return transferNullability(ReceiverType);
+}
+
+QualType Sema::getMessageSendResultType(QualType ReceiverType,
+ ObjCMethodDecl *Method,
+ bool isClassMessage,
+ bool isSuperMessage) {
+ // Produce the result type.
+ QualType resultType = getBaseMessageSendResultType(*this, ReceiverType,
+ Method,
+ isClassMessage,
+ isSuperMessage);
+
+ // Map the nullability of the result into a table index.
+ unsigned receiverNullabilityIdx = 0;
+ if (auto nullability = ReceiverType->getNullability(Context))
+ receiverNullabilityIdx = 1 + static_cast<unsigned>(*nullability);
+
+ unsigned resultNullabilityIdx = 0;
+ if (auto nullability = resultType->getNullability(Context))
+ resultNullabilityIdx = 1 + static_cast<unsigned>(*nullability);
+
+ // The table of nullability mappings, indexed by the receiver's nullability
+ // and then the result type's nullability.
+ static const uint8_t None = 0;
+ static const uint8_t NonNull = 1;
+ static const uint8_t Nullable = 2;
+ static const uint8_t Unspecified = 3;
+ static const uint8_t nullabilityMap[4][4] = {
+ // None NonNull Nullable Unspecified
+ /* None */ { None, None, Nullable, None },
+ /* NonNull */ { None, NonNull, Nullable, Unspecified },
+ /* Nullable */ { Nullable, Nullable, Nullable, Nullable },
+ /* Unspecified */ { None, Unspecified, Nullable, Unspecified }
+ };
+
+ unsigned newResultNullabilityIdx
+ = nullabilityMap[receiverNullabilityIdx][resultNullabilityIdx];
+ if (newResultNullabilityIdx == resultNullabilityIdx)
+ return resultType;
+
+ // Strip off the existing nullability. This removes as little type sugar as
+ // possible.
+ do {
+ if (auto attributed = dyn_cast<AttributedType>(resultType.getTypePtr())) {
+ resultType = attributed->getModifiedType();
+ } else {
+ resultType = resultType.getDesugaredType(Context);
+ }
+ } while (resultType->getNullability(Context));
+
+ // Add nullability back if needed.
+ if (newResultNullabilityIdx > 0) {
+ auto newNullability
+ = static_cast<NullabilityKind>(newResultNullabilityIdx-1);
+ return Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(newNullability),
+ resultType, resultType);
+ }
+
+ return resultType;
}
/// Look for an ObjC method whose result type exactly matches the given type.
diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp
index 5e7b4b8b859..f62af8eb81d 100644
--- a/clang/lib/Sema/SemaObjCProperty.cpp
+++ b/clang/lib/Sema/SemaObjCProperty.cpp
@@ -149,7 +149,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S);
QualType T = TSI->getType();
Attributes |= deduceWeakPropertyFromType(*this, T);
-
bool isReadWrite = ((Attributes & ObjCDeclSpec::DQ_PR_readwrite) ||
// default is readwrite!
!(Attributes & ObjCDeclSpec::DQ_PR_readonly));
@@ -173,7 +172,7 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
isAssign, isReadWrite,
Attributes,
ODS.getPropertyAttributes(),
- isOverridingProperty, TSI,
+ isOverridingProperty, T, TSI,
MethodImplKind);
if (!Res)
return nullptr;
@@ -184,7 +183,7 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
Res = CreatePropertyDecl(S, ClassDecl, AtLoc, LParenLoc, FD,
GetterSel, SetterSel, isAssign, isReadWrite,
Attributes, ODS.getPropertyAttributes(),
- TSI, MethodImplKind);
+ T, TSI, MethodImplKind);
if (lexicalDC)
Res->setLexicalDeclContext(lexicalDC);
}
@@ -322,7 +321,8 @@ Sema::HandlePropertyInClassExtension(Scope *S,
const unsigned Attributes,
const unsigned AttributesAsWritten,
bool *isOverridingProperty,
- TypeSourceInfo *T,
+ QualType T,
+ TypeSourceInfo *TSI,
tok::ObjCKeywordKind MethodImplKind) {
ObjCCategoryDecl *CDecl = cast<ObjCCategoryDecl>(CurContext);
// Diagnose if this property is already in continuation class.
@@ -348,7 +348,7 @@ Sema::HandlePropertyInClassExtension(Scope *S,
// FIXME. We should really be using CreatePropertyDecl for this.
ObjCPropertyDecl *PDecl =
ObjCPropertyDecl::Create(Context, DC, FD.D.getIdentifierLoc(),
- PropertyId, AtLoc, LParenLoc, T);
+ PropertyId, AtLoc, LParenLoc, T, TSI);
PDecl->setPropertyAttributesAsWritten(
makePropertyAttributesAsWritten(AttributesAsWritten));
if (Attributes & ObjCDeclSpec::DQ_PR_readonly)
@@ -359,6 +359,8 @@ Sema::HandlePropertyInClassExtension(Scope *S,
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nonatomic);
if (Attributes & ObjCDeclSpec::DQ_PR_atomic)
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_atomic);
+ if (Attributes & ObjCDeclSpec::DQ_PR_nullability)
+ PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nullability);
// Set setter/getter selector name. Needed later.
PDecl->setGetterName(GetterSel);
PDecl->setSetterName(SetterSel);
@@ -383,7 +385,8 @@ Sema::HandlePropertyInClassExtension(Scope *S,
ObjCPropertyDecl *PrimaryPDecl =
CreatePropertyDecl(S, CCPrimary, AtLoc, LParenLoc,
FD, GetterSel, SetterSel, isAssign, isReadWrite,
- Attributes,AttributesAsWritten, T, MethodImplKind, DC);
+ Attributes,AttributesAsWritten, T, TSI, MethodImplKind,
+ DC);
// A case of continuation class adding a new property in the class. This
// is not what it was meant for. However, gcc supports it and so should we.
@@ -531,11 +534,11 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
const bool isReadWrite,
const unsigned Attributes,
const unsigned AttributesAsWritten,
+ QualType T,
TypeSourceInfo *TInfo,
tok::ObjCKeywordKind MethodImplKind,
DeclContext *lexicalDC){
IdentifierInfo *PropertyId = FD.D.getIdentifier();
- QualType T = TInfo->getType();
// Issue a warning if property is 'assign' as default and its object, which is
// gc'able conforms to NSCopying protocol
@@ -564,7 +567,8 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
DeclContext *DC = cast<DeclContext>(CDecl);
ObjCPropertyDecl *PDecl = ObjCPropertyDecl::Create(Context, DC,
FD.D.getIdentifierLoc(),
- PropertyId, AtLoc, LParenLoc, TInfo);
+ PropertyId, AtLoc,
+ LParenLoc, T, TInfo);
if (ObjCPropertyDecl *prevDecl =
ObjCPropertyDecl::findPropertyDecl(DC, PropertyId)) {
@@ -639,6 +643,9 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
else if (MethodImplKind == tok::objc_optional)
PDecl->setPropertyImplementation(ObjCPropertyDecl::Optional);
+ if (Attributes & ObjCDeclSpec::DQ_PR_nullability)
+ PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nullability);
+
return PDecl;
}
@@ -2228,6 +2235,23 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl,
Attributes &= ~ObjCDeclSpec::DQ_PR_weak;
}
+ if ((Attributes & ObjCDeclSpec::DQ_PR_weak) &&
+ !(Attributes & ObjCDeclSpec::DQ_PR_readonly)) {
+ // 'weak' and 'nonnull' are mutually exclusive.
+ if (auto nullability = PropertyTy->getNullability(Context)) {
+ if (*nullability == NullabilityKind::NonNull)
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "nonnull" << "weak";
+ } else {
+ PropertyTy =
+ Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(NullabilityKind::Nullable),
+ PropertyTy, PropertyTy);
+ TypeSourceInfo *TSInfo = PropertyDecl->getTypeSourceInfo();
+ PropertyDecl->setType(PropertyTy, TSInfo);
+ }
+ }
+
if ((Attributes & ObjCDeclSpec::DQ_PR_atomic) &&
(Attributes & ObjCDeclSpec::DQ_PR_nonatomic)) {
Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index ea749a46e09..750172c4cd9 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -4522,34 +4522,10 @@ static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State,
return false;
}
-/// Map a nullability attribute kind to a nullability kind.
-static NullabilityKind mapNullabilityAttrKind(AttributeList::Kind kind) {
- switch (kind) {
- case AttributeList::AT_TypeNonNull:
- return NullabilityKind::NonNull;
-
- case AttributeList::AT_TypeNullable:
- return NullabilityKind::Nullable;
-
- case AttributeList::AT_TypeNullUnspecified:
- return NullabilityKind::Unspecified;
-
- default:
- llvm_unreachable("not a nullability attribute kind");
- }
-}
-
-/// Handle a nullability type attribute.
-static bool handleNullabilityTypeAttr(TypeProcessingState &state,
- AttributeList &attr,
- QualType &type) {
- Sema &S = state.getSema();
- ASTContext &Context = S.Context;
-
- // Determine the nullability.
- AttributeList::Kind kind = attr.getKind();
- NullabilityKind nullability = mapNullabilityAttrKind(kind);
-
+bool Sema::checkNullabilityTypeSpecifier(QualType &type,
+ NullabilityKind nullability,
+ SourceLocation nullabilityLoc,
+ bool isContextSensitive) {
// Check for existing nullability attributes on the type.
QualType desugared = type;
while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {
@@ -4557,19 +4533,24 @@ static bool handleNullabilityTypeAttr(TypeProcessingState &state,
if (auto existingNullability = attributed->getImmediateNullability()) {
// Duplicated nullability.
if (nullability == *existingNullability) {
- S.Diag(attr.getLoc(), diag::warn_duplicate_nullability)
- << static_cast<unsigned>(nullability);
- return true;
- }
+ Diag(nullabilityLoc, diag::warn_nullability_duplicate)
+ << static_cast<unsigned>(nullability)
+ << isContextSensitive
+ << FixItHint::CreateRemoval(nullabilityLoc);
+
+ break;
+ }
// Conflicting nullability.
- S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
- << static_cast<unsigned>(nullability)
- << static_cast<unsigned>(*existingNullability);
+ Diag(nullabilityLoc, diag::err_nullability_conflicting)
+ << static_cast<unsigned>(nullability)
+ << isContextSensitive
+ << static_cast<unsigned>(*existingNullability)
+ << false;
return true;
}
- desugared = attributed->getEquivalentType();
+ desugared = attributed->getModifiedType();
}
// If there is already a different nullability specifier, complain.
@@ -4578,8 +4559,9 @@ static bool handleNullabilityTypeAttr(TypeProcessingState &state,
// provide a useful Fix-It.
if (auto existingNullability = desugared->getNullability(Context)) {
if (nullability != *existingNullability) {
- S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
+ Diag(nullabilityLoc, diag::err_nullability_conflicting)
<< static_cast<unsigned>(nullability)
+ << isContextSensitive
<< static_cast<unsigned>(*existingNullability);
// Try to find the typedef with the existing nullability specifier.
@@ -4589,7 +4571,7 @@ static bool handleNullabilityTypeAttr(TypeProcessingState &state,
if (auto typedefNullability
= AttributedType::stripOuterNullability(underlyingType)) {
if (*typedefNullability == *existingNullability) {
- S.Diag(typedefDecl->getLocation(), diag::note_nullability_here)
+ Diag(typedefDecl->getLocation(), diag::note_nullability_here)
<< static_cast<unsigned>(*existingNullability);
}
}
@@ -4600,31 +4582,35 @@ static bool handleNullabilityTypeAttr(TypeProcessingState &state,
}
// If this definitely isn't a pointer type, reject the specifier.
- if (!type->canHaveNullability()) {
- S.Diag(attr.getLoc(), diag::err_nullability_nonpointer)
- << static_cast<unsigned>(nullability) << type;
+ if (!desugared->canHaveNullability()) {
+ Diag(nullabilityLoc, diag::err_nullability_nonpointer)
+ << static_cast<unsigned>(nullability) << isContextSensitive << type;
return true;
}
+
+ // For the context-sensitive keywords/Objective-C property
+ // attributes, require that the type be a single-level pointer.
+ if (isContextSensitive) {
+ // Make sure that the pointee isn't itself a pointer type.
+ QualType pointeeType = desugared->getPointeeType();
+ if (pointeeType->isAnyPointerType() ||
+ pointeeType->isObjCObjectPointerType() ||
+ pointeeType->isMemberPointerType()) {
+ Diag(nullabilityLoc, diag::err_nullability_cs_multilevel)
+ << static_cast<unsigned>(nullability)
+ << type;
+ Diag(nullabilityLoc, diag::note_nullability_type_specifier)
+ << static_cast<unsigned>(nullability)
+ << type
+ << FixItHint::CreateReplacement(nullabilityLoc,
+ getNullabilitySpelling(nullability));
+ return true;
+ }
+ }
// Form the attributed type.
- AttributedType::Kind typeAttrKind;
- switch (kind) {
- case AttributeList::AT_TypeNonNull:
- typeAttrKind = AttributedType::attr_nonnull;
- break;
-
- case AttributeList::AT_TypeNullable:
- typeAttrKind = AttributedType::attr_nullable;
- break;
-
- case AttributeList::AT_TypeNullUnspecified:
- typeAttrKind = AttributedType::attr_null_unspecified;
- break;
-
- default:
- llvm_unreachable("Not a nullability specifier");
- }
- type = S.Context.getAttributedType(typeAttrKind, type, type);
+ type = Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(nullability), type, type);
return false;
}
@@ -4642,6 +4628,23 @@ static bool hasNullabilityAttr(const AttributeList *attrs) {
return false;
}
+/// Map a nullability attribute kind to a nullability kind.
+static NullabilityKind mapNullabilityAttrKind(AttributeList::Kind kind) {
+ switch (kind) {
+ case AttributeList::AT_TypeNonNull:
+ return NullabilityKind::NonNull;
+
+ case AttributeList::AT_TypeNullable:
+ return NullabilityKind::Nullable;
+
+ case AttributeList::AT_TypeNullUnspecified:
+ return NullabilityKind::Unspecified;
+
+ default:
+ llvm_unreachable("not a nullability attribute kind");
+ }
+}
+
/// Distribute a nullability type attribute that cannot be applied to
/// the type specifier to a pointer, block pointer, or member pointer
/// declarator, complaining if necessary.
@@ -5233,7 +5236,11 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
// dependent type, because that complicates the user model.
if (type->canHaveNullability() || type->isDependentType() ||
!distributeNullabilityTypeAttr(state, type, attr)) {
- if (handleNullabilityTypeAttr(state, attr, type)) {
+ if (state.getSema().checkNullabilityTypeSpecifier(
+ type,
+ mapNullabilityAttrKind(attr.getKind()),
+ attr.getLoc(),
+ attr.isContextSensitiveKeywordAttribute())) {
attr.setInvalid();
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index c4cd22609b7..73dde7c97fc 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5392,7 +5392,7 @@ QualType TreeTransform<Derived>::TransformAttributedType(
if (auto nullability = oldType->getImmediateNullability()) {
if (!modifiedType->canHaveNullability()) {
SemaRef.Diag(TL.getAttrNameLoc(), diag::err_nullability_nonpointer)
- << static_cast<unsigned>(*nullability) << modifiedType;
+ << static_cast<unsigned>(*nullability) << false << modifiedType;
return QualType();
}
}
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index a3cbce0e3cd..7cff979fd6b 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1042,7 +1042,9 @@ void ASTDeclReader::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
VisitNamedDecl(D);
D->setAtLoc(ReadSourceLocation(Record, Idx));
D->setLParenLoc(ReadSourceLocation(Record, Idx));
- D->setType(GetTypeSourceInfo(Record, Idx));
+ QualType T = Reader.readType(F, Record, Idx);
+ TypeSourceInfo *TSI = GetTypeSourceInfo(Record, Idx);
+ D->setType(T, TSI);
// FIXME: stable encoding
D->setPropertyAttributes(
(ObjCPropertyDecl::PropertyAttributeKind)Record[Idx++]);
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index f69367fb6f3..6c5bc5bbd48 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -542,7 +542,7 @@ void ASTDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) {
// FIXME: stable encoding for @required/@optional
Record.push_back(D->getImplementationControl());
- // FIXME: stable encoding for in/out/inout/bycopy/byref/oneway
+ // FIXME: stable encoding for in/out/inout/bycopy/byref/oneway/nullability
Record.push_back(D->getObjCDeclQualifier());
Record.push_back(D->hasRelatedResultType());
Writer.AddTypeRef(D->getReturnType(), Record);
@@ -678,6 +678,7 @@ void ASTDeclWriter::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
VisitNamedDecl(D);
Writer.AddSourceLocation(D->getAtLoc(), Record);
Writer.AddSourceLocation(D->getLParenLoc(), Record);
+ Writer.AddTypeRef(D->getType(), Record);
Writer.AddTypeSourceInfo(D->getTypeSourceInfo(), Record);
// FIXME: stable encoding
Record.push_back((unsigned)D->getPropertyAttributes());
OpenPOWER on IntegriCloud