summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/Type.cpp151
-rw-r--r--clang/lib/AST/TypePrinter.cpp37
-rw-r--r--clang/lib/Lex/PPMacroExpansion.cpp2
-rw-r--r--clang/lib/Parse/ParseDecl.cpp45
-rw-r--r--clang/lib/Parse/ParseTentative.cpp7
-rw-r--r--clang/lib/Sema/SemaDecl.cpp31
-rw-r--r--clang/lib/Sema/SemaType.cpp264
-rw-r--r--clang/lib/Sema/TreeTransform.h11
8 files changed, 533 insertions, 15 deletions
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 09bb7692596..15f3d39634d 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -1919,7 +1919,10 @@ bool AttributedType::isCallingConv() const {
case attr_objc_gc:
case attr_objc_ownership:
case attr_noreturn:
- return false;
+ case attr_nonnull:
+ case attr_nullable:
+ case attr_null_unspecified:
+ return false;
case attr_pcs:
case attr_pcs_vfp:
case attr_cdecl:
@@ -2340,6 +2343,152 @@ LinkageInfo Type::getLinkageAndVisibility() const {
return LV;
}
+Optional<NullabilityKind> Type::getNullability(const ASTContext &context) const {
+ QualType type(this, 0);
+ do {
+ // Check whether this is an attributed type with nullability
+ // information.
+ if (auto attributed = dyn_cast<AttributedType>(type.getTypePtr())) {
+ if (auto nullability = attributed->getImmediateNullability())
+ return nullability;
+ }
+
+ // Desugar the type. If desugaring does nothing, we're done.
+ QualType desugared = type.getSingleStepDesugaredType(context);
+ if (desugared.getTypePtr() == type.getTypePtr())
+ return None;
+
+ type = desugared;
+ } while (true);
+}
+
+bool Type::canHaveNullability() const {
+ QualType type = getCanonicalTypeInternal();
+
+ switch (type->getTypeClass()) {
+ // We'll only see canonical types here.
+#define NON_CANONICAL_TYPE(Class, Parent) \
+ case Type::Class: \
+ llvm_unreachable("non-canonical type");
+#define TYPE(Class, Parent)
+#include "clang/AST/TypeNodes.def"
+
+ // Pointer types.
+ case Type::Pointer:
+ case Type::BlockPointer:
+ case Type::MemberPointer:
+ case Type::ObjCObjectPointer:
+ return true;
+
+ // Dependent types that could instantiate to pointer types.
+ case Type::UnresolvedUsing:
+ case Type::TypeOfExpr:
+ case Type::TypeOf:
+ case Type::Decltype:
+ case Type::UnaryTransform:
+ case Type::TemplateTypeParm:
+ case Type::SubstTemplateTypeParmPack:
+ case Type::DependentName:
+ case Type::DependentTemplateSpecialization:
+ return true;
+
+ // Dependent template specializations can instantiate to pointer
+ // types unless they're known to be specializations of a class
+ // template.
+ case Type::TemplateSpecialization:
+ if (TemplateDecl *templateDecl
+ = cast<TemplateSpecializationType>(type.getTypePtr())
+ ->getTemplateName().getAsTemplateDecl()) {
+ if (isa<ClassTemplateDecl>(templateDecl))
+ return false;
+ }
+ return true;
+
+ // auto is considered dependent when it isn't deduced.
+ case Type::Auto:
+ return !cast<AutoType>(type.getTypePtr())->isDeduced();
+
+ case Type::Builtin:
+ switch (cast<BuiltinType>(type.getTypePtr())->getKind()) {
+ // Signed, unsigned, and floating-point types cannot have nullability.
+#define SIGNED_TYPE(Id, SingletonId) case BuiltinType::Id:
+#define UNSIGNED_TYPE(Id, SingletonId) case BuiltinType::Id:
+#define FLOATING_TYPE(Id, SingletonId) case BuiltinType::Id:
+#define BUILTIN_TYPE(Id, SingletonId)
+#include "clang/AST/BuiltinTypes.def"
+ return false;
+
+ // Dependent types that could instantiate to a pointer type.
+ case BuiltinType::Dependent:
+ case BuiltinType::Overload:
+ case BuiltinType::BoundMember:
+ case BuiltinType::PseudoObject:
+ case BuiltinType::UnknownAny:
+ case BuiltinType::ARCUnbridgedCast:
+ return true;
+
+ case BuiltinType::Void:
+ case BuiltinType::ObjCId:
+ case BuiltinType::ObjCClass:
+ case BuiltinType::ObjCSel:
+ case BuiltinType::OCLImage1d:
+ case BuiltinType::OCLImage1dArray:
+ case BuiltinType::OCLImage1dBuffer:
+ case BuiltinType::OCLImage2d:
+ case BuiltinType::OCLImage2dArray:
+ case BuiltinType::OCLImage3d:
+ case BuiltinType::OCLSampler:
+ case BuiltinType::OCLEvent:
+ case BuiltinType::BuiltinFn:
+ case BuiltinType::NullPtr:
+ return false;
+ }
+
+ // Non-pointer types.
+ case Type::Complex:
+ case Type::LValueReference:
+ case Type::RValueReference:
+ case Type::ConstantArray:
+ case Type::IncompleteArray:
+ case Type::VariableArray:
+ case Type::DependentSizedArray:
+ case Type::DependentSizedExtVector:
+ case Type::Vector:
+ case Type::ExtVector:
+ case Type::FunctionProto:
+ case Type::FunctionNoProto:
+ case Type::Record:
+ case Type::Enum:
+ case Type::InjectedClassName:
+ case Type::PackExpansion:
+ case Type::ObjCObject:
+ case Type::ObjCInterface:
+ case Type::Atomic:
+ return false;
+ }
+}
+
+llvm::Optional<NullabilityKind> AttributedType::getImmediateNullability() const {
+ if (getAttrKind() == AttributedType::attr_nonnull)
+ return NullabilityKind::NonNull;
+ if (getAttrKind() == AttributedType::attr_nullable)
+ return NullabilityKind::Nullable;
+ if (getAttrKind() == AttributedType::attr_null_unspecified)
+ return NullabilityKind::Unspecified;
+ return None;
+}
+
+Optional<NullabilityKind> AttributedType::stripOuterNullability(QualType &T) {
+ if (auto attributed = dyn_cast<AttributedType>(T.getTypePtr())) {
+ if (auto nullability = attributed->getImmediateNullability()) {
+ T = attributed->getModifiedType();
+ return nullability;
+ }
+ }
+
+ return None;
+}
+
Qualifiers::ObjCLifetime Type::getObjCARCImplicitLifetime() const {
if (isObjCARCImplicitlyUnretainedType())
return Qualifiers::OCL_ExplicitNone;
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 3928fe8f894..ebe09d85495 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1141,6 +1141,21 @@ void TypePrinter::printAttributedBefore(const AttributedType *T,
}
spaceBeforePlaceHolder(OS);
}
+
+ // Print nullability type specifiers.
+ if (T->getAttrKind() == AttributedType::attr_nonnull ||
+ T->getAttrKind() == AttributedType::attr_nullable ||
+ T->getAttrKind() == AttributedType::attr_null_unspecified) {
+ if (T->getAttrKind() == AttributedType::attr_nonnull)
+ OS << " __nonnull";
+ else if (T->getAttrKind() == AttributedType::attr_nullable)
+ OS << " __nullable";
+ else if (T->getAttrKind() == AttributedType::attr_null_unspecified)
+ OS << " __null_unspecified";
+ else
+ llvm_unreachable("unhandled nullability");
+ spaceBeforePlaceHolder(OS);
+ }
}
void TypePrinter::printAttributedAfter(const AttributedType *T,
@@ -1154,12 +1169,34 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
if (T->isMSTypeSpec())
return;
+ // Nothing to print after.
+ if (T->getAttrKind() == AttributedType::attr_nonnull ||
+ T->getAttrKind() == AttributedType::attr_nullable ||
+ T->getAttrKind() == AttributedType::attr_null_unspecified)
+ return printAfter(T->getModifiedType(), OS);
+
// If this is a calling convention attribute, don't print the implicit CC from
// the modified type.
SaveAndRestore<bool> MaybeSuppressCC(InsideCCAttribute, T->isCallingConv());
printAfter(T->getModifiedType(), OS);
+ // Print nullability type specifiers that occur after
+ if (T->getAttrKind() == AttributedType::attr_nonnull ||
+ T->getAttrKind() == AttributedType::attr_nullable ||
+ T->getAttrKind() == AttributedType::attr_null_unspecified) {
+ if (T->getAttrKind() == AttributedType::attr_nonnull)
+ OS << " __nonnull";
+ else if (T->getAttrKind() == AttributedType::attr_nullable)
+ OS << " __nullable";
+ else if (T->getAttrKind() == AttributedType::attr_null_unspecified)
+ OS << " __null_unspecified";
+ else
+ llvm_unreachable("unhandled nullability");
+
+ return;
+ }
+
OS << " __attribute__((";
switch (T->getAttrKind()) {
default: llvm_unreachable("This attribute should have been handled already");
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index ccfb30240b0..62498c64464 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1075,6 +1075,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("cxx_exceptions", LangOpts.CXXExceptions)
.Case("cxx_rtti", LangOpts.RTTI)
.Case("enumerator_attributes", true)
+ .Case("nullability", LangOpts.ObjC1 || LangOpts.GNUMode)
.Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory))
.Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread))
.Case("dataflow_sanitizer", LangOpts.Sanitize.has(SanitizerKind::DataFlow))
@@ -1222,6 +1223,7 @@ static bool HasExtension(const Preprocessor &PP, const IdentifierInfo *II) {
// Because we inherit the feature list from HasFeature, this string switch
// must be less restrictive than HasFeature's.
return llvm::StringSwitch<bool>(Extension)
+ .Case("nullability", true)
// C11 features supported by other languages as extensions.
.Case("c_alignas", true)
.Case("c_alignof", true)
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index eb6800d65c9..96555fcf877 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -687,6 +687,28 @@ void Parser::ParseOpenCLQualifiers(ParsedAttributes &Attrs) {
AttributeList::AS_Keyword);
}
+void Parser::ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs) {
+ // Treat these like attributes, even though they're type specifiers.
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified: {
+ IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+ SourceLocation AttrNameLoc = ConsumeToken();
+ if (!getLangOpts().ObjC1)
+ Diag(AttrNameLoc, diag::ext_nullability)
+ << AttrName;
+ attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
+ AttributeList::AS_Keyword);
+ break;
+ }
+ default:
+ return;
+ }
+ }
+}
+
static bool VersionNumberSeparator(const char Separator) {
return (Separator == '.' || Separator == '_');
}
@@ -3040,6 +3062,13 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
ParseOpenCLAttributes(DS.getAttributes());
continue;
+ // Nullability type specifiers.
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
+ ParseNullabilityTypeSpecifiers(DS.getAttributes());
+ continue;
+
// storage-class-specifier
case tok::kw_typedef:
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_typedef, Loc,
@@ -4284,6 +4313,10 @@ bool Parser::isTypeSpecifierQualifier() {
case tok::kw___pascal:
case tok::kw___unaligned:
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
+
case tok::kw___private:
case tok::kw___local:
case tok::kw___global:
@@ -4457,6 +4490,10 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
case tok::kw___pascal:
case tok::kw___unaligned:
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
+
case tok::kw___private:
case tok::kw___local:
case tok::kw___global:
@@ -4686,6 +4723,14 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, unsigned AttrReqs,
continue;
}
goto DoneWithTypeQuals;
+
+ // Nullability type specifiers.
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
+ ParseNullabilityTypeSpecifiers(DS.getAttributes());
+ continue;
+
case tok::kw___attribute:
if (AttrReqs & AR_GNUAttributesParsedAndRejected)
// When GNU attributes are expressly forbidden, diagnose their usage.
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index e21409a62fc..d63cf24bcdb 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -631,7 +631,9 @@ Parser::TPResult Parser::TryParsePtrOperatorSeq() {
(Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
// ptr-operator
ConsumeToken();
- while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict))
+ while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict,
+ tok::kw___nonnull, tok::kw___nullable,
+ tok::kw___null_unspecified))
ConsumeToken();
} else {
return TPResult::True;
@@ -1274,6 +1276,9 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw___ptr32:
case tok::kw___forceinline:
case tok::kw___unaligned:
+ case tok::kw___nonnull:
+ case tok::kw___nullable:
+ case tok::kw___null_unspecified:
return TPResult::True;
// Borland
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 73bdd984c0b..f4af3f320a8 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2465,6 +2465,28 @@ static void mergeParamDeclAttributes(ParmVarDecl *newDecl,
if (!foundAny) newDecl->dropAttrs();
}
+static void mergeParamDeclTypes(ParmVarDecl *NewParam,
+ const ParmVarDecl *OldParam,
+ Sema &S) {
+ if (auto Oldnullability = OldParam->getType()->getNullability(S.Context)) {
+ if (auto Newnullability = NewParam->getType()->getNullability(S.Context)) {
+ if (*Oldnullability != *Newnullability) {
+ S.Diag(NewParam->getLocation(), diag::warn_mismatched_nullability_attr)
+ << static_cast<unsigned>(*Newnullability)
+ << static_cast<unsigned>(*Oldnullability);
+ S.Diag(OldParam->getLocation(), diag::note_previous_declaration);
+ }
+ }
+ else {
+ QualType NewT = NewParam->getType();
+ NewT = S.Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(*Oldnullability),
+ NewT, NewT);
+ NewParam->setType(NewT);
+ }
+ }
+}
+
namespace {
/// Used in MergeFunctionDecl to keep track of function parameters in
@@ -3101,9 +3123,12 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old,
// Merge attributes from the parameters. These can mismatch with K&R
// declarations.
if (New->getNumParams() == Old->getNumParams())
- for (unsigned i = 0, e = New->getNumParams(); i != e; ++i)
- mergeParamDeclAttributes(New->getParamDecl(i), Old->getParamDecl(i),
- *this);
+ for (unsigned i = 0, e = New->getNumParams(); i != e; ++i) {
+ ParmVarDecl *NewParam = New->getParamDecl(i);
+ ParmVarDecl *OldParam = Old->getParamDecl(i);
+ mergeParamDeclAttributes(NewParam, OldParam, *this);
+ mergeParamDeclTypes(NewParam, OldParam, *this);
+ }
if (getLangOpts().CPlusPlus)
return MergeCXXFunctionDecl(New, Old, S);
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d3787ec44b1..ea749a46e09 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -22,6 +22,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeLocVisitor.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Parse/ParseDiagnostic.h"
@@ -121,6 +122,12 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr,
case AttributeList::AT_SPtr: \
case AttributeList::AT_UPtr
+// Nullability qualifiers.
+#define NULLABILITY_TYPE_ATTRS_CASELIST \
+ case AttributeList::AT_TypeNonNull: \
+ case AttributeList::AT_TypeNullable: \
+ case AttributeList::AT_TypeNullUnspecified
+
namespace {
/// An object which stores processing state for the entire
/// GetTypeForDeclarator process.
@@ -307,8 +314,12 @@ static bool handleObjCPointerTypeAttr(TypeProcessingState &state,
///
/// \param i - a notional index which the search will start
/// immediately inside
+///
+/// \param onlyBlockPointers Whether we should only look into block
+/// pointer types (vs. all pointer types).
static DeclaratorChunk *maybeMovePastReturnType(Declarator &declarator,
- unsigned i) {
+ unsigned i,
+ bool onlyBlockPointers) {
assert(i <= declarator.getNumTypeObjects());
DeclaratorChunk *result = nullptr;
@@ -329,20 +340,26 @@ static DeclaratorChunk *maybeMovePastReturnType(Declarator &declarator,
return result;
// If we do find a function declarator, scan inwards from that,
- // looking for a block-pointer declarator.
+ // looking for a (block-)pointer declarator.
case DeclaratorChunk::Function:
for (--i; i != 0; --i) {
- DeclaratorChunk &blockChunk = declarator.getTypeObject(i-1);
- switch (blockChunk.Kind) {
+ DeclaratorChunk &ptrChunk = declarator.getTypeObject(i-1);
+ switch (ptrChunk.Kind) {
case DeclaratorChunk::Paren:
- case DeclaratorChunk::Pointer:
case DeclaratorChunk::Array:
case DeclaratorChunk::Function:
case DeclaratorChunk::Reference:
- case DeclaratorChunk::MemberPointer:
continue;
+
+ case DeclaratorChunk::MemberPointer:
+ case DeclaratorChunk::Pointer:
+ if (onlyBlockPointers)
+ continue;
+
+ // fallthrough
+
case DeclaratorChunk::BlockPointer:
- result = &blockChunk;
+ result = &ptrChunk;
goto continue_outer;
}
llvm_unreachable("bad declarator chunk kind");
@@ -382,7 +399,8 @@ static void distributeObjCPointerTypeAttr(TypeProcessingState &state,
DeclaratorChunk *destChunk = nullptr;
if (state.isProcessingDeclSpec() &&
attr.getKind() == AttributeList::AT_ObjCOwnership)
- destChunk = maybeMovePastReturnType(declarator, i - 1);
+ destChunk = maybeMovePastReturnType(declarator, i - 1,
+ /*onlyBlockPointers=*/true);
if (!destChunk) destChunk = &chunk;
moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
@@ -398,7 +416,9 @@ static void distributeObjCPointerTypeAttr(TypeProcessingState &state,
case DeclaratorChunk::Function:
if (state.isProcessingDeclSpec() &&
attr.getKind() == AttributeList::AT_ObjCOwnership) {
- if (DeclaratorChunk *dest = maybeMovePastReturnType(declarator, i)) {
+ if (DeclaratorChunk *dest = maybeMovePastReturnType(
+ declarator, i,
+ /*onlyBlockPointers=*/true)) {
moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
dest->getAttrListRef());
return;
@@ -620,6 +640,10 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state,
// Microsoft type attributes cannot go after the declarator-id.
continue;
+ NULLABILITY_TYPE_ATTRS_CASELIST:
+ // Nullability specifiers cannot go after the declarator-id.
+ continue;
+
default:
break;
}
@@ -3495,6 +3519,12 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) {
return AttributeList::AT_SPtr;
case AttributedType::attr_uptr:
return AttributeList::AT_UPtr;
+ case AttributedType::attr_nonnull:
+ return AttributeList::AT_TypeNonNull;
+ case AttributedType::attr_nullable:
+ return AttributeList::AT_TypeNullable;
+ case AttributedType::attr_null_unspecified:
+ return AttributeList::AT_TypeNullUnspecified;
}
llvm_unreachable("unexpected attribute kind!");
}
@@ -4114,7 +4144,8 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state,
// just be the return type of a block pointer.
if (state.isProcessingDeclSpec()) {
Declarator &D = state.getDeclarator();
- if (maybeMovePastReturnType(D, D.getNumTypeObjects()))
+ if (maybeMovePastReturnType(D, D.getNumTypeObjects(),
+ /*onlyBlockPointers=*/true))
return false;
}
}
@@ -4491,6 +4522,205 @@ 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);
+
+ // Check for existing nullability attributes on the type.
+ QualType desugared = type;
+ while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {
+ // Check whether there is already a null
+ if (auto existingNullability = attributed->getImmediateNullability()) {
+ // Duplicated nullability.
+ if (nullability == *existingNullability) {
+ S.Diag(attr.getLoc(), diag::warn_duplicate_nullability)
+ << static_cast<unsigned>(nullability);
+ return true;
+ }
+
+ // Conflicting nullability.
+ S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
+ << static_cast<unsigned>(nullability)
+ << static_cast<unsigned>(*existingNullability);
+ return true;
+ }
+
+ desugared = attributed->getEquivalentType();
+ }
+
+ // If there is already a different nullability specifier, complain.
+ // This (unlike the code above) looks through typedefs that might
+ // have nullability specifiers on them, which means we cannot
+ // provide a useful Fix-It.
+ if (auto existingNullability = desugared->getNullability(Context)) {
+ if (nullability != *existingNullability) {
+ S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
+ << static_cast<unsigned>(nullability)
+ << static_cast<unsigned>(*existingNullability);
+
+ // Try to find the typedef with the existing nullability specifier.
+ if (auto typedefType = desugared->getAs<TypedefType>()) {
+ TypedefNameDecl *typedefDecl = typedefType->getDecl();
+ QualType underlyingType = typedefDecl->getUnderlyingType();
+ if (auto typedefNullability
+ = AttributedType::stripOuterNullability(underlyingType)) {
+ if (*typedefNullability == *existingNullability) {
+ S.Diag(typedefDecl->getLocation(), diag::note_nullability_here)
+ << static_cast<unsigned>(*existingNullability);
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+
+ // 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;
+ 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);
+ return false;
+}
+
+/// Check whether there is a nullability attribute of any kind in the given
+/// attribute list.
+static bool hasNullabilityAttr(const AttributeList *attrs) {
+ for (const AttributeList *attr = attrs; attr;
+ attr = attr->getNext()) {
+ if (attr->getKind() == AttributeList::AT_TypeNonNull ||
+ attr->getKind() == AttributeList::AT_TypeNullable ||
+ attr->getKind() == AttributeList::AT_TypeNullUnspecified)
+ return true;
+ }
+
+ return false;
+}
+
+/// 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.
+///
+/// \returns true if the nullability annotation was distributed, false
+/// otherwise.
+static bool distributeNullabilityTypeAttr(TypeProcessingState &state,
+ QualType type,
+ AttributeList &attr) {
+ Declarator &declarator = state.getDeclarator();
+
+ /// Attempt to move the attribute to the specified chunk.
+ auto moveToChunk = [&](DeclaratorChunk &chunk, bool inFunction) -> bool {
+ // If there is already a nullability attribute there, don't add
+ // one.
+ if (hasNullabilityAttr(chunk.getAttrListRef()))
+ return false;
+
+ // Complain about the nullability qualifier being in the wrong
+ // place.
+ unsigned pointerKind
+ = chunk.Kind == DeclaratorChunk::Pointer ? (inFunction ? 3 : 0)
+ : chunk.Kind == DeclaratorChunk::BlockPointer ? 1
+ : inFunction? 4 : 2;
+
+ auto diag = state.getSema().Diag(attr.getLoc(),
+ diag::warn_nullability_declspec)
+ << static_cast<unsigned>(mapNullabilityAttrKind(attr.getKind()))
+ << type
+ << pointerKind;
+
+ // FIXME: MemberPointer chunks don't carry the location of the *.
+ if (chunk.Kind != DeclaratorChunk::MemberPointer) {
+ diag << FixItHint::CreateRemoval(attr.getLoc())
+ << FixItHint::CreateInsertion(
+ state.getSema().getPreprocessor()
+ .getLocForEndOfToken(chunk.Loc),
+ " " + attr.getName()->getName().str() + " ");
+ }
+
+ moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
+ chunk.getAttrListRef());
+ return true;
+ };
+
+ // Move it to the outermost pointer, member pointer, or block
+ // pointer declarator.
+ for (unsigned i = state.getCurrentChunkIndex(); i != 0; --i) {
+ DeclaratorChunk &chunk = declarator.getTypeObject(i-1);
+ switch (chunk.Kind) {
+ case DeclaratorChunk::Pointer:
+ case DeclaratorChunk::BlockPointer:
+ case DeclaratorChunk::MemberPointer:
+ return moveToChunk(chunk, false);
+
+ case DeclaratorChunk::Paren:
+ case DeclaratorChunk::Array:
+ continue;
+
+ case DeclaratorChunk::Function:
+ // Try to move past the return type to a function/block/member
+ // function pointer.
+ if (DeclaratorChunk *dest = maybeMovePastReturnType(
+ declarator, i,
+ /*onlyBlockPointers=*/false)) {
+ return moveToChunk(*dest, true);
+ }
+
+ return false;
+
+ // Don't walk through these.
+ case DeclaratorChunk::Reference:
+ return false;
+ }
+ }
+
+ return false;
+}
+
static AttributedType::Kind getCCTypeAttrKind(AttributeList &Attr) {
assert(!Attr.isInvalid());
switch (Attr.getKind()) {
@@ -4997,6 +5227,20 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
attr.setUsedAsTypeAttr();
break;
+ NULLABILITY_TYPE_ATTRS_CASELIST:
+ // Either add nullability here or try to distribute it. We
+ // don't want to distribute the nullability specifier past any
+ // dependent type, because that complicates the user model.
+ if (type->canHaveNullability() || type->isDependentType() ||
+ !distributeNullabilityTypeAttr(state, type, attr)) {
+ if (handleNullabilityTypeAttr(state, attr, type)) {
+ attr.setInvalid();
+ }
+
+ attr.setUsedAsTypeAttr();
+ }
+ break;
+
case AttributeList::AT_NSReturnsRetained:
if (!state.getSema().getLangOpts().ObjCAutoRefCount)
break;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 878300ebc9f..c4cd22609b7 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5386,6 +5386,17 @@ QualType TreeTransform<Derived>::TransformAttributedType(
= getDerived().TransformType(oldType->getEquivalentType());
if (equivalentType.isNull())
return QualType();
+
+ // Check whether we can add nullability; it is only represented as
+ // type sugar, and therefore cannot be diagnosed in any other way.
+ if (auto nullability = oldType->getImmediateNullability()) {
+ if (!modifiedType->canHaveNullability()) {
+ SemaRef.Diag(TL.getAttrNameLoc(), diag::err_nullability_nonpointer)
+ << static_cast<unsigned>(*nullability) << modifiedType;
+ return QualType();
+ }
+ }
+
result = SemaRef.Context.getAttributedType(oldType->getAttrKind(),
modifiedType,
equivalentType);
OpenPOWER on IntegriCloud