diff options
author | Douglas Gregor <dgregor@apple.com> | 2015-06-19 18:14:38 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2015-06-19 18:14:38 +0000 |
commit | 813a066f16df52783708fdc2ef708bb76a9ff521 (patch) | |
tree | 0657ccae705e2a09c50dd92d26d645bff370aded /clang/lib/Sema | |
parent | b4866e85e5ffa0d352d496958582f0983172dc01 (diff) | |
download | bcm5719-llvm-813a066f16df52783708fdc2ef708bb76a9ff521.tar.gz bcm5719-llvm-813a066f16df52783708fdc2ef708bb76a9ff521.zip |
Extend type nullability qualifiers for Objective-C.
Introduce context-sensitive, non-underscored nullability specifiers
(nonnull, nullable, null_unspecified) for Objective-C method return
types, method parameter types, and properties.
Introduce Objective-C-specific semantics, including computation of the
nullability of the result of a message send, merging of nullability
information from the @interface of a class into its @implementation,
etc .
This is the Objective-C part of rdar://problem/18868820.
llvm-svn: 240154
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 8 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclObjC.cpp | 165 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprObjC.cpp | 133 | ||||
-rw-r--r-- | clang/lib/Sema/SemaObjCProperty.cpp | 40 | ||||
-rw-r--r-- | clang/lib/Sema/SemaType.cpp | 127 | ||||
-rw-r--r-- | clang/lib/Sema/TreeTransform.h | 2 |
6 files changed, 365 insertions, 110 deletions
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(); } } |