diff options
| author | Douglas Gregor <dgregor@apple.com> | 2015-06-19 17:51:05 +0000 |
|---|---|---|
| committer | Douglas Gregor <dgregor@apple.com> | 2015-06-19 17:51:05 +0000 |
| commit | 261a89b0f70b3193b5358698eb92d18c4c855b36 (patch) | |
| tree | 9c12c7684ec69fc50b2e33f16c10314ffa033017 /clang/lib | |
| parent | 4f093bf1ce0c6b4c9cd4472fbaa0b2b0a942a38c (diff) | |
| download | bcm5719-llvm-261a89b0f70b3193b5358698eb92d18c4c855b36.tar.gz bcm5719-llvm-261a89b0f70b3193b5358698eb92d18c4c855b36.zip | |
Introduce type nullability specifiers for C/C++.
Introduces the type specifiers __nonnull, __nullable, and
__null_unspecified that describe the nullability of the pointer type
to which the specifier appertains. Nullability type specifiers improve
on the existing nonnull attributes in a few ways:
- They apply to types, so one can represent a pointer to a non-null
pointer, use them in function pointer types, etc.
- As type specifiers, they are syntactically more lightweight than
__attribute__s or [[attribute]]s.
- They can express both the notion of 'should never be null' and
also 'it makes sense for this to be null', and therefore can more
easily catch errors of omission where one forgot to annotate the
nullability of a particular pointer (this will come in a subsequent
patch).
Nullability type specifiers are maintained as type sugar, and
therefore have no effect on mangling, encoding, overloading,
etc. Nonetheless, they will be used for warnings about, e.g., passing
'null' to a method that does not accept it.
This is the C/C++ part of rdar://problem/18868820.
llvm-svn: 240146
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/AST/Type.cpp | 151 | ||||
| -rw-r--r-- | clang/lib/AST/TypePrinter.cpp | 37 | ||||
| -rw-r--r-- | clang/lib/Lex/PPMacroExpansion.cpp | 2 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 45 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseTentative.cpp | 7 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 31 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaType.cpp | 264 | ||||
| -rw-r--r-- | clang/lib/Sema/TreeTransform.h | 11 |
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); |

