r240154 - Extend type nullability qualifiers for Objective-C.
Aaron Ballman
aaron at aaronballman.com
Sat Jun 20 12:04:06 PDT 2015
On Fri, Jun 19, 2015 at 2:14 PM, Douglas Gregor <dgregor at apple.com> wrote:
> Author: dgregor
> Date: Fri Jun 19 13:14:38 2015
> New Revision: 240154
>
> URL: http://llvm.org/viewvc/llvm-project?rev=240154&view=rev
> Log:
> 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.
>
> Added:
> cfe/trunk/test/SemaObjC/nullable-weak-property.m
> cfe/trunk/test/SemaObjC/override-nullability.m
> Modified:
> cfe/trunk/include/clang/AST/ASTContext.h
> cfe/trunk/include/clang/AST/DeclBase.h
> cfe/trunk/include/clang/AST/DeclObjC.h
> cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td
> cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
> cfe/trunk/include/clang/Basic/Specifiers.h
> cfe/trunk/include/clang/Parse/Parser.h
> cfe/trunk/include/clang/Sema/AttributeList.h
> cfe/trunk/include/clang/Sema/DeclSpec.h
> cfe/trunk/include/clang/Sema/Sema.h
> cfe/trunk/lib/AST/ASTImporter.cpp
> cfe/trunk/lib/AST/DeclObjC.cpp
> cfe/trunk/lib/AST/DeclPrinter.cpp
> cfe/trunk/lib/Basic/IdentifierTable.cpp
> cfe/trunk/lib/Parse/ParseObjc.cpp
> cfe/trunk/lib/Parse/Parser.cpp
> cfe/trunk/lib/Sema/SemaDecl.cpp
> cfe/trunk/lib/Sema/SemaDeclObjC.cpp
> cfe/trunk/lib/Sema/SemaExprObjC.cpp
> cfe/trunk/lib/Sema/SemaObjCProperty.cpp
> cfe/trunk/lib/Sema/SemaType.cpp
> cfe/trunk/lib/Sema/TreeTransform.h
> cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
> cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
> cfe/trunk/test/Index/comment-objc-decls.m
> cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m
> cfe/trunk/test/SemaObjC/arc-unavailable-for-weakref.m
> cfe/trunk/test/SemaObjC/nullability.m
>
> Modified: cfe/trunk/include/clang/AST/ASTContext.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/AST/ASTContext.h (original)
> +++ cfe/trunk/include/clang/AST/ASTContext.h Fri Jun 19 13:14:38 2015
> @@ -1854,6 +1854,36 @@ public:
> getCanonicalType(T2).getTypePtr();
> }
>
> + bool hasSameNullabilityTypeQualifier(QualType SubT, QualType SuperT,
> + bool IsParam) const {
> + auto SubTnullability = SubT->getNullability(*this);
> + auto SuperTnullability = SuperT->getNullability(*this);
> + if (SubTnullability.hasValue() == SuperTnullability.hasValue()) {
> + // Neither has nullability; return true
> + if (!SubTnullability)
> + return true;
> + // Both have nullability qualifier.
> + if (*SubTnullability == *SuperTnullability ||
> + *SubTnullability == NullabilityKind::Unspecified ||
> + *SuperTnullability == NullabilityKind::Unspecified)
> + return true;
> +
> + if (IsParam) {
> + // Ok for the superclass method parameter to be “nonnull†and the subclass
> + // method parameter to be “nullableâ€
Non-ASCII characters in the comments.
> + return (*SuperTnullability == NullabilityKind::NonNull &&
> + *SubTnullability == NullabilityKind::Nullable);
> + }
> + else {
> + // For the return type, it’s okay for the superclass method to specify
> + // “nullable†and the subclass method specify “nonnullâ€
Non-ASCII characters in the comments.
> + return (*SuperTnullability == NullabilityKind::Nullable &&
> + *SubTnullability == NullabilityKind::NonNull);
> + }
> + }
> + return true;
This appears to be one of the few functions that care about the
optionality of getNullability(). I find the dereferences sprinkled all
over to be really distracting and kind of ugly. Is there a better
approach we could use (even if it's a different code pattern when
dealing with optional) so that we don't have to stare at stars all day
long?
> + }
> +
> bool ObjCMethodsAreEqual(const ObjCMethodDecl *MethodDecl,
> const ObjCMethodDecl *MethodImp);
>
>
> Modified: cfe/trunk/include/clang/AST/DeclBase.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclBase.h?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/AST/DeclBase.h (original)
> +++ cfe/trunk/include/clang/AST/DeclBase.h Fri Jun 19 13:14:38 2015
> @@ -178,7 +178,12 @@ public:
> OBJC_TQ_Out = 0x4,
> OBJC_TQ_Bycopy = 0x8,
> OBJC_TQ_Byref = 0x10,
> - OBJC_TQ_Oneway = 0x20
> + OBJC_TQ_Oneway = 0x20,
> +
> + /// The nullability qualifier is set when the nullability of the
> + /// result or parameter was expressed via a context-sensitive
> + /// keyword.
> + OBJC_TQ_CSNullability = 0x40
> };
>
> protected:
>
> Modified: cfe/trunk/include/clang/AST/DeclObjC.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclObjC.h?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/AST/DeclObjC.h (original)
> +++ cfe/trunk/include/clang/AST/DeclObjC.h Fri Jun 19 13:14:38 2015
> @@ -141,7 +141,7 @@ private:
>
> // NOTE: VC++ treats enums as signed, avoid using the ObjCDeclQualifier enum
> /// in, inout, etc.
> - unsigned objcDeclQualifier : 6;
> + unsigned objcDeclQualifier : 7;
>
> /// \brief Indicates whether this method has a related result type.
> unsigned RelatedResultType : 1;
> @@ -2203,13 +2203,16 @@ public:
> OBJC_PR_atomic = 0x100,
> OBJC_PR_weak = 0x200,
> OBJC_PR_strong = 0x400,
> - OBJC_PR_unsafe_unretained = 0x800
> + OBJC_PR_unsafe_unretained = 0x800,
> + /// Indicates that the nullability of the type was spelled with a
> + /// property attribute rather than a type qualifier.
> + OBJC_PR_nullability = 0x1000
> // Adding a property should change NumPropertyAttrsBits
> };
>
> enum {
> /// \brief Number of bits fitting all the property attributes.
> - NumPropertyAttrsBits = 12
> + NumPropertyAttrsBits = 13
> };
>
> enum SetterKind { Assign, Retain, Copy, Weak };
> @@ -2217,7 +2220,8 @@ public:
> private:
> SourceLocation AtLoc; // location of \@property
> SourceLocation LParenLoc; // location of '(' starting attribute list or null.
> - TypeSourceInfo *DeclType;
> + QualType DeclType;
> + TypeSourceInfo *DeclTypeSourceInfo;
> unsigned PropertyAttributes : NumPropertyAttrsBits;
> unsigned PropertyAttributesAsWritten : NumPropertyAttrsBits;
> // \@required/\@optional
> @@ -2232,12 +2236,13 @@ private:
>
> ObjCPropertyDecl(DeclContext *DC, SourceLocation L, IdentifierInfo *Id,
> SourceLocation AtLocation, SourceLocation LParenLocation,
> - TypeSourceInfo *T)
> + QualType T, TypeSourceInfo *TSI,
> + PropertyControl propControl)
> : NamedDecl(ObjCProperty, DC, L, Id), AtLoc(AtLocation),
> - LParenLoc(LParenLocation), DeclType(T),
> + LParenLoc(LParenLocation), DeclType(T), DeclTypeSourceInfo(TSI),
> PropertyAttributes(OBJC_PR_noattr),
> PropertyAttributesAsWritten(OBJC_PR_noattr),
> - PropertyImplementation(None),
> + PropertyImplementation(propControl),
> GetterName(Selector()),
> SetterName(Selector()),
> GetterMethodDecl(nullptr), SetterMethodDecl(nullptr),
> @@ -2248,7 +2253,8 @@ public:
> SourceLocation L,
> IdentifierInfo *Id, SourceLocation AtLocation,
> SourceLocation LParenLocation,
> - TypeSourceInfo *T,
> + QualType T,
> + TypeSourceInfo *TSI,
> PropertyControl propControl = None);
>
> static ObjCPropertyDecl *CreateDeserialized(ASTContext &C, unsigned ID);
> @@ -2259,9 +2265,14 @@ public:
> SourceLocation getLParenLoc() const { return LParenLoc; }
> void setLParenLoc(SourceLocation L) { LParenLoc = L; }
>
> - TypeSourceInfo *getTypeSourceInfo() const { return DeclType; }
> - QualType getType() const { return DeclType->getType(); }
> - void setType(TypeSourceInfo *T) { DeclType = T; }
> + TypeSourceInfo *getTypeSourceInfo() const { return DeclTypeSourceInfo; }
> +
> + QualType getType() const { return DeclType; }
> +
> + void setType(QualType T, TypeSourceInfo *TSI) {
> + DeclType = T;
> + DeclTypeSourceInfo = TSI;
> + }
>
> PropertyAttributeKind getPropertyAttributes() const {
> return PropertyAttributeKind(PropertyAttributes);
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td Fri Jun 19 13:14:38 2015
> @@ -101,15 +101,32 @@ def err_enum_template : Error<"enumerati
>
> let CategoryName = "Nullability Issue" in {
>
> -def warn_mismatched_nullability_attr : Warning<
> - "nullability specifier "
> - "'__%select{nonnull|nullable|null_unspecified}0' "
> - "conflicts with existing specifier "
> - "'__%select{nonnull|nullable|null_unspecified}1'">,
> +def warn_nullability_duplicate : Warning<
> + "duplicate nullability specifier "
> + "'%select{__|}1%select{nonnull|nullable|null_unspecified}0'">,
> + InGroup<Nullability>;
> +
> +def warn_conflicting_nullability_attr_overriding_ret_types : Warning<
> + "conflicting nullability specifier on return types, "
> + "'%select{%select{__|}1nonnull|"
> + "%select{__|}1nullable|%select{__|}1null_unspecified}0' "
> + "conflicts with existing specifier '%select{%select{__|}3nonnull|"
> + "%select{__|}3nullable|%select{__|}3null_unspecified}2'">,
> InGroup<Nullability>;
>
> -def note_nullability_here : Note<
> - "'%select{__nonnull|__nullable|__null_unspecified}0' specified here">;
> +def warn_conflicting_nullability_attr_overriding_param_types : Warning<
> + "conflicting nullability specifier on parameter types, "
> + "'%select{%select{__|}1nonnull|"
> + "%select{__|}1nullable|%select{__|}1null_unspecified}0' "
> + "conflicts with existing specifier '%select{%select{__|}3nonnull|"
> + "%select{__|}3nullable|%select{__|}3null_unspecified}2'">,
> + InGroup<Nullability>;
> +
> +def err_nullability_conflicting : Error<
> + "nullability specifier "
> + "'%select{__|}1%select{nonnull|nullable|null_unspecified}0' conflicts with "
> + "existing specifier '%select{__|}3%select{nonnull|nullable|"
> + "null_unspecified}2'">;
>
> }
>
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jun 19 13:14:38 2015
> @@ -7676,9 +7676,11 @@ def warn_profile_data_unprofiled : Warni
>
> let CategoryName = "Nullability Issue" in {
>
> -def warn_duplicate_nullability : Warning<
> - "duplicate nullability specifier "
> - "'%select{__nonnull|__nullable|__null_unspecified}0'">,
> +def warn_mismatched_nullability_attr : Warning<
> + "nullability specifier "
> + "'%select{__|}1%select{nonnull|nullable|null_unspecified}0' "
> + "conflicts with existing specifier "
> + "'%select{__|}3%select{nonnull|nullable|null_unspecified}2'">,
> InGroup<Nullability>;
>
> def warn_nullability_declspec : Warning<
> @@ -7690,21 +7692,28 @@ def warn_nullability_declspec : Warning<
> InGroup<NullabilityDeclSpec>,
> DefaultError;
>
> -def err_nullability_nonpointer : Error<
> - "nullability specifier "
> - "'%select{__nonnull|__nullable|__null_unspecified}0' cannot be applied to "
> - "non-pointer type %1">;
> +def note_nullability_here : Note<
> + "'%select{__nonnull|__nullable|__null_unspecified}0' specified here">;
>
> -def err_nullability_conflicting : Error<
> +def err_nullability_nonpointer : Error<
> "nullability specifier "
> - "'%select{__nonnull|__nullable|__null_unspecified}0' conflicts with existing "
> - "specifier '%select{__nonnull|__nullable|__null_unspecified}1'">;
> + "'%select{__|}1%select{nonnull|nullable|null_unspecified}0' cannot be applied "
> + "to non-pointer type %2">;
>
> def warn_nullability_lost : Warning<
> "implicit conversion from nullable pointer %0 to non-nullable pointer "
> "type %1">,
> InGroup<NullableToNonNullConversion>, DefaultIgnore;
>
> +def err_nullability_cs_multilevel : Error<
> + "nullability keyword "
> + "'%select{nonnull|nullable|null_unspecified}0' cannot be applied to "
> + "multi-level pointer type %1">;
> +def note_nullability_type_specifier : Note<
> + "use nullability type specifier "
> + "'%select{__nonnull|__nullable|__null_unspecified}0' to affect the innermost "
> + "pointer type of %1">;
> +
> }
>
> } // end of sema component.
>
> Modified: cfe/trunk/include/clang/Basic/Specifiers.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Specifiers.h?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/Specifiers.h (original)
> +++ cfe/trunk/include/clang/Basic/Specifiers.h Fri Jun 19 13:14:38 2015
> @@ -16,6 +16,7 @@
> #ifndef LLVM_CLANG_BASIC_SPECIFIERS_H
> #define LLVM_CLANG_BASIC_SPECIFIERS_H
>
> +#include "llvm/ADT/StringRef.h"
> #include "llvm/Support/DataTypes.h"
>
> namespace clang {
> @@ -254,6 +255,9 @@ namespace clang {
> /// though it has been considered.
> Unspecified
> };
> +
> + /// Retrieve the spelling of the given nullability kind.
> + llvm::StringRef getNullabilitySpelling(NullabilityKind kind);
> } // end namespace clang
>
> #endif // LLVM_CLANG_BASIC_SPECIFIERS_H
>
> Modified: cfe/trunk/include/clang/Parse/Parser.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Parse/Parser.h (original)
> +++ cfe/trunk/include/clang/Parse/Parser.h Fri Jun 19 13:14:38 2015
> @@ -139,6 +139,11 @@ class Parser : public CodeCompletionHand
> // used as type traits.
> llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind> RevertibleTypeTraits;
>
> + /// Nullability type specifiers.
> + IdentifierInfo *Ident___nonnull = nullptr;
> + IdentifierInfo *Ident___nullable = nullptr;
> + IdentifierInfo *Ident___null_unspecified = nullptr;
> +
> std::unique_ptr<PragmaHandler> AlignHandler;
> std::unique_ptr<PragmaHandler> GCCVisibilityHandler;
> std::unique_ptr<PragmaHandler> OptionsHandler;
> @@ -303,6 +308,10 @@ public:
> return true;
> }
>
> + /// Retrieve the underscored keyword (__nonnull, __nullable,
> + /// __null_unspecified) that corresponds to the given nullability kind.
> + IdentifierInfo *getNullabilityKeyword(NullabilityKind nullability);
> +
> private:
> //===--------------------------------------------------------------------===//
> // Low-Level token peeking and consumption methods.
> @@ -1282,6 +1291,7 @@ private:
> // Definitions for Objective-c context sensitive keywords recognition.
> enum ObjCTypeQual {
> objc_in=0, objc_out, objc_inout, objc_oneway, objc_bycopy, objc_byref,
> + objc_nonnull, objc_nullable, objc_null_unspecified,
> objc_NumQuals
> };
> IdentifierInfo *ObjCTypeQuals[objc_NumQuals];
>
> Modified: cfe/trunk/include/clang/Sema/AttributeList.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AttributeList.h?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/AttributeList.h (original)
> +++ cfe/trunk/include/clang/Sema/AttributeList.h Fri Jun 19 13:14:38 2015
> @@ -120,6 +120,9 @@ private:
> /// True if this has a ParsedType
> unsigned HasParsedType : 1;
>
> + /// True when this keyword attribute is a context-sensitive keyword.
> + unsigned IsContextSensitiveKeyword : 1;
This is more expensive than you might think since the bit field will
now be 33 bits.
> +
> unsigned AttrKind : 8;
>
> /// \brief The location of the 'unavailable' keyword in an
> @@ -220,7 +223,8 @@ private:
> ScopeLoc(scopeLoc), EllipsisLoc(ellipsisLoc), NumArgs(numArgs),
> SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false),
> IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false),
> - HasParsedType(false), NextInPosition(nullptr), NextInPool(nullptr) {
> + HasParsedType(false), IsContextSensitiveKeyword(false),
> + NextInPosition(nullptr), NextInPool(nullptr) {
> if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion));
> AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
> }
> @@ -238,8 +242,8 @@ private:
> ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed),
> Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
> IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
> - UnavailableLoc(unavailable), MessageExpr(messageExpr),
> - NextInPosition(nullptr), NextInPool(nullptr) {
> + IsContextSensitiveKeyword(false), UnavailableLoc(unavailable),
> + MessageExpr(messageExpr), NextInPosition(nullptr), NextInPool(nullptr) {
> ArgsUnion PVal(Parm);
> memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
> new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced);
> @@ -259,7 +263,8 @@ private:
> ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(3), SyntaxUsed(syntaxUsed),
> Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
> IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
> - NextInPosition(nullptr), NextInPool(nullptr) {
> + IsContextSensitiveKeyword(false), NextInPosition(nullptr),
> + NextInPool(nullptr) {
> ArgsVector Args;
> Args.push_back(Parm1);
> Args.push_back(Parm2);
> @@ -277,7 +282,8 @@ private:
> ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed),
> Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
> IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false),
> - NextInPosition(nullptr), NextInPool(nullptr) {
> + IsContextSensitiveKeyword(false), NextInPosition(nullptr),
> + NextInPool(nullptr) {
> ArgsUnion PVal(ArgKind);
> memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
> TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot();
> @@ -295,7 +301,8 @@ private:
> ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed),
> Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
> IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true),
> - NextInPosition(nullptr), NextInPool(nullptr) {
> + IsContextSensitiveKeyword(false), NextInPosition(nullptr),
> + NextInPool(nullptr) {
> new (&getTypeBuffer()) ParsedType(typeArg);
> AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
> }
> @@ -309,7 +316,8 @@ private:
> ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed),
> Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
> IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false),
> - NextInPosition(nullptr), NextInPool(nullptr) {
> + IsContextSensitiveKeyword(false), NextInPosition(nullptr),
> + NextInPool(nullptr) {
> new (&getPropertyDataBuffer()) PropertyData(getterId, setterId);
> AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
> }
> @@ -352,6 +360,15 @@ public:
> }
> bool isKeywordAttribute() const { return SyntaxUsed == AS_Keyword; }
>
> + bool isContextSensitiveKeywordAttribute() const {
> + return IsContextSensitiveKeyword;
> + }
> +
> + void setContextSensitiveKeywordAttribute() {
> + assert(SyntaxUsed == AS_Keyword);
> + IsContextSensitiveKeyword = true;
> + }
> +
> bool isInvalid() const { return Invalid; }
> void setInvalid(bool b = true) const { Invalid = b; }
>
>
> Modified: cfe/trunk/include/clang/Sema/DeclSpec.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/DeclSpec.h?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/DeclSpec.h (original)
> +++ cfe/trunk/include/clang/Sema/DeclSpec.h Fri Jun 19 13:14:38 2015
> @@ -31,6 +31,7 @@
> #include "clang/Lex/Token.h"
> #include "clang/Sema/AttributeList.h"
> #include "clang/Sema/Ownership.h"
> +#include "llvm/ADT/Optional.h"
> #include "llvm/ADT/SmallVector.h"
> #include "llvm/Support/Compiler.h"
> #include "llvm/Support/ErrorHandling.h"
> @@ -785,7 +786,8 @@ public:
> DQ_Out = 0x4,
> DQ_Bycopy = 0x8,
> DQ_Byref = 0x10,
> - DQ_Oneway = 0x20
> + DQ_Oneway = 0x20,
> + DQ_CSNullability = 0x40
> };
>
> /// PropertyAttributeKind - list of property attributes.
> @@ -802,17 +804,21 @@ public:
> DQ_PR_atomic = 0x100,
> DQ_PR_weak = 0x200,
> DQ_PR_strong = 0x400,
> - DQ_PR_unsafe_unretained = 0x800
> + DQ_PR_unsafe_unretained = 0x800,
> + DQ_PR_nullability = 0x1000
> };
>
> -
> ObjCDeclSpec()
> : objcDeclQualifier(DQ_None), PropertyAttributes(DQ_PR_noattr),
> - GetterName(nullptr), SetterName(nullptr) { }
> + Nullability(0), GetterName(nullptr), SetterName(nullptr) { }
> +
> ObjCDeclQualifier getObjCDeclQualifier() const { return objcDeclQualifier; }
> void setObjCDeclQualifier(ObjCDeclQualifier DQVal) {
> objcDeclQualifier = (ObjCDeclQualifier) (objcDeclQualifier | DQVal);
> }
> + void clearObjCDeclQualifier(ObjCDeclQualifier DQVal) {
> + objcDeclQualifier = (ObjCDeclQualifier) (objcDeclQualifier & ~DQVal);
> + }
>
> ObjCPropertyAttributeKind getPropertyAttributes() const {
> return ObjCPropertyAttributeKind(PropertyAttributes);
> @@ -822,6 +828,28 @@ public:
> (ObjCPropertyAttributeKind)(PropertyAttributes | PRVal);
> }
>
> + NullabilityKind getNullability() const {
> + assert(((getObjCDeclQualifier() & DQ_CSNullability) ||
> + (getPropertyAttributes() & DQ_PR_nullability)) &&
> + "Objective-C declspec doesn't have nullability");
> + return static_cast<NullabilityKind>(Nullability);
> + }
> +
> + SourceLocation getNullabilityLoc() const {
> + assert(((getObjCDeclQualifier() & DQ_CSNullability) ||
> + (getPropertyAttributes() & DQ_PR_nullability)) &&
> + "Objective-C declspec doesn't have nullability");
> + return NullabilityLoc;
> + }
> +
> + void setNullability(SourceLocation loc, NullabilityKind kind) {
> + assert(((getObjCDeclQualifier() & DQ_CSNullability) ||
> + (getPropertyAttributes() & DQ_PR_nullability)) &&
> + "Set the nullability declspec or property attribute first");
> + Nullability = static_cast<unsigned>(kind);
> + NullabilityLoc = loc;
> + }
> +
> const IdentifierInfo *getGetterName() const { return GetterName; }
> IdentifierInfo *getGetterName() { return GetterName; }
> void setGetterName(IdentifierInfo *name) { GetterName = name; }
> @@ -834,10 +862,15 @@ private:
> // FIXME: These two are unrelated and mutually exclusive. So perhaps
> // we can put them in a union to reflect their mutual exclusivity
> // (space saving is negligible).
> - ObjCDeclQualifier objcDeclQualifier : 6;
> + ObjCDeclQualifier objcDeclQualifier : 7;
>
> // NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind
> - unsigned PropertyAttributes : 12;
> + unsigned PropertyAttributes : 13;
> +
> + unsigned Nullability : 2;
> +
> + SourceLocation NullabilityLoc;
> +
> IdentifierInfo *GetterName; // getter name or NULL if no getter
> IdentifierInfo *SetterName; // setter name or NULL if no setter
> };
>
> Modified: cfe/trunk/include/clang/Sema/Sema.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/Sema.h (original)
> +++ cfe/trunk/include/clang/Sema/Sema.h Fri Jun 19 13:14:38 2015
> @@ -2859,6 +2859,26 @@ public:
> /// Valid types should not have multiple attributes with different CCs.
> const AttributedType *getCallingConvAttributedType(QualType T) const;
>
> + /// Check whether a nullability type specifier can be added to the given
> + /// type.
> + ///
> + /// \param type The type to which the nullability specifier will be
> + /// added. On success, this type will be updated appropriately.
> + ///
> + /// \param nullability The nullability specifier to add.
> + ///
> + /// \param nullabilityLoc The location of the nullability specifier.
> + ///
> + /// \param isContextSensitive Whether this nullability specifier was
> + /// written as a context-sensitive keyword (in an Objective-C
> + /// method) or an Objective-C property attribute, rather than as an
> + /// underscored type specifier.
> + ///
> + /// \returns true if nullability cannot be applied, false otherwise.
> + bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability,
> + SourceLocation nullabilityLoc,
> + bool isContextSensitive);
> +
> /// \brief Stmt attributes - this routine is the top level dispatcher.
> StmtResult ProcessStmtAttributes(Stmt *Stmt, AttributeList *Attrs,
> SourceRange Range);
> @@ -2934,7 +2954,8 @@ public:
> const unsigned Attributes,
> const unsigned AttributesAsWritten,
> bool *isOverridingProperty,
> - TypeSourceInfo *T,
> + QualType T,
> + TypeSourceInfo *TSI,
> tok::ObjCKeywordKind MethodImplKind);
>
> /// Called by ActOnProperty and HandlePropertyInClassExtension to
> @@ -2950,7 +2971,8 @@ public:
> const bool isReadWrite,
> const unsigned Attributes,
> const unsigned AttributesAsWritten,
> - TypeSourceInfo *T,
> + QualType T,
> + TypeSourceInfo *TSI,
> tok::ObjCKeywordKind MethodImplKind,
> DeclContext *lexicalDC = nullptr);
>
>
> Modified: cfe/trunk/lib/AST/ASTImporter.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/AST/ASTImporter.cpp (original)
> +++ cfe/trunk/lib/AST/ASTImporter.cpp Fri Jun 19 13:14:38 2015
> @@ -3922,8 +3922,8 @@ Decl *ASTNodeImporter::VisitObjCProperty
> }
>
> // 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::VisitObjCProperty
> 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);
>
> Modified: cfe/trunk/lib/AST/DeclObjC.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclObjC.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/AST/DeclObjC.cpp (original)
> +++ cfe/trunk/lib/AST/DeclObjC.cpp Fri Jun 19 13:14:38 2015
> @@ -1862,16 +1862,18 @@ ObjCPropertyDecl *ObjCPropertyDecl::Crea
> 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);
> }
>
> //===----------------------------------------------------------------------===//
>
> Modified: cfe/trunk/lib/AST/DeclPrinter.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclPrinter.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/AST/DeclPrinter.cpp (original)
> +++ cfe/trunk/lib/AST/DeclPrinter.cpp Fri Jun 19 13:14:38 2015
> @@ -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
> // 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(
> 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(
> 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 << ';';
>
> Modified: cfe/trunk/lib/Basic/IdentifierTable.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/IdentifierTable.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Basic/IdentifierTable.cpp (original)
> +++ cfe/trunk/lib/Basic/IdentifierTable.cpp Fri Jun 19 13:14:38 2015
> @@ -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(O
>
> 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";
> + }
> +}
>
> Modified: cfe/trunk/lib/Parse/ParseObjc.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseObjc.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Parse/ParseObjc.cpp (original)
> +++ cfe/trunk/lib/Parse/ParseObjc.cpp Fri Jun 19 13:14:38 2015
> @@ -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::ParseObjCAtInterfaceDeclar
> 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(
> 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(
> 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(
> 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(
> /// 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(
> 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(
>
> 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(
> 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(Obj
>
> // 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(Obj
> 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();
> + }
> }
> }
>
>
> Modified: cfe/trunk/lib/Parse/Parser.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Parse/Parser.cpp (original)
> +++ cfe/trunk/lib/Parse/Parser.cpp Fri Jun 19 13:14:38 2015
> @@ -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;
>
> Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Jun 19 13:14:38 2015
> @@ -2471,9 +2471,13 @@ static void mergeParamDeclTypes(ParmVarD
> 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);
> }
> }
>
> Modified: cfe/trunk/lib/Sema/SemaDeclObjC.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclObjC.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaDeclObjC.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDeclObjC.cpp Fri Jun 19 13:14:38 2015
> @@ -1366,6 +1366,13 @@ static SourceRange getTypeRange(TypeSour
> 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(Se
> 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(Se
> 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(Sem
> 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(Sem
>
> 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(ObjC
> 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);
>
> Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Fri Jun 19 13:14:38 2015
> @@ -1135,49 +1135,150 @@ ObjCMethodDecl *Sema::tryCaptureObjCSelf
> }
>
> 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);
> + }
Elide braces (here and elsewhere).
> + } 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.
>
> Modified: cfe/trunk/lib/Sema/SemaObjCProperty.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaObjCProperty.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaObjCProperty.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaObjCProperty.cpp Fri Jun 19 13:14:38 2015
> @@ -149,7 +149,6 @@ Decl *Sema::ActOnProperty(Scope *S, Sour
> 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, Sour
> isAssign, isReadWrite,
> Attributes,
> ODS.getPropertyAttributes(),
> - isOverridingProperty, TSI,
> + isOverridingProperty, T, TSI,
> MethodImplKind);
> if (!Res)
> return nullptr;
> @@ -184,7 +183,7 @@ Decl *Sema::ActOnProperty(Scope *S, Sour
> 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(Sco
> 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(Sco
> // 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(Sco
> 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(Sco
> 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::CreatePropertyDe
> 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::CreatePropertyDe
> 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::CreatePropertyDe
> 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(D
> 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)
>
> Modified: cfe/trunk/lib/Sema/SemaType.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaType.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaType.cpp Fri Jun 19 13:14:38 2015
> @@ -4522,34 +4522,10 @@ static bool handleMSPointerTypeQualifier
> 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(Ty
> 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(Ty
> // 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(Ty
> 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(Ty
> }
>
> // 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 Att
> 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(TypeProcess
> // 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();
> }
>
>
> Modified: cfe/trunk/lib/Sema/TreeTransform.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/TreeTransform.h (original)
> +++ cfe/trunk/lib/Sema/TreeTransform.h Fri Jun 19 13:14:38 2015
> @@ -5392,7 +5392,7 @@ QualType TreeTransform<Derived>::Transfo
> 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();
> }
> }
>
> Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
> +++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Fri Jun 19 13:14:38 2015
> @@ -1042,7 +1042,9 @@ void ASTDeclReader::VisitObjCPropertyDec
> 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++]);
>
> Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
> +++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Fri Jun 19 13:14:38 2015
> @@ -542,7 +542,7 @@ void ASTDeclWriter::VisitObjCMethodDecl(
>
> // 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::VisitObjCPropertyDec
> 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());
>
> Modified: cfe/trunk/test/Index/comment-objc-decls.m
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/comment-objc-decls.m?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/test/Index/comment-objc-decls.m (original)
> +++ cfe/trunk/test/Index/comment-objc-decls.m Fri Jun 19 13:14:38 2015
> @@ -20,19 +20,19 @@
> * \param[in] range output value is unsigned int
> * \result return index
> */
> -- (unsigned int)MethodMyProto:(id)anObject inRange:(unsigned int)range;
> +- (unsigned int)MethodMyProto:(nullable id)anObject inRange:(unsigned int)range;
> /**
> * \brief PropertyMyProto - This is protocol's property.
> */
> - at property (copy) id PropertyMyProto;
> + at property (copy, nonnull) id PropertyMyProto;
> /**
> * \brief ClassMethodMyProto
> */
> + ClassMethodMyProto;
> @end
> // CHECK: <Declaration>@protocol MyProto\n at end</Declaration>
> -// CHECK: <Declaration>- (unsigned int)MethodMyProto:(id)anObject inRange:(unsigned int)range;</Declaration>
> -// CHECK: <Declaration>@optional\n at property(readwrite, copy, atomic) id PropertyMyProto;</Declaration>
> +// CHECK: <Declaration>- (unsigned int)MethodMyProto:(nullable id)anObject inRange:(unsigned int)range;</Declaration>
> +// CHECK: <Declaration>@optional\n at property(readwrite, copy, atomic, nonnull) id PropertyMyProto;</Declaration>
> // CHECK: <Declaration>+ (id)ClassMethodMyProto;</Declaration>
>
> /**
>
> Modified: cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m (original)
> +++ cfe/trunk/test/SemaObjC/arc-property-decl-attrs.m Fri Jun 19 13:14:38 2015
> @@ -79,3 +79,11 @@
> @property (readwrite) id frr;
> @end
>
> +// rdar://20152386
> + at interface NSObject @end
> +
> + at interface rdar20152386_2: NSObject
> + at property(nonatomic, weak, nonnull) id delegate; // expected-error {{property attributes 'nonnull' and 'weak' are mutually exclusive}}
> + at property(nonatomic, weak, nonnull, readonly) id ReadDelegate; // no warning
> + at end
> +
>
> Modified: cfe/trunk/test/SemaObjC/arc-unavailable-for-weakref.m
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/arc-unavailable-for-weakref.m?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/test/SemaObjC/arc-unavailable-for-weakref.m (original)
> +++ cfe/trunk/test/SemaObjC/arc-unavailable-for-weakref.m Fri Jun 19 13:14:38 2015
> @@ -56,7 +56,7 @@ __attribute__((objc_arc_weak_reference_u
> @interface I
> {
> }
> - at property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont *', which does not support weak references}}
> + at property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont * __nullable', which does not support weak references}}
> @end
>
> @implementation I // expected-note {{when implemented by class I}}
> @@ -65,7 +65,7 @@ __attribute__((objc_arc_weak_reference_u
>
> // rdar://13676793
> @protocol MyProtocol
> - at property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont *', which does not support weak references}}
> + at property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont * __nullable', which does not support weak references}}
> @end
>
> @interface I1 <MyProtocol>
> @@ -76,7 +76,7 @@ __attribute__((objc_arc_weak_reference_u
> @end
>
> @interface Super
> - at property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont *', which does not support weak references}}
> + at property (weak) NSFont *font; // expected-error {{synthesizing __weak instance variable of type 'NSFont * __nullable', which does not support weak references}}
> @end
>
>
>
> Modified: cfe/trunk/test/SemaObjC/nullability.m
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/nullability.m?rev=240154&r1=240153&r2=240154&view=diff
> ==============================================================================
> --- cfe/trunk/test/SemaObjC/nullability.m (original)
> +++ cfe/trunk/test/SemaObjC/nullability.m Fri Jun 19 13:14:38 2015
> @@ -20,9 +20,6 @@ typedef __nonnull NSFoo * __nullable con
> void testBlocksPrinting(NSFoo * __nullable (^bp)(int)) {
> int *ip = bp; // expected-error{{'NSFoo * __nullable (^)(int)'}}
> }
> -void test_accepts_nonnull_null_pointer_literal(NSFoo *foo) {
> - [foo methodTakingIntPtr: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
> -}
>
> // Check returning nil from a __nonnull-returning method.
> @implementation NSFoo
> @@ -31,3 +28,151 @@ void test_accepts_nonnull_null_pointer_l
> return 0; // no warning
> }
> @end
> +
> +// Context-sensitive keywords and property attributes for nullability.
> +__attribute__((objc_root_class))
> + at interface NSBar
> +- (nonnull NSFoo *)methodWithFoo:(nonnull NSFoo *)foo;
> +
> +- (nonnull NSFoo **)invalidMethod1; // expected-error{{nullability keyword 'nonnull' cannot be applied to multi-level pointer type 'NSFoo **'}}
> +// expected-note at -1{{use nullability type specifier '__nonnull' to affect the innermost pointer type of 'NSFoo **'}}
> +- (nonnull NSFoo * __nullable)conflictingMethod1; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
> +- (nonnull NSFoo * __nonnull)redundantMethod1; // expected-warning{{duplicate nullability specifier '__nonnull'}}
> +
> + at property(nonnull,retain) NSFoo *property1;
> + at property(nullable,assign) NSFoo ** invalidProperty1; // expected-error{{nullability keyword 'nullable' cannot be applied to multi-level pointer type 'NSFoo **'}}
> +// expected-note at -1{{use nullability type specifier '__nullable' to affect the innermost pointer type of 'NSFoo **'}}
> + at property(null_unspecified,retain) NSFoo * __nullable conflictingProperty1; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__null_unspecified'}}
> + at property(retain,nonnull) NSFoo * __nonnull redundantProperty1; // expected-warning{{duplicate nullability specifier '__nonnull'}}
> +
> + at property(null_unspecified,retain,nullable) NSFoo *conflictingProperty3; // expected-error{{nullability specifier 'nullable' conflicts with existing specifier 'null_unspecified'}}
> + at property(nullable,retain,nullable) NSFoo *redundantProperty3; // expected-warning{{duplicate nullability specifier 'nullable'}}
> + at end
> +
> + at interface NSBar ()
> + at property(nonnull,retain) NSFoo *property2;
> + at property(nullable,assign) NSFoo ** invalidProperty2; // expected-error{{nullability keyword 'nullable' cannot be applied to multi-level pointer type 'NSFoo **'}}
> +// expected-note at -1{{use nullability type specifier '__nullable' to affect the innermost pointer type of 'NSFoo **'}}
> + at property(null_unspecified,retain) NSFoo * __nullable conflictingProperty2; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__null_unspecified'}}
> + at property(retain,nonnull) NSFoo * __nonnull redundantProperty2; // expected-warning{{duplicate nullability specifier '__nonnull'}}
> + at end
> +
> +void test_accepts_nonnull_null_pointer_literal(NSFoo *foo, __nonnull NSBar *bar) {
> + [foo methodTakingIntPtr: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
> + [bar methodWithFoo: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
> + bar.property1 = 0; // expected-warning{{null passed to a callee that requires a non-null argument}}
> + bar.property2 = 0; // expected-warning{{null passed to a callee that requires a non-null argument}}
> + [bar setProperty1: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
> + [bar setProperty2: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
> + int *ptr = bar.property1; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'NSFoo * __nonnull'}}
> +}
> +
> +// Check returning nil from a nonnull-returning method.
> + at implementation NSBar
> +- (nonnull NSFoo *)methodWithFoo:(nonnull NSFoo *)foo {
> + return 0; // no warning
> +}
> +
> +- (NSFoo **)invalidMethod1 {
> + return 0;
> +}
> +
> +- (NSFoo *)conflictingMethod1 {
> + return 0; // no warning
> +}
> +- (NSFoo *)redundantMethod1 {
> + int *ip = 0;
> + return ip; // expected-warning{{result type 'NSFoo * __nonnull'}}
> +}
> + at end
> +
> +__attribute__((objc_root_class))
> + at interface NSMerge
> +- (nonnull NSFoo *)methodA:(nonnull NSFoo*)foo;
> +- (nonnull NSFoo *)methodB:(nonnull NSFoo*)foo;
> +- (NSFoo *)methodC:(NSFoo*)foo;
> + at end
> +
> + at implementation NSMerge
> +- (NSFoo *)methodA:(NSFoo*)foo {
> + int *ptr = foo; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'NSFoo * __nonnull'}}
> + return ptr; // expected-warning{{result type 'NSFoo * __nonnull'}}
> +}
> +
> +- (nullable NSFoo *)methodB:(null_unspecified NSFoo*)foo { // expected-error{{nullability specifier 'nullable' conflicts with existing specifier 'nonnull'}} \
> + // expected-error{{nullability specifier 'null_unspecified' conflicts with existing specifier 'nonnull'}}
> + return 0;
> +}
> +
> +- (nonnull NSFoo *)methodC:(nullable NSFoo*)foo {
> + int *ip = 0;
> + return ip; // expected-warning{{result type 'NSFoo * __nonnull'}}
> +}
> + at end
> +
> +// Checking merging of nullability when sending a message.
> + at interface NSMergeReceiver
> +- (id)returnsNone;
> +- (nonnull id)returnsNonNull;
> +- (nullable id)returnsNullable;
> +- (null_unspecified id)returnsNullUnspecified;
> + at end
> +
> +void test_receiver_merge(NSMergeReceiver *none,
> + __nonnull NSMergeReceiver *nonnull,
> + __nullable NSMergeReceiver *nullable,
> + __null_unspecified NSMergeReceiver *null_unspecified) {
> + int *ptr;
> +
> + ptr = [nullable returnsNullable]; // expected-warning{{'id __nullable'}}
> + ptr = [nullable returnsNullUnspecified]; // expected-warning{{'id __nullable'}}
> + ptr = [nullable returnsNonNull]; // expected-warning{{'id __nullable'}}
> + ptr = [nullable returnsNone]; // expected-warning{{'id __nullable'}}
> +
> + ptr = [null_unspecified returnsNullable]; // expected-warning{{'id __nullable'}}
> + ptr = [null_unspecified returnsNullUnspecified]; // expected-warning{{'id __null_unspecified'}}
> + ptr = [null_unspecified returnsNonNull]; // expected-warning{{'id __null_unspecified'}}
> + ptr = [null_unspecified returnsNone]; // expected-warning{{'id'}}
> +
> + ptr = [nonnull returnsNullable]; // expected-warning{{'id __nullable'}}
> + ptr = [nonnull returnsNullUnspecified]; // expected-warning{{'id __null_unspecified'}}
> + ptr = [nonnull returnsNonNull]; // expected-warning{{'id __nonnull'}}
> + ptr = [nonnull returnsNone]; // expected-warning{{'id'}}
> +
> + ptr = [none returnsNullable]; // expected-warning{{'id __nullable'}}
> + ptr = [none returnsNullUnspecified]; // expected-warning{{'id'}}
> + ptr = [none returnsNonNull]; // expected-warning{{'id'}}
> + ptr = [none returnsNone]; // expected-warning{{'id'}}
> +
> +}
> +
> +// instancetype
> + at protocol Initializable
> +- (instancetype)initWithBlah:(id)blah;
> + at end
> +
> +__attribute__((objc_root_class))
> + at interface InitializableClass <Initializable>
> +- (nonnull instancetype)initWithBlah:(nonnull id)blah;
> +- (nullable instancetype)returnMe;
> ++ (nullable instancetype)returnInstanceOfMe;
> + at end
> +
> +void test_instancetype(InitializableClass * __nonnull ic, id __nonnull object) {
> + int *ip = [ic returnMe]; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'InitializableClass * __nullable'}}
> + ip = [InitializableClass returnMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'id __nullable'}}
> + ip = [InitializableClass returnInstanceOfMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'InitializableClass * __nullable'}}
> + ip = [object returnMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'id __nullable'}}
> +}
> +// rdar://problem/19814852
> + at interface MultiProp
> + at property (nullable, copy) id a, b, c;
> + at property (nullable, copy) MultiProp *d, *(^e)(int);
> + at end
> +
> +void testMultiProp(MultiProp *foo) {
> + int *ip;
> + ip = foo.a; // expected-warning{{from 'id __nullable'}}
> + ip = foo.d; // expected-warning{{from 'MultiProp * __nullable'}}
> + ip = foo.e; // expected-error{{incompatible type 'MultiProp *(^ __nullable)(int)'}}
> +}
>
> Added: cfe/trunk/test/SemaObjC/nullable-weak-property.m
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/nullable-weak-property.m?rev=240154&view=auto
> ==============================================================================
> --- cfe/trunk/test/SemaObjC/nullable-weak-property.m (added)
> +++ cfe/trunk/test/SemaObjC/nullable-weak-property.m Fri Jun 19 13:14:38 2015
> @@ -0,0 +1,18 @@
> +// RUN: %clang_cc1 -fobjc-arc -fobjc-runtime-has-weak -Wnullable-to-nonnull-conversion %s -verify
> +
> +
> +// rdar://19985330
> + at interface NSObject @end
> +
> + at class NSFoo;
> +void foo (NSFoo * __nonnull);
> +
> + at interface NSBar : NSObject
> + at property(weak) NSFoo *property1;
> + at end
> +
> + at implementation NSBar
> +- (void) Meth {
> + foo (self.property1); // expected-warning {{implicit conversion from nullable pointer 'NSFoo * __nullable' to non-nullable pointer type 'NSFoo * __nonnull'}}
> +}
> + at end
>
> Added: cfe/trunk/test/SemaObjC/override-nullability.m
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/override-nullability.m?rev=240154&view=auto
> ==============================================================================
> --- cfe/trunk/test/SemaObjC/override-nullability.m (added)
> +++ cfe/trunk/test/SemaObjC/override-nullability.m Fri Jun 19 13:14:38 2015
> @@ -0,0 +1,15 @@
> +// RUN: %clang_cc1 -fobjc-arc -fobjc-runtime-has-weak -Wnonnull %s -verify
> +//rdar://19211059
> +
> + at interface NSObject @end
> +
> + at interface Base : NSObject
> +- (nonnull id)bad:(nullable id)obj; // expected-note 2 {{previous declaration is here}}
> +- (nullable id)notAsBad:(nonnull id)obj;
> + at end
> +
> + at interface Sub : Base
> +- (nullable id)bad:(nonnull id)obj; // expected-warning {{conflicting nullability specifier on return types, 'nullable' conflicts with existing specifier 'nonnull'}} \
> + // expected-warning {{conflicting nullability specifier on parameter types, 'nonnull' conflicts with existing specifier 'nullable'}}
> +- (nonnull id)notAsBad:(nullable id)obj;
> + at end
>
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
~Aaron
More information about the cfe-commits
mailing list