r240146 - Introduce type nullability specifiers for C/C++.
Aaron Ballman
aaron at aaronballman.com
Fri Jun 19 11:10:53 PDT 2015
On Fri, Jun 19, 2015 at 1:51 PM, Douglas Gregor <dgregor at apple.com> wrote:
> Author: dgregor
> Date: Fri Jun 19 12:51:05 2015
> New Revision: 240146
>
> URL: http://llvm.org/viewvc/llvm-project?rev=240146&view=rev
> Log:
> Introduce type nullability specifiers for C/C++.
>
> Introduces the type specifiers __nonnull, __nullable, and
> __null_unspecified that describe the nullability of the pointer type
> to which the specifier appertains. Nullability type specifiers improve
> on the existing nonnull attributes in a few ways:
> - They apply to types, so one can represent a pointer to a non-null
> pointer, use them in function pointer types, etc.
> - As type specifiers, they are syntactically more lightweight than
> __attribute__s or [[attribute]]s.
> - They can express both the notion of 'should never be null' and
> also 'it makes sense for this to be null', and therefore can more
> easily catch errors of omission where one forgot to annotate the
> nullability of a particular pointer (this will come in a subsequent
> patch).
>
> Nullability type specifiers are maintained as type sugar, and
> therefore have no effect on mangling, encoding, overloading,
> etc. Nonetheless, they will be used for warnings about, e.g., passing
> 'null' to a method that does not accept it.
>
> This is the C/C++ part of rdar://problem/18868820.
This is great stuff! I have some trivial comments below.
>
> Added:
> cfe/trunk/test/FixIt/fixit-nullability-declspec.cpp (with props)
> cfe/trunk/test/Parser/nullability.c (with props)
> cfe/trunk/test/Sema/non-null-warning.c (with props)
> cfe/trunk/test/Sema/nullability.c (with props)
> cfe/trunk/test/SemaCXX/nullability-declspec.cpp (with props)
> cfe/trunk/test/SemaCXX/nullability.cpp (with props)
> cfe/trunk/test/SemaObjC/nullability.m
> Modified:
> cfe/trunk/include/clang/AST/Type.h
> cfe/trunk/include/clang/Basic/Attr.td
> cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td
> cfe/trunk/include/clang/Basic/DiagnosticGroups.td
> cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
> cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
> cfe/trunk/include/clang/Basic/Specifiers.h
> cfe/trunk/include/clang/Basic/TokenKinds.def
> cfe/trunk/include/clang/Parse/Parser.h
> cfe/trunk/lib/AST/Type.cpp
> cfe/trunk/lib/AST/TypePrinter.cpp
> cfe/trunk/lib/Lex/PPMacroExpansion.cpp
> cfe/trunk/lib/Parse/ParseDecl.cpp
> cfe/trunk/lib/Parse/ParseTentative.cpp
> cfe/trunk/lib/Sema/SemaDecl.cpp
> cfe/trunk/lib/Sema/SemaType.cpp
> cfe/trunk/lib/Sema/TreeTransform.h
>
> Modified: cfe/trunk/include/clang/AST/Type.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/AST/Type.h (original)
> +++ cfe/trunk/include/clang/AST/Type.h Fri Jun 19 12:51:05 2015
> @@ -1818,6 +1818,19 @@ public:
> /// checking. Should always return true.
> bool isLinkageValid() const;
>
> + /// Determine the nullability of the given type.
> + ///
> + /// Note that nullability is only captured as sugar within the type
> + /// system, not as part of the canonical type, so nullability will
> + /// be lost by canonicalization and desugaring.
> + Optional<NullabilityKind> getNullability(const ASTContext &context) const;
> +
> + /// Determine whether the given type can have a nullability
> + /// specifier applied to it, i.e., if it is any kind of pointer type
> + /// or a dependent type that could instantiate to any kind of
> + /// pointer type.
> + bool canHaveNullability() const;
> +
> const char *getTypeClassName() const;
>
> QualType getCanonicalTypeInternal() const {
> @@ -3479,7 +3492,10 @@ public:
> attr_ptr32,
> attr_ptr64,
> attr_sptr,
> - attr_uptr
> + attr_uptr,
> + attr_nonnull,
> + attr_nullable,
> + attr_null_unspecified,
> };
>
> private:
> @@ -3513,6 +3529,34 @@ public:
>
> bool isCallingConv() const;
>
> + llvm::Optional<NullabilityKind> getImmediateNullability() const;
> +
> + /// Retrieve the attribute kind corresponding to the given
> + /// nullability kind.
> + static Kind getNullabilityAttrKind(NullabilityKind kind) {
> + switch (kind) {
> + case NullabilityKind::NonNull:
> + return attr_nonnull;
> +
> + case NullabilityKind::Nullable:
> + return attr_nullable;
> +
> + case NullabilityKind::Unspecified:
> + return attr_null_unspecified;
> + }
> + }
> +
> + /// Strip off the top-level nullability annotation on the given
> + /// type, if it's there.
> + ///
> + /// \param T The type to strip. If the type is exactly an
> + /// AttributedType specifying nullability (without looking through
> + /// type sugar), the nullability is returned and this type changed
> + /// to the underlying modified type.
> + ///
> + /// \returns the top-level nullability, if present.
> + static Optional<NullabilityKind> stripOuterNullability(QualType &T);
> +
> void Profile(llvm::FoldingSetNodeID &ID) {
> Profile(ID, getAttrKind(), ModifiedType, EquivalentType);
> }
>
> Modified: cfe/trunk/include/clang/Basic/Attr.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/Attr.td (original)
> +++ cfe/trunk/include/clang/Basic/Attr.td Fri Jun 19 12:51:05 2015
> @@ -960,6 +960,22 @@ def ReturnsNonNull : InheritableAttr {
> let Documentation = [Undocumented];
> }
>
> +// Nullability type attributes.
> +def TypeNonNull : TypeAttr {
> + let Spellings = [Keyword<"__nonnull">];
> + let Documentation = [Undocumented];
> +}
> +
> +def TypeNullable : TypeAttr {
> + let Spellings = [Keyword<"__nullable">];
> + let Documentation = [Undocumented];
> +}
> +
> +def TypeNullUnspecified : TypeAttr {
> + let Spellings = [Keyword<"__null_unspecified">];
> + let Documentation = [Undocumented];
> +}
Please add documentation for these type attributes (we frown on new
attributes that are flagged as Undocumented).
> +
> def AssumeAligned : InheritableAttr {
> let Spellings = [GCC<"assume_aligned">];
> let Subjects = SubjectList<[ObjCMethod, Function]>;
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td Fri Jun 19 12:51:05 2015
> @@ -99,6 +99,20 @@ 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'">,
> + InGroup<Nullability>;
> +
> +def note_nullability_here : Note<
> + "'%select{__nonnull|__nullable|__null_unspecified}0' specified here">;
> +
> +}
> +
> // Sema && Lex
> def ext_c99_longlong : Extension<
> "'long long' is an extension when C99 mode is not enabled">,
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Fri Jun 19 12:51:05 2015
> @@ -248,6 +248,8 @@ def MissingFieldInitializers : DiagGroup
> def ModuleBuild : DiagGroup<"module-build">;
> def ModuleConflict : DiagGroup<"module-conflict">;
> def NewlineEOF : DiagGroup<"newline-eof">;
> +def Nullability : DiagGroup<"nullability">;
> +def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
> def NullArithmetic : DiagGroup<"null-arithmetic">;
> def NullCharacter : DiagGroup<"null-character">;
> def NullDereference : DiagGroup<"null-dereference">;
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Fri Jun 19 12:51:05 2015
> @@ -65,6 +65,10 @@ def ext_keyword_as_ident : ExtWarn<
> "%select{here|for the remainder of the translation unit}1">,
> InGroup<KeywordCompat>;
>
> +def ext_nullability : Extension<
> + "type nullability specifier %0 is a Clang extension">,
> + InGroup<DiagGroup<"nullability-extension">>;
Please have %0 quoted in the diagnostic.
> +
> def error_empty_enum : Error<"use of empty enum">;
> def err_invalid_sign_spec : Error<"'%0' cannot be signed or unsigned">;
> def err_invalid_short_spec : Error<"'short %0' is invalid">;
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jun 19 12:51:05 2015
> @@ -7674,4 +7674,32 @@ def warn_profile_data_unprofiled : Warni
>
> } // end of instrumentation issue category
>
> +let CategoryName = "Nullability Issue" in {
> +
> +def warn_duplicate_nullability : Warning<
> + "duplicate nullability specifier "
> + "'%select{__nonnull|__nullable|__null_unspecified}0'">,
> + InGroup<Nullability>;
> +
> +def warn_nullability_declspec : Warning<
> + "nullability specifier "
> + "'%select{__nonnull|__nullable|__null_unspecified}0' cannot be applied "
> + "to non-pointer type %1; did you mean to apply the specifier to the "
> + "%select{pointer|block pointer|member pointer|function pointer|"
> + "member function pointer}2?">,
> + 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 err_nullability_conflicting : Error<
> + "nullability specifier "
> + "'%select{__nonnull|__nullable|__null_unspecified}0' conflicts with existing "
> + "specifier '%select{__nonnull|__nullable|__null_unspecified}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=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/Specifiers.h (original)
> +++ cfe/trunk/include/clang/Basic/Specifiers.h Fri Jun 19 12:51:05 2015
> @@ -16,6 +16,8 @@
> #ifndef LLVM_CLANG_BASIC_SPECIFIERS_H
> #define LLVM_CLANG_BASIC_SPECIFIERS_H
>
> +#include "llvm/Support/DataTypes.h"
> +
> namespace clang {
> /// \brief Specifies the width of a type, e.g., short, long, or long long.
> enum TypeSpecifierWidth {
> @@ -239,6 +241,19 @@ namespace clang {
> SD_Static, ///< Static storage duration.
> SD_Dynamic ///< Dynamic storage duration.
> };
> +
> + /// Describes the nullability of a particular type.
> + enum class NullabilityKind : uint8_t {
> + /// Values of this type can never be null.
> + NonNull = 0,
> + /// Values of this type can be null.
> + Nullable,
> + /// Whether values of this type can be null is (explicitly)
> + /// unspecified. This captures a (fairly rare) case where we
> + /// can't conclude anything about the nullability of the type even
> + /// though it has been considered.
> + Unspecified
> + };
> } // end namespace clang
>
> #endif // LLVM_CLANG_BASIC_SPECIFIERS_H
>
> Modified: cfe/trunk/include/clang/Basic/TokenKinds.def
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/TokenKinds.def?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/TokenKinds.def (original)
> +++ cfe/trunk/include/clang/Basic/TokenKinds.def Fri Jun 19 12:51:05 2015
> @@ -548,6 +548,11 @@ ALIAS("__typeof__" , typeof , KEYA
> ALIAS("__volatile" , volatile , KEYALL)
> ALIAS("__volatile__" , volatile , KEYALL)
>
> +// Type nullability.
> +KEYWORD(__nonnull , KEYALL)
> +KEYWORD(__nullable , KEYALL)
> +KEYWORD(__null_unspecified , KEYALL)
> +
> // Microsoft extensions which should be disabled in strict conformance mode
> KEYWORD(__ptr64 , KEYMS)
> KEYWORD(__ptr32 , KEYMS)
>
> Modified: cfe/trunk/include/clang/Parse/Parser.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Parse/Parser.h (original)
> +++ cfe/trunk/include/clang/Parse/Parser.h Fri Jun 19 12:51:05 2015
> @@ -2106,6 +2106,7 @@ private:
> void ParseBorlandTypeAttributes(ParsedAttributes &attrs);
> void ParseOpenCLAttributes(ParsedAttributes &attrs);
> void ParseOpenCLQualifiers(ParsedAttributes &Attrs);
> + void ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs);
>
> VersionTuple ParseVersionTuple(SourceRange &Range);
> void ParseAvailabilityAttribute(IdentifierInfo &Availability,
>
> Modified: cfe/trunk/lib/AST/Type.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Type.cpp?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/lib/AST/Type.cpp (original)
> +++ cfe/trunk/lib/AST/Type.cpp Fri Jun 19 12:51:05 2015
> @@ -1919,7 +1919,10 @@ bool AttributedType::isCallingConv() con
> case attr_objc_gc:
> case attr_objc_ownership:
> case attr_noreturn:
> - return false;
> + case attr_nonnull:
> + case attr_nullable:
> + case attr_null_unspecified:
> + return false;
> case attr_pcs:
> case attr_pcs_vfp:
> case attr_cdecl:
> @@ -2340,6 +2343,152 @@ LinkageInfo Type::getLinkageAndVisibilit
> return LV;
> }
>
> +Optional<NullabilityKind> Type::getNullability(const ASTContext &context) const {
> + QualType type(this, 0);
> + do {
> + // Check whether this is an attributed type with nullability
> + // information.
> + if (auto attributed = dyn_cast<AttributedType>(type.getTypePtr())) {
> + if (auto nullability = attributed->getImmediateNullability())
> + return nullability;
> + }
> +
> + // Desugar the type. If desugaring does nothing, we're done.
> + QualType desugared = type.getSingleStepDesugaredType(context);
> + if (desugared.getTypePtr() == type.getTypePtr())
> + return None;
> +
> + type = desugared;
> + } while (true);
> +}
> +
> +bool Type::canHaveNullability() const {
> + QualType type = getCanonicalTypeInternal();
> +
> + switch (type->getTypeClass()) {
> + // We'll only see canonical types here.
> +#define NON_CANONICAL_TYPE(Class, Parent) \
> + case Type::Class: \
> + llvm_unreachable("non-canonical type");
> +#define TYPE(Class, Parent)
> +#include "clang/AST/TypeNodes.def"
> +
> + // Pointer types.
> + case Type::Pointer:
> + case Type::BlockPointer:
> + case Type::MemberPointer:
> + case Type::ObjCObjectPointer:
> + return true;
> +
> + // Dependent types that could instantiate to pointer types.
> + case Type::UnresolvedUsing:
> + case Type::TypeOfExpr:
> + case Type::TypeOf:
> + case Type::Decltype:
> + case Type::UnaryTransform:
> + case Type::TemplateTypeParm:
> + case Type::SubstTemplateTypeParmPack:
> + case Type::DependentName:
> + case Type::DependentTemplateSpecialization:
> + return true;
> +
> + // Dependent template specializations can instantiate to pointer
> + // types unless they're known to be specializations of a class
> + // template.
> + case Type::TemplateSpecialization:
> + if (TemplateDecl *templateDecl
> + = cast<TemplateSpecializationType>(type.getTypePtr())
> + ->getTemplateName().getAsTemplateDecl()) {
> + if (isa<ClassTemplateDecl>(templateDecl))
> + return false;
> + }
> + return true;
> +
> + // auto is considered dependent when it isn't deduced.
> + case Type::Auto:
> + return !cast<AutoType>(type.getTypePtr())->isDeduced();
> +
> + case Type::Builtin:
> + switch (cast<BuiltinType>(type.getTypePtr())->getKind()) {
> + // Signed, unsigned, and floating-point types cannot have nullability.
> +#define SIGNED_TYPE(Id, SingletonId) case BuiltinType::Id:
> +#define UNSIGNED_TYPE(Id, SingletonId) case BuiltinType::Id:
> +#define FLOATING_TYPE(Id, SingletonId) case BuiltinType::Id:
> +#define BUILTIN_TYPE(Id, SingletonId)
> +#include "clang/AST/BuiltinTypes.def"
> + return false;
> +
> + // Dependent types that could instantiate to a pointer type.
> + case BuiltinType::Dependent:
> + case BuiltinType::Overload:
> + case BuiltinType::BoundMember:
> + case BuiltinType::PseudoObject:
> + case BuiltinType::UnknownAny:
> + case BuiltinType::ARCUnbridgedCast:
> + return true;
> +
> + case BuiltinType::Void:
> + case BuiltinType::ObjCId:
> + case BuiltinType::ObjCClass:
> + case BuiltinType::ObjCSel:
> + case BuiltinType::OCLImage1d:
> + case BuiltinType::OCLImage1dArray:
> + case BuiltinType::OCLImage1dBuffer:
> + case BuiltinType::OCLImage2d:
> + case BuiltinType::OCLImage2dArray:
> + case BuiltinType::OCLImage3d:
> + case BuiltinType::OCLSampler:
> + case BuiltinType::OCLEvent:
> + case BuiltinType::BuiltinFn:
> + case BuiltinType::NullPtr:
> + return false;
> + }
> +
> + // Non-pointer types.
> + case Type::Complex:
> + case Type::LValueReference:
> + case Type::RValueReference:
> + case Type::ConstantArray:
> + case Type::IncompleteArray:
> + case Type::VariableArray:
> + case Type::DependentSizedArray:
> + case Type::DependentSizedExtVector:
> + case Type::Vector:
> + case Type::ExtVector:
> + case Type::FunctionProto:
> + case Type::FunctionNoProto:
> + case Type::Record:
> + case Type::Enum:
> + case Type::InjectedClassName:
> + case Type::PackExpansion:
> + case Type::ObjCObject:
> + case Type::ObjCInterface:
> + case Type::Atomic:
> + return false;
> + }
> +}
> +
> +llvm::Optional<NullabilityKind> AttributedType::getImmediateNullability() const {
> + if (getAttrKind() == AttributedType::attr_nonnull)
> + return NullabilityKind::NonNull;
> + if (getAttrKind() == AttributedType::attr_nullable)
> + return NullabilityKind::Nullable;
> + if (getAttrKind() == AttributedType::attr_null_unspecified)
> + return NullabilityKind::Unspecified;
> + return None;
> +}
> +
> +Optional<NullabilityKind> AttributedType::stripOuterNullability(QualType &T) {
> + if (auto attributed = dyn_cast<AttributedType>(T.getTypePtr())) {
> + if (auto nullability = attributed->getImmediateNullability()) {
> + T = attributed->getModifiedType();
> + return nullability;
> + }
> + }
> +
> + return None;
> +}
> +
> Qualifiers::ObjCLifetime Type::getObjCARCImplicitLifetime() const {
> if (isObjCARCImplicitlyUnretainedType())
> return Qualifiers::OCL_ExplicitNone;
>
> Modified: cfe/trunk/lib/AST/TypePrinter.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TypePrinter.cpp?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/lib/AST/TypePrinter.cpp (original)
> +++ cfe/trunk/lib/AST/TypePrinter.cpp Fri Jun 19 12:51:05 2015
> @@ -1141,6 +1141,21 @@ void TypePrinter::printAttributedBefore(
> }
> spaceBeforePlaceHolder(OS);
> }
> +
> + // Print nullability type specifiers.
> + if (T->getAttrKind() == AttributedType::attr_nonnull ||
> + T->getAttrKind() == AttributedType::attr_nullable ||
> + T->getAttrKind() == AttributedType::attr_null_unspecified) {
> + if (T->getAttrKind() == AttributedType::attr_nonnull)
> + OS << " __nonnull";
> + else if (T->getAttrKind() == AttributedType::attr_nullable)
> + OS << " __nullable";
> + else if (T->getAttrKind() == AttributedType::attr_null_unspecified)
> + OS << " __null_unspecified";
> + else
> + llvm_unreachable("unhandled nullability");
> + spaceBeforePlaceHolder(OS);
> + }
> }
>
> void TypePrinter::printAttributedAfter(const AttributedType *T,
> @@ -1154,12 +1169,34 @@ void TypePrinter::printAttributedAfter(c
> if (T->isMSTypeSpec())
> return;
>
> + // Nothing to print after.
> + if (T->getAttrKind() == AttributedType::attr_nonnull ||
> + T->getAttrKind() == AttributedType::attr_nullable ||
> + T->getAttrKind() == AttributedType::attr_null_unspecified)
> + return printAfter(T->getModifiedType(), OS);
> +
> // If this is a calling convention attribute, don't print the implicit CC from
> // the modified type.
> SaveAndRestore<bool> MaybeSuppressCC(InsideCCAttribute, T->isCallingConv());
>
> printAfter(T->getModifiedType(), OS);
>
> + // Print nullability type specifiers that occur after
> + if (T->getAttrKind() == AttributedType::attr_nonnull ||
> + T->getAttrKind() == AttributedType::attr_nullable ||
> + T->getAttrKind() == AttributedType::attr_null_unspecified) {
> + if (T->getAttrKind() == AttributedType::attr_nonnull)
> + OS << " __nonnull";
> + else if (T->getAttrKind() == AttributedType::attr_nullable)
> + OS << " __nullable";
> + else if (T->getAttrKind() == AttributedType::attr_null_unspecified)
> + OS << " __null_unspecified";
> + else
> + llvm_unreachable("unhandled nullability");
> +
> + return;
> + }
> +
> OS << " __attribute__((";
> switch (T->getAttrKind()) {
> default: llvm_unreachable("This attribute should have been handled already");
>
> Modified: cfe/trunk/lib/Lex/PPMacroExpansion.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPMacroExpansion.cpp?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Lex/PPMacroExpansion.cpp (original)
> +++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp Fri Jun 19 12:51:05 2015
> @@ -1075,6 +1075,7 @@ static bool HasFeature(const Preprocesso
> .Case("cxx_exceptions", LangOpts.CXXExceptions)
> .Case("cxx_rtti", LangOpts.RTTI)
> .Case("enumerator_attributes", true)
> + .Case("nullability", LangOpts.ObjC1 || LangOpts.GNUMode)
> .Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory))
> .Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread))
> .Case("dataflow_sanitizer", LangOpts.Sanitize.has(SanitizerKind::DataFlow))
> @@ -1222,6 +1223,7 @@ static bool HasExtension(const Preproces
> // Because we inherit the feature list from HasFeature, this string switch
> // must be less restrictive than HasFeature's.
> return llvm::StringSwitch<bool>(Extension)
> + .Case("nullability", true)
> // C11 features supported by other languages as extensions.
> .Case("c_alignas", true)
> .Case("c_alignof", true)
>
> Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
> +++ cfe/trunk/lib/Parse/ParseDecl.cpp Fri Jun 19 12:51:05 2015
> @@ -687,6 +687,28 @@ void Parser::ParseOpenCLQualifiers(Parse
> AttributeList::AS_Keyword);
> }
>
> +void Parser::ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs) {
> + // Treat these like attributes, even though they're type specifiers.
> + while (true) {
> + switch (Tok.getKind()) {
> + case tok::kw___nonnull:
> + case tok::kw___nullable:
> + case tok::kw___null_unspecified: {
> + IdentifierInfo *AttrName = Tok.getIdentifierInfo();
> + SourceLocation AttrNameLoc = ConsumeToken();
> + if (!getLangOpts().ObjC1)
> + Diag(AttrNameLoc, diag::ext_nullability)
> + << AttrName;
> + attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0,
> + AttributeList::AS_Keyword);
> + break;
> + }
> + default:
> + return;
Would it be more clear to move the default to be the first label in
the switch, and have it break instead of return (in case we would like
to do post-processing of these at some point)?
> + }
> + }
> +}
> +
> static bool VersionNumberSeparator(const char Separator) {
> return (Separator == '.' || Separator == '_');
> }
> @@ -3040,6 +3062,13 @@ void Parser::ParseDeclarationSpecifiers(
> ParseOpenCLAttributes(DS.getAttributes());
> continue;
>
> + // Nullability type specifiers.
> + case tok::kw___nonnull:
> + case tok::kw___nullable:
> + case tok::kw___null_unspecified:
> + ParseNullabilityTypeSpecifiers(DS.getAttributes());
> + continue;
> +
> // storage-class-specifier
> case tok::kw_typedef:
> isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_typedef, Loc,
> @@ -4284,6 +4313,10 @@ bool Parser::isTypeSpecifierQualifier()
> case tok::kw___pascal:
> case tok::kw___unaligned:
>
> + case tok::kw___nonnull:
> + case tok::kw___nullable:
> + case tok::kw___null_unspecified:
> +
> case tok::kw___private:
> case tok::kw___local:
> case tok::kw___global:
> @@ -4457,6 +4490,10 @@ bool Parser::isDeclarationSpecifier(bool
> case tok::kw___pascal:
> case tok::kw___unaligned:
>
> + case tok::kw___nonnull:
> + case tok::kw___nullable:
> + case tok::kw___null_unspecified:
> +
> case tok::kw___private:
> case tok::kw___local:
> case tok::kw___global:
> @@ -4686,6 +4723,14 @@ void Parser::ParseTypeQualifierListOpt(D
> continue;
> }
> goto DoneWithTypeQuals;
> +
> + // Nullability type specifiers.
> + case tok::kw___nonnull:
> + case tok::kw___nullable:
> + case tok::kw___null_unspecified:
> + ParseNullabilityTypeSpecifiers(DS.getAttributes());
> + continue;
> +
> case tok::kw___attribute:
> if (AttrReqs & AR_GNUAttributesParsedAndRejected)
> // When GNU attributes are expressly forbidden, diagnose their usage.
>
> Modified: cfe/trunk/lib/Parse/ParseTentative.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTentative.cpp?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Parse/ParseTentative.cpp (original)
> +++ cfe/trunk/lib/Parse/ParseTentative.cpp Fri Jun 19 12:51:05 2015
> @@ -631,7 +631,9 @@ Parser::TPResult Parser::TryParsePtrOper
> (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
> // ptr-operator
> ConsumeToken();
> - while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict))
> + while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict,
> + tok::kw___nonnull, tok::kw___nullable,
> + tok::kw___null_unspecified))
> ConsumeToken();
> } else {
> return TPResult::True;
> @@ -1274,6 +1276,9 @@ Parser::isCXXDeclarationSpecifier(Parser
> case tok::kw___ptr32:
> case tok::kw___forceinline:
> case tok::kw___unaligned:
> + case tok::kw___nonnull:
> + case tok::kw___nullable:
> + case tok::kw___null_unspecified:
> return TPResult::True;
>
> // Borland
>
> Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Jun 19 12:51:05 2015
> @@ -2465,6 +2465,28 @@ static void mergeParamDeclAttributes(Par
> if (!foundAny) newDecl->dropAttrs();
> }
>
> +static void mergeParamDeclTypes(ParmVarDecl *NewParam,
> + const ParmVarDecl *OldParam,
> + Sema &S) {
> + if (auto Oldnullability = OldParam->getType()->getNullability(S.Context)) {
> + if (auto Newnullability = NewParam->getType()->getNullability(S.Context)) {
Can we put the dereference into the assignment expression, since these
are only ever used as dereferenced?
> + if (*Oldnullability != *Newnullability) {
> + S.Diag(NewParam->getLocation(), diag::warn_mismatched_nullability_attr)
> + << static_cast<unsigned>(*Newnullability)
> + << static_cast<unsigned>(*Oldnullability);
> + S.Diag(OldParam->getLocation(), diag::note_previous_declaration);
> + }
> + }
> + else {
Formatting.
> + QualType NewT = NewParam->getType();
> + NewT = S.Context.getAttributedType(
> + AttributedType::getNullabilityAttrKind(*Oldnullability),
> + NewT, NewT);
> + NewParam->setType(NewT);
> + }
> + }
> +}
> +
> namespace {
>
> /// Used in MergeFunctionDecl to keep track of function parameters in
> @@ -3101,9 +3123,12 @@ bool Sema::MergeCompatibleFunctionDecls(
> // Merge attributes from the parameters. These can mismatch with K&R
> // declarations.
> if (New->getNumParams() == Old->getNumParams())
> - for (unsigned i = 0, e = New->getNumParams(); i != e; ++i)
> - mergeParamDeclAttributes(New->getParamDecl(i), Old->getParamDecl(i),
> - *this);
> + for (unsigned i = 0, e = New->getNumParams(); i != e; ++i) {
> + ParmVarDecl *NewParam = New->getParamDecl(i);
> + ParmVarDecl *OldParam = Old->getParamDecl(i);
> + mergeParamDeclAttributes(NewParam, OldParam, *this);
> + mergeParamDeclTypes(NewParam, OldParam, *this);
> + }
>
> if (getLangOpts().CPlusPlus)
> return MergeCXXFunctionDecl(New, Old, S);
>
> Modified: cfe/trunk/lib/Sema/SemaType.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaType.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaType.cpp Fri Jun 19 12:51:05 2015
> @@ -22,6 +22,7 @@
> #include "clang/AST/Expr.h"
> #include "clang/AST/TypeLoc.h"
> #include "clang/AST/TypeLocVisitor.h"
> +#include "clang/Lex/Preprocessor.h"
> #include "clang/Basic/PartialDiagnostic.h"
> #include "clang/Basic/TargetInfo.h"
> #include "clang/Parse/ParseDiagnostic.h"
> @@ -121,6 +122,12 @@ static void diagnoseBadTypeAttribute(Sem
> case AttributeList::AT_SPtr: \
> case AttributeList::AT_UPtr
>
> +// Nullability qualifiers.
> +#define NULLABILITY_TYPE_ATTRS_CASELIST \
> + case AttributeList::AT_TypeNonNull: \
> + case AttributeList::AT_TypeNullable: \
> + case AttributeList::AT_TypeNullUnspecified
> +
> namespace {
> /// An object which stores processing state for the entire
> /// GetTypeForDeclarator process.
> @@ -307,8 +314,12 @@ static bool handleObjCPointerTypeAttr(Ty
> ///
> /// \param i - a notional index which the search will start
> /// immediately inside
> +///
> +/// \param onlyBlockPointers Whether we should only look into block
> +/// pointer types (vs. all pointer types).
> static DeclaratorChunk *maybeMovePastReturnType(Declarator &declarator,
> - unsigned i) {
> + unsigned i,
> + bool onlyBlockPointers) {
> assert(i <= declarator.getNumTypeObjects());
>
> DeclaratorChunk *result = nullptr;
> @@ -329,20 +340,26 @@ static DeclaratorChunk *maybeMovePastRet
> return result;
>
> // If we do find a function declarator, scan inwards from that,
> - // looking for a block-pointer declarator.
> + // looking for a (block-)pointer declarator.
> case DeclaratorChunk::Function:
> for (--i; i != 0; --i) {
> - DeclaratorChunk &blockChunk = declarator.getTypeObject(i-1);
> - switch (blockChunk.Kind) {
> + DeclaratorChunk &ptrChunk = declarator.getTypeObject(i-1);
> + switch (ptrChunk.Kind) {
> case DeclaratorChunk::Paren:
> - case DeclaratorChunk::Pointer:
> case DeclaratorChunk::Array:
> case DeclaratorChunk::Function:
> case DeclaratorChunk::Reference:
> - case DeclaratorChunk::MemberPointer:
> continue;
> +
> + case DeclaratorChunk::MemberPointer:
> + case DeclaratorChunk::Pointer:
> + if (onlyBlockPointers)
> + continue;
> +
> + // fallthrough
> +
> case DeclaratorChunk::BlockPointer:
> - result = &blockChunk;
> + result = &ptrChunk;
> goto continue_outer;
> }
> llvm_unreachable("bad declarator chunk kind");
> @@ -382,7 +399,8 @@ static void distributeObjCPointerTypeAtt
> DeclaratorChunk *destChunk = nullptr;
> if (state.isProcessingDeclSpec() &&
> attr.getKind() == AttributeList::AT_ObjCOwnership)
> - destChunk = maybeMovePastReturnType(declarator, i - 1);
> + destChunk = maybeMovePastReturnType(declarator, i - 1,
> + /*onlyBlockPointers=*/true);
> if (!destChunk) destChunk = &chunk;
>
> moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
> @@ -398,7 +416,9 @@ static void distributeObjCPointerTypeAtt
> case DeclaratorChunk::Function:
> if (state.isProcessingDeclSpec() &&
> attr.getKind() == AttributeList::AT_ObjCOwnership) {
> - if (DeclaratorChunk *dest = maybeMovePastReturnType(declarator, i)) {
> + if (DeclaratorChunk *dest = maybeMovePastReturnType(
> + declarator, i,
> + /*onlyBlockPointers=*/true)) {
> moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
> dest->getAttrListRef());
> return;
> @@ -620,6 +640,10 @@ static void distributeTypeAttrsFromDecla
> // Microsoft type attributes cannot go after the declarator-id.
> continue;
>
> + NULLABILITY_TYPE_ATTRS_CASELIST:
> + // Nullability specifiers cannot go after the declarator-id.
> + continue;
> +
> default:
> break;
> }
> @@ -3495,6 +3519,12 @@ static AttributeList::Kind getAttrListKi
> return AttributeList::AT_SPtr;
> case AttributedType::attr_uptr:
> return AttributeList::AT_UPtr;
> + case AttributedType::attr_nonnull:
> + return AttributeList::AT_TypeNonNull;
> + case AttributedType::attr_nullable:
> + return AttributeList::AT_TypeNullable;
> + case AttributedType::attr_null_unspecified:
> + return AttributeList::AT_TypeNullUnspecified;
> }
> llvm_unreachable("unexpected attribute kind!");
> }
> @@ -4114,7 +4144,8 @@ static bool handleObjCOwnershipTypeAttr(
> // just be the return type of a block pointer.
> if (state.isProcessingDeclSpec()) {
> Declarator &D = state.getDeclarator();
> - if (maybeMovePastReturnType(D, D.getNumTypeObjects()))
> + if (maybeMovePastReturnType(D, D.getNumTypeObjects(),
> + /*onlyBlockPointers=*/true))
> return false;
> }
> }
> @@ -4491,6 +4522,205 @@ static bool 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);
> +
> + // Check for existing nullability attributes on the type.
> + QualType desugared = type;
> + while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {
> + // Check whether there is already a null
> + if (auto existingNullability = attributed->getImmediateNullability()) {
> + // Duplicated nullability.
> + if (nullability == *existingNullability) {
> + S.Diag(attr.getLoc(), diag::warn_duplicate_nullability)
> + << static_cast<unsigned>(nullability);
> + return true;
> + }
> +
> + // Conflicting nullability.
> + S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
> + << static_cast<unsigned>(nullability)
> + << static_cast<unsigned>(*existingNullability);
> + return true;
> + }
> +
> + desugared = attributed->getEquivalentType();
> + }
> +
> + // If there is already a different nullability specifier, complain.
> + // This (unlike the code above) looks through typedefs that might
> + // have nullability specifiers on them, which means we cannot
> + // provide a useful Fix-It.
> + if (auto existingNullability = desugared->getNullability(Context)) {
> + if (nullability != *existingNullability) {
> + S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
> + << static_cast<unsigned>(nullability)
> + << static_cast<unsigned>(*existingNullability);
> +
> + // Try to find the typedef with the existing nullability specifier.
> + if (auto typedefType = desugared->getAs<TypedefType>()) {
> + TypedefNameDecl *typedefDecl = typedefType->getDecl();
> + QualType underlyingType = typedefDecl->getUnderlyingType();
> + if (auto typedefNullability
> + = AttributedType::stripOuterNullability(underlyingType)) {
> + if (*typedefNullability == *existingNullability) {
> + S.Diag(typedefDecl->getLocation(), diag::note_nullability_here)
> + << static_cast<unsigned>(*existingNullability);
> + }
> + }
> + }
> +
> + return true;
> + }
> + }
> +
> + // If this definitely isn't a pointer type, reject the specifier.
> + if (!type->canHaveNullability()) {
> + S.Diag(attr.getLoc(), diag::err_nullability_nonpointer)
> + << static_cast<unsigned>(nullability) << type;
> + return true;
> + }
> +
> + // Form the attributed type.
> + AttributedType::Kind typeAttrKind;
> + switch (kind) {
> + case AttributeList::AT_TypeNonNull:
> + typeAttrKind = AttributedType::attr_nonnull;
> + break;
> +
> + case AttributeList::AT_TypeNullable:
> + typeAttrKind = AttributedType::attr_nullable;
> + break;
> +
> + case AttributeList::AT_TypeNullUnspecified:
> + typeAttrKind = AttributedType::attr_null_unspecified;
> + break;
> +
> + default:
> + llvm_unreachable("Not a nullability specifier");
> + }
> + type = S.Context.getAttributedType(typeAttrKind, type, type);
> + return false;
> +}
> +
> +/// Check whether there is a nullability attribute of any kind in the given
> +/// attribute list.
> +static bool hasNullabilityAttr(const AttributeList *attrs) {
> + for (const AttributeList *attr = attrs; attr;
> + attr = attr->getNext()) {
> + if (attr->getKind() == AttributeList::AT_TypeNonNull ||
> + attr->getKind() == AttributeList::AT_TypeNullable ||
> + attr->getKind() == AttributeList::AT_TypeNullUnspecified)
> + return true;
> + }
> +
> + return false;
> +}
> +
> +/// Distribute a nullability type attribute that cannot be applied to
> +/// the type specifier to a pointer, block pointer, or member pointer
> +/// declarator, complaining if necessary.
> +///
> +/// \returns true if the nullability annotation was distributed, false
> +/// otherwise.
> +static bool distributeNullabilityTypeAttr(TypeProcessingState &state,
> + QualType type,
> + AttributeList &attr) {
> + Declarator &declarator = state.getDeclarator();
> +
> + /// Attempt to move the attribute to the specified chunk.
> + auto moveToChunk = [&](DeclaratorChunk &chunk, bool inFunction) -> bool {
> + // If there is already a nullability attribute there, don't add
> + // one.
> + if (hasNullabilityAttr(chunk.getAttrListRef()))
> + return false;
> +
> + // Complain about the nullability qualifier being in the wrong
> + // place.
> + unsigned pointerKind
> + = chunk.Kind == DeclaratorChunk::Pointer ? (inFunction ? 3 : 0)
> + : chunk.Kind == DeclaratorChunk::BlockPointer ? 1
> + : inFunction? 4 : 2;
Can we replace the magic numbers with something else?
> +
> + auto diag = state.getSema().Diag(attr.getLoc(),
> + diag::warn_nullability_declspec)
> + << static_cast<unsigned>(mapNullabilityAttrKind(attr.getKind()))
> + << type
> + << pointerKind;
> +
> + // FIXME: MemberPointer chunks don't carry the location of the *.
> + if (chunk.Kind != DeclaratorChunk::MemberPointer) {
> + diag << FixItHint::CreateRemoval(attr.getLoc())
> + << FixItHint::CreateInsertion(
> + state.getSema().getPreprocessor()
> + .getLocForEndOfToken(chunk.Loc),
> + " " + attr.getName()->getName().str() + " ");
> + }
> +
> + moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
> + chunk.getAttrListRef());
> + return true;
> + };
> +
> + // Move it to the outermost pointer, member pointer, or block
> + // pointer declarator.
> + for (unsigned i = state.getCurrentChunkIndex(); i != 0; --i) {
> + DeclaratorChunk &chunk = declarator.getTypeObject(i-1);
> + switch (chunk.Kind) {
> + case DeclaratorChunk::Pointer:
> + case DeclaratorChunk::BlockPointer:
> + case DeclaratorChunk::MemberPointer:
> + return moveToChunk(chunk, false);
> +
> + case DeclaratorChunk::Paren:
> + case DeclaratorChunk::Array:
> + continue;
> +
> + case DeclaratorChunk::Function:
> + // Try to move past the return type to a function/block/member
> + // function pointer.
> + if (DeclaratorChunk *dest = maybeMovePastReturnType(
> + declarator, i,
> + /*onlyBlockPointers=*/false)) {
> + return moveToChunk(*dest, true);
> + }
> +
> + return false;
> +
> + // Don't walk through these.
> + case DeclaratorChunk::Reference:
> + return false;
> + }
> + }
> +
> + return false;
> +}
> +
> static AttributedType::Kind getCCTypeAttrKind(AttributeList &Attr) {
> assert(!Attr.isInvalid());
> switch (Attr.getKind()) {
> @@ -4997,6 +5227,20 @@ static void processTypeAttrs(TypeProcess
> attr.setUsedAsTypeAttr();
> break;
>
> + NULLABILITY_TYPE_ATTRS_CASELIST:
> + // Either add nullability here or try to distribute it. We
> + // don't want to distribute the nullability specifier past any
> + // dependent type, because that complicates the user model.
> + if (type->canHaveNullability() || type->isDependentType() ||
> + !distributeNullabilityTypeAttr(state, type, attr)) {
> + if (handleNullabilityTypeAttr(state, attr, type)) {
> + attr.setInvalid();
> + }
> +
> + attr.setUsedAsTypeAttr();
> + }
> + break;
> +
> case AttributeList::AT_NSReturnsRetained:
> if (!state.getSema().getLangOpts().ObjCAutoRefCount)
> break;
>
> Modified: cfe/trunk/lib/Sema/TreeTransform.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=240146&r1=240145&r2=240146&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/TreeTransform.h (original)
> +++ cfe/trunk/lib/Sema/TreeTransform.h Fri Jun 19 12:51:05 2015
> @@ -5386,6 +5386,17 @@ QualType TreeTransform<Derived>::Transfo
> = getDerived().TransformType(oldType->getEquivalentType());
> if (equivalentType.isNull())
> return QualType();
> +
> + // Check whether we can add nullability; it is only represented as
> + // type sugar, and therefore cannot be diagnosed in any other way.
> + if (auto nullability = oldType->getImmediateNullability()) {
> + if (!modifiedType->canHaveNullability()) {
> + SemaRef.Diag(TL.getAttrNameLoc(), diag::err_nullability_nonpointer)
> + << static_cast<unsigned>(*nullability) << modifiedType;
> + return QualType();
> + }
> + }
> +
> result = SemaRef.Context.getAttributedType(oldType->getAttrKind(),
> modifiedType,
> equivalentType);
>
> Added: cfe/trunk/test/FixIt/fixit-nullability-declspec.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/fixit-nullability-declspec.cpp?rev=240146&view=auto
> ==============================================================================
> --- cfe/trunk/test/FixIt/fixit-nullability-declspec.cpp (added)
> +++ cfe/trunk/test/FixIt/fixit-nullability-declspec.cpp Fri Jun 19 12:51:05 2015
> @@ -0,0 +1,9 @@
> +// RUN: %clang_cc1 -fblocks -Werror=nullability-declspec -x c++ -verify %s
> +
> +// RUN: cp %s %t
> +// RUN: not %clang_cc1 -fixit -fblocks -Werror=nullability-declspec -x c++ %t
> +// RUN: %clang_cc1 -fblocks -Werror=nullability-declspec -x c++ %t
> +
> +__nullable int *ip1; // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the pointer?}}
> +__nullable int (*fp1)(int); // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the function pointer?}}
> +__nonnull int (^bp1)(int); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the block pointer?}}
>
> Propchange: cfe/trunk/test/FixIt/fixit-nullability-declspec.cpp
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: cfe/trunk/test/FixIt/fixit-nullability-declspec.cpp
> ------------------------------------------------------------------------------
> svn:keywords = Id
>
> Propchange: cfe/trunk/test/FixIt/fixit-nullability-declspec.cpp
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: cfe/trunk/test/Parser/nullability.c
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/nullability.c?rev=240146&view=auto
> ==============================================================================
> --- cfe/trunk/test/Parser/nullability.c (added)
> +++ cfe/trunk/test/Parser/nullability.c Fri Jun 19 12:51:05 2015
> @@ -0,0 +1,16 @@
> +// RUN: %clang_cc1 -fsyntax-only -std=c99 -Wno-nullability-declspec -pedantic %s -verify
> +
> +__nonnull int *ptr; // expected-warning{{type nullability specifier '__nonnull' is a Clang extension}}
> +
> +#pragma clang diagnostic push
> +#pragma clang diagnostic ignored "-Wnullability-extension"
> +__nonnull int *ptr2; // no-warning
> +#pragma clang diagnostic pop
> +
> +#if __has_feature(nullability)
> +# error Nullability should not be supported in C under -pedantic -std=c99
> +#endif
> +
> +#if !__has_extension(nullability)
> +# error Nullability should always be supported as an extension
> +#endif
>
> Propchange: cfe/trunk/test/Parser/nullability.c
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: cfe/trunk/test/Parser/nullability.c
> ------------------------------------------------------------------------------
> svn:keywords = Id
>
> Propchange: cfe/trunk/test/Parser/nullability.c
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: cfe/trunk/test/Sema/non-null-warning.c
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/non-null-warning.c?rev=240146&view=auto
> ==============================================================================
> --- cfe/trunk/test/Sema/non-null-warning.c (added)
> +++ cfe/trunk/test/Sema/non-null-warning.c Fri Jun 19 12:51:05 2015
> @@ -0,0 +1,35 @@
> +// RUN: %clang_cc1 -fsyntax-only -Wnonnull -Wnullability %s -verify
> +// rdar://19160762
> +
> +#if __has_feature(nullability)
> +#else
> +# error nullability feature should be defined
> +#endif
> +
> +
> +int * __nullable foo(int * __nonnull x);
> +
> +int *__nonnull ret_nonnull();
> +
> +int *foo(int *x) {
> + return 0;
> +}
> +
> +int * __nullable foo1(int * __nonnull x); // expected-note {{previous declaration is here}}
> +
> +int *foo1(int * __nullable x) { // expected-warning {{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
> + return 0;
> +}
> +
> +int * __nullable foo2(int * __nonnull x);
> +
> +int *foo2(int * __nonnull x) {
> + return 0;
> +}
> +
> +int * __nullable foo3(int * __nullable x); // expected-note {{previous declaration is here}}
> +
> +int *foo3(int * __nonnull x) { // expected-warning {{nullability specifier '__nonnull' conflicts with existing specifier '__nullable'}}
> + return 0;
> +}
> +
>
> Propchange: cfe/trunk/test/Sema/non-null-warning.c
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: cfe/trunk/test/Sema/non-null-warning.c
> ------------------------------------------------------------------------------
> svn:keywords = Id
>
> Propchange: cfe/trunk/test/Sema/non-null-warning.c
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: cfe/trunk/test/Sema/nullability.c
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/nullability.c?rev=240146&view=auto
> ==============================================================================
> --- cfe/trunk/test/Sema/nullability.c (added)
> +++ cfe/trunk/test/Sema/nullability.c Fri Jun 19 12:51:05 2015
> @@ -0,0 +1,87 @@
> +// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-nullability-declspec %s -verify
> +
> +#if __has_feature(nullability)
> +#else
> +# error nullability feature should be defined
> +#endif
> +
> +typedef int * int_ptr;
> +
> +// Parse nullability type specifiers.
> +typedef int * __nonnull nonnull_int_ptr; // expected-note{{'__nonnull' specified here}}
> +typedef int * __nullable nullable_int_ptr;
> +typedef int * __null_unspecified null_unspecified_int_ptr;
> +
> +// Redundant nullability type specifiers.
> +typedef int * __nonnull __nonnull redundant_1; // expected-warning{{duplicate nullability specifier '__nonnull'}}
> +
> +// Conflicting nullability type specifiers.
> +typedef int * __nonnull __nullable conflicting_1; // expected-error{{nullability specifier '__nonnull' conflicts with existing specifier '__nullable'}}
> +typedef int * __null_unspecified __nonnull conflicting_2; // expected-error{{nullability specifier '__null_unspecified' conflicts with existing specifier '__nonnull'}}
> +
> +// Redundant nullability specifiers via a typedef are okay.
> +typedef nonnull_int_ptr __nonnull redundant_okay_1;
> +
> +// Conflicting nullability specifiers via a typedef are not.
> +typedef nonnull_int_ptr __nullable conflicting_2; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
> +typedef nonnull_int_ptr nonnull_int_ptr_typedef;
> +typedef nonnull_int_ptr_typedef __nullable conflicting_2; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
> +typedef nonnull_int_ptr_typedef nonnull_int_ptr_typedef_typedef;
> +typedef nonnull_int_ptr_typedef_typedef __null_unspecified conflicting_3; // expected-error{{nullability specifier '__null_unspecified' conflicts with existing specifier '__nonnull'}}
> +
> +// Nullability applies to all pointer types.
> +typedef int (* __nonnull function_pointer_type_1)(int, int);
> +typedef int (^ __nonnull block_type_1)(int, int);
> +
> +// Nullability must be on a pointer type.
> +typedef int __nonnull int_type_1; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'}}
> +
> +// Nullability can move out to a pointer/block pointer declarator
> +// (with a suppressed warning).
> +typedef __nonnull int * nonnull_int_ptr_2;
> +typedef int __nullable * nullable_int_ptr_2;
> +typedef __nonnull int (* function_pointer_type_2)(int, int);
> +typedef __nonnull int (^ block_type_2)(int, int);
> +typedef __nonnull int * * __nullable nonnull_int_ptr_ptr_1;
> +typedef __nonnull int *(^ block_type_3)(int, int);
> +typedef __nonnull int *(* function_pointer_type_3)(int, int);
> +typedef __nonnull int_ptr (^ block_type_4)(int, int);
> +typedef __nonnull int_ptr (* function_pointer_type_4)(int, int);
> +
> +void acceptFunctionPtr(__nonnull int *(*)(void));
> +void acceptBlockPtr(__nonnull int *(^)(void));
> +
> +void testBlockFunctionPtrNullability() {
> + float *fp;
> + fp = (function_pointer_type_3)0; // expected-warning{{from 'function_pointer_type_3' (aka 'int * __nonnull (*)(int, int)')}}
> + fp = (block_type_3)0; // expected-error{{from incompatible type 'block_type_3' (aka 'int * __nonnull (^)(int, int)')}}
> + fp = (function_pointer_type_4)0; // expected-warning{{from 'function_pointer_type_4' (aka 'int_ptr __nonnull (*)(int, int)')}}
> + fp = (block_type_4)0; // expected-error{{from incompatible type 'block_type_4' (aka 'int_ptr __nonnull (^)(int, int)')}}
> +
> + acceptFunctionPtr(0); // no-warning
> + acceptBlockPtr(0); // no-warning
> +}
> +
> +// Moving nullability where it creates a conflict.
> +typedef __nonnull int * __nullable * conflict_int_ptr_ptr_2; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'}}
> +
> +// Nullability is not part of the canonical type.
> +typedef int * __nonnull ambiguous_int_ptr;
> +typedef int * ambiguous_int_ptr;
> +typedef int * __nullable ambiguous_int_ptr;
> +
> +// Printing of nullability.
> +float f;
> +int * __nonnull ip_1 = &f; // expected-warning{{incompatible pointer types initializing 'int * __nonnull' with an expression of type 'float *'}}
> +
> +// Check printing of nullability specifiers.
> +void printing_nullability(void) {
> + int * __nonnull iptr;
> + float *fptr = iptr; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * __nonnull'}}
> +
> + int * * __nonnull iptrptr;
> + float **fptrptr = iptrptr; // expected-warning{{incompatible pointer types initializing 'float **' with an expression of type 'int ** __nonnull'}}
> +
> + int * __nullable * __nonnull iptrptr2;
> + float * *fptrptr2 = iptrptr2; // expected-warning{{incompatible pointer types initializing 'float **' with an expression of type 'int * __nullable * __nonnull'}}
> +}
>
> Propchange: cfe/trunk/test/Sema/nullability.c
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: cfe/trunk/test/Sema/nullability.c
> ------------------------------------------------------------------------------
> svn:keywords = Id
>
> Propchange: cfe/trunk/test/Sema/nullability.c
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: cfe/trunk/test/SemaCXX/nullability-declspec.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nullability-declspec.cpp?rev=240146&view=auto
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/nullability-declspec.cpp (added)
> +++ cfe/trunk/test/SemaCXX/nullability-declspec.cpp Fri Jun 19 12:51:05 2015
> @@ -0,0 +1,9 @@
> +// RUN: %clang_cc1 -fblocks -Werror=nullability-declspec -verify %s
> +
> +struct X { };
> +
> +__nullable int *ip1; // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the pointer?}}
> +__nullable int (*fp1)(int); // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the function pointer?}}
> +__nonnull int (^bp1)(int); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the block pointer?}}
> +__nonnull int X::*pmd1; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the member pointer?}}
> +__nonnull int (X::*pmf1)(int); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the member function pointer?}}
>
> Propchange: cfe/trunk/test/SemaCXX/nullability-declspec.cpp
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: cfe/trunk/test/SemaCXX/nullability-declspec.cpp
> ------------------------------------------------------------------------------
> svn:keywords = Id
>
> Propchange: cfe/trunk/test/SemaCXX/nullability-declspec.cpp
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: cfe/trunk/test/SemaCXX/nullability.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nullability.cpp?rev=240146&view=auto
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/nullability.cpp (added)
> +++ cfe/trunk/test/SemaCXX/nullability.cpp Fri Jun 19 12:51:05 2015
> @@ -0,0 +1,42 @@
> +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wno-nullability-declspec %s -verify
> +
> +typedef decltype(nullptr) nullptr_t;
> +
> +class X {
> +};
> +
> +// Nullability applies to all pointer types.
> +typedef int (X::* __nonnull member_function_type_1)(int);
> +typedef int X::* __nonnull member_data_type_1;
> +typedef nullptr_t __nonnull nonnull_nullptr_t; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'nullptr_t'}}
> +
> +// Nullability can move into member pointers (this is suppressing a warning).
> +typedef __nonnull int (X::* member_function_type_2)(int);
> +typedef int (X::* __nonnull member_function_type_3)(int);
> +typedef __nonnull int X::* member_data_type_2;
> +
> +// Adding non-null via a template.
> +template<typename T>
> +struct AddNonNull {
> + typedef __nonnull T type; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'}}
> + // expected-error at -1{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'nullptr_t'}}
> +};
> +
> +typedef AddNonNull<int *>::type nonnull_int_ptr_1;
> +typedef AddNonNull<int * __nullable>::type nonnull_int_ptr_2; // FIXME: check that it was overridden
> +typedef AddNonNull<nullptr_t>::type nonnull_int_ptr_3; // expected-note{{in instantiation of template class}}
> +
> +typedef AddNonNull<int>::type nonnull_non_pointer_1; // expected-note{{in instantiation of template class 'AddNonNull<int>' requested here}}
> +
> +// Non-null checking within a template.
> +template<typename T>
> +struct AddNonNull2 {
> + typedef __nonnull AddNonNull<T> invalid1; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull<T>'}}
> + typedef __nonnull AddNonNull2 invalid2; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull2<T>'}}
> + typedef __nonnull AddNonNull2<T> invalid3; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull2<T>'}}
> + typedef __nonnull typename AddNonNull<T>::type okay1;
> +
> + // Don't move past a dependent type even if we know that nullability
> + // cannot apply to that specific dependent type.
> + typedef __nonnull AddNonNull<T> (*invalid4); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull<T>'}}
> +};
>
> Propchange: cfe/trunk/test/SemaCXX/nullability.cpp
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: cfe/trunk/test/SemaCXX/nullability.cpp
> ------------------------------------------------------------------------------
> svn:keywords = Id
>
> Propchange: cfe/trunk/test/SemaCXX/nullability.cpp
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: cfe/trunk/test/SemaObjC/nullability.m
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/nullability.m?rev=240146&view=auto
> ==============================================================================
> --- cfe/trunk/test/SemaObjC/nullability.m (added)
> +++ cfe/trunk/test/SemaObjC/nullability.m Fri Jun 19 12:51:05 2015
> @@ -0,0 +1,19 @@
> +// RUN: %clang_cc1 -fsyntax-only -fblocks -Woverriding-method-mismatch -Wno-nullability-declspec %s -verify
> +
> + at interface NSFoo
> + at end
> +
> +// Nullability applies to all pointer types.
> +typedef NSFoo * __nonnull nonnull_NSFoo_ptr;
> +typedef id __nonnull nonnull_id;
> +typedef SEL __nonnull nonnull_SEL;
> +
> +// Nullability can move into Objective-C pointer types.
> +typedef __nonnull NSFoo * nonnull_NSFoo_ptr_2;
> +
> +// Conflicts from nullability moving into Objective-C pointer type.
> +typedef __nonnull NSFoo * __nullable conflict_NSFoo_ptr_2; // expected-error{{'__nonnull' cannot be applied to non-pointer type 'NSFoo'}}
> +
> +void testBlocksPrinting(NSFoo * __nullable (^bp)(int)) {
> + int *ip = bp; // expected-error{{'NSFoo * __nullable (^)(int)'}}
> +}
>
>
> _______________________________________________
> 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